blob: 04c648126ece01f95718dd8f46f5841b56d9afb6 [file] [log] [blame]
#
# Copyright (c) 2011,2012,2013 Big Switch Networks, Inc.
#
# Licensed under the Eclipse Public License, Version 1.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
#
#
# DATA HANDLERS
#
import re
import modi
import utif
import error
import command
import time
import datetime
import rest_to_model
import traceback
from midw import *
COMMAND_CIDR_RE = re.compile(r'^((\d{1,3}\.){3}\d{1,3})/(\d{1,2}?)$')
def split_cidr_data_handler(value, data,
dest_ip='ip', dest_netmask='netmask', neg = False):
"""
Split a cidr address (e.g. 192.168.1.1/24) into separate IP address
and netmask value. The names of the ip and netmask fields are
specified (typically directly in the same block/dictionary where
the argument data handler is specifed) with a 'dest-ip' and a
'dest-netmask' values.
"""
global sdnsh
m = COMMAND_CIDR_RE.match(value)
if m:
bits = int(m.group(3))
if bits > 32:
raise error.ArgumentValidationError("max cidr block is 32")
data[dest_ip] = m.group(1)
if neg:
data[dest_netmask] = utif.inet_ntoa(~(0xffffffff << (32 - bits)))
else:
data[dest_netmask] = utif.inet_ntoa((0xffffffff << (32 - bits)))
def alias_to_value_handler(value, obj_type, data, field, other = None):
"""
Compute the alias value for the named field for the obj_type.
Place the resulting converted field into the data dictionary.
Since this is a data-handler, the data dict must be updated
even if this isn't an alias, otherwise the field value is lost.
"""
global sdnsh
if sdnsh.description:
print 'alias_to_value_handler: ', value, obj_type, data, field
if field != mi.pk(obj_type):
# if this is a forgeign key, use the obj_type of the fk.
if mi.is_foreign_key(obj_type, field):
(obj_type, fk_name) = mi.foreign_key_references(obj_type, field)
else:
# XXX possibly other choices to determine alias_obj_type?
if sdnsh.description:
print 'alias_to_value_handler: field %s no obj-type ref %s ' % \
(field, obj_type)
if other:
parts = other.split('|')
key = mi.pk(parts[0]) # parts[0] <- first part of other
if len(parts) > 0:
other = parts[0]
key = field
# not clear whether the part[1] is useful, two parts used
# in other functions, example complete-from-another()
other = mi.obj_type_related_config_obj_type(other)
converted_value = convert_alias_to_object_key(other, value)
pk = mi.pk(other)
if mi.is_compound_key(other, pk) and converted_value != value:
pk_dict = { pk : converted_value }
mi.split_compound_into_dict(other, pk, pk_dict, is_prefix = True)
for (k,v) in pk_dict.items():
if k != pk:
data[k] = v
if sdnsh.description:
print "alias_to_value_handler: compound (other) %s:%s <- %s:%s" % \
(k, data[k], other, converted_value)
else:
case = mi.get_obj_type_field_case_sensitive(other, field)
data[field] = utif.convert_case(case, converted_value)
if sdnsh.description:
print "alias_to_value_handler: (other) %s:%s <- %s:%s" % \
(key, data[key], other, value)
else:
# Some obj_types, for example, host, have no cassandra data,
# but do have a related obj_type which is in the store
obj_type = mi.obj_type_related_config_obj_type(obj_type)
converted_value = convert_alias_to_object_key(obj_type, value)
pk = mi.pk(obj_type)
if mi.is_compound_key(obj_type, pk) and converted_value != value:
pk_dict = { pk : converted_value }
split_obj_type = other if other != None else obj_type
mi.split_compound_into_dict(split_obj_type, pk, pk_dict, is_prefix = True)
for (k,v) in pk_dict.items():
if k != pk:
data[k] = v
if sdnsh.description:
print "alias_to_value_handler: compound %s:%s <- %s:%s" % \
(k, data[k], obj_type, converted_value)
else:
case = mi.get_obj_type_field_case_sensitive(obj_type, field)
data[field] = utif.convert_case(case, converted_value)
if sdnsh.description:
print "alias_to_value_handler: %s:%s <- %s:%s" % (field,
data[field], obj_type, value)
def replace_value_handler(value, obj_type, data, field, other = None):
"""
Use the other field when its present to find a related obj_type,
look for the field in that structure to populate data.
"""
global sdnsh
table = obj_type
if sdnsh.description:
print "replace_value_handler: obj_type: %s value: %s data: %s field %s other %s" % \
(obj_type, value, data, field, other)
fields = [field]
if other:
parts = other.split('|')
table = parts[0]
fields = parts[1:]
try:
row = sdnsh.get_object_from_store(table, value)
except Exception, e:
raise error.ArgumentValidationError("Unknown value %s (%s)" %
(value, obj_type))
for field in fields:
if field not in row:
raise error.ArgumentValidationError("Unknown field %s (%s)" %
(field, obj_type))
if sdnsh.description:
print 'replace_value_handler: set %s <- %s from obj-type %s' %\
(field, row[field], table)
data[field] = row[field]
def enable_disable_to_boolean_handler(value, data, field):
if value == 'enable':
data[field] = True
if value == 'disable':
data[field] = False
def date_to_integer_handler(value, data, field):
if (value == 'now' or value == 'current'):
data[field] = int(time.time()*1000)
try:
data[field] = int(value)
except:
pass
for f,pre in [('%Y-%m-%dT%H:%M:%S', None),
('%Y-%m-%d %H:%M:%S', None),
('%Y-%m-%dT%H:%M:%S%z', None),
('%Y-%m-%d %H:%M:%S%z', None),
('%Y-%m-%d', None),
('%m-%d', '%Y-'),
('%H:%M', '%Y-%m-%dT')]:
try:
t = value
if pre:
pref = datetime.datetime.now().strftime(pre)
f = pre + f
t = pref + t
thetime = datetime.datetime.strptime(t, f)
data[field] = int(time.mktime(thetime.timetuple())*1000)
except:
pass
HEX_RE = re.compile(r'^0x[0-9a-fA-F]+$')
def hex_to_integer_handler(value, data, field):
if HEX_RE.match(str(value)):
_value = str(int(value, 16))
else:
_value = str(int(value))
data[field] = _value
def _invert_netmask(value):
split_bytes = value.split('.')
return "%s.%s.%s.%s" % (255-int(split_bytes[0]),
255-int(split_bytes[1]),
255-int(split_bytes[2]),
255-int(split_bytes[3]))
def convert_inverse_netmask_handler(value, data, field):
data[field] = _invert_netmask(value)
def interface_ranges(names):
"""
Given a list of interfaces (strings), in any order, with a numeric suffix,
collect together the prefix components, and create ranges with
adjacent numeric interfaces, so that a large collection of names
becomes easier to read. At the worst, the list will be as
complicated as the original (which would typically be very unlikely)
Example: names <- ['Eth0', 'Eth1', 'Eth2', 'Eth4', 'Eth5', 'Eth8']
result <- ['Eth0-2', 'Eth4-5', 'Eth8']
names <- ['1','2','3']
result <- ['1-3']
"""
# collect the interfaces into dictionaries based on prefixes
# ahead of groups of digits.
groups = {}
def is_digit(c):
c_ord = ord(c)
if c_ord >= ord('0') and c_ord <= ord('9'):
return True
return False
for name in names:
if is_digit(name[-1]):
for index in range(-2, -len(name)-1, -1):
if not is_digit(name[index]):
index += 1
break;
else:
index = -len(name)
prefix = name[:index]
number = int(name[index:])
if not prefix in groups:
groups[prefix] = []
groups[prefix].append(number)
else:
groups[name] = []
for prefix in groups:
groups[prefix] = sorted(utif.unique_list_from_list(groups[prefix]))
ranges = []
for (prefix, value) in groups.items():
if len(value) == 0:
ranges.append(prefix)
else:
low = value[0]
prev = low
for next in value[1:] + [value[-1] + 2]: # +[] flushes last item
if next > prev + 1:
if prev == low:
ranges.append('%s%s' % (prefix, low))
else:
ranges.append('%s%s-%s' % (prefix, low, prev))
low = next
prev = next
return ranges
#print interface_ranges(['1','2','3', 'oe'])
#print interface_ranges(['Eth1','Eth2','Eth3', 'Eth4', 'o5', 'o6'])
def check_missing_interface(switch, interface):
#
# The switch value could be a compound key reference to a
# switch, if there's a '|' in the switch valud, try to guess
# which entry is the switch
parts = switch.split('|')
if len(parts) > 1:
for part in parts:
if utif.COMMAND_DPID_RE.match(part):
switch = part
break
else:
switch = part[0]
try:
row = rest_to_model.get_model_from_url('switches', {'dpid' : switch })
except:
if sdnsh.description or sdnsh.debug_backtrace:
traceback.print_exc()
row = []
if len(row) == 0 or row[0].get('ip-address', '') == '':
sdnsh.warning('switch %s currently not active, '
'interface %s may not exist' %
(switch, interface))
return
try:
ports = rest_to_model.get_model_from_url('interfaces', {'dpid' : switch })
except:
# can't validate current list of interface names
return
if_names = [x['portName'].lower() for x in ports]
if not interface.lower() in if_names:
# pre-servce case, try to identify unique ranges
ranges = interface_ranges([x['portName'] for x in ports])
sdnsh.warning( 'active switch has no interface "%s", '
'known: %s' % (interface, ', '.join(ranges)) +
'\nUse \"exit; no interface %s\" to remove' % interface)
def warn_missing_interface(value, data, field, is_no, obj_type, obj_value ):
if not is_no:
# need switch, if_name
pk_data = { mi.pk(obj_type) : obj_value }
mi.split_compound_into_dict(obj_type, mi.pk(obj_type), pk_data, True)
switch = pk_data.get('switch')
if switch == None:
switch = pk_data.get('dpid')
if switch == None:
switch = data.get('switch')
if switch == None:
switch = data.get('dpid')
if switch == None:
raise error.ArgumentValidationError("Can't identify switch for validation")
force = True if data.get('force', '') != '' else False
check_missing_interface(switch, value)
data[field] = value
def convert_interface_to_port(value, data, field, other = None, scoped = None):
# look for the switch name in data
if scoped:
dpid = data.get(scoped)
elif 'dpid' in data:
dpid = data['dpid']
else:
dpid = data.get('switch', '') # possibly other choices
# if its not a specific switch, no conversion is possible
# should the value be passed through?
if dpid == '':
data[field] = value
return
ports = rest_to_model.get_model_from_url('interfaces', {'dpid' : dpid})
for port in ports:
if port['portName'] == value:
data[field] = port['portNumber'] # should this be a string?
break
else:
raise error.ArgumentValidationError("Can't find port %s on switch %s" %
(value, dpid))
def convert_tag_to_parts(value, data, namespace_key, name_key, value_key):
"""
Split a tag of the form [ns].name=value into the three
component parts
"""
if sdnsh.description:
print "convert_tag_to_parts: %s %s %s %s %s" % (
value, data, namespace_key, name_key, value_key)
tag_and_value = value.split('=')
if len(tag_and_value) != 2:
raise error.ArgumentValidationError("tag <[tag-namespace.]name>=<value>")
tag_parts = tag_and_value[0].split('.')
if len(tag_parts) == 1:
tag_namespace = "default"
tag_name = tag_parts[0]
elif len(tag_parts) >= 2:
tag_namespace = '.'.join(tag_parts[:-1])
tag_name = tag_parts[-1]
# should the names have some specific validation?
data[namespace_key] = tag_namespace
data[name_key] = tag_name
data[value_key] = tag_and_value[1]
def init_data_handlers(bs, modi):
global sdnsh, mi
sdnsh = bs
mi = modi
command.add_argument_data_handler('split-cidr-data', split_cidr_data_handler,
{'kwargs': {'value': '$value',
'data': '$data',
'dest_ip': '$dest-ip',
'dest_netmask': '$dest-netmask'}})
command.add_argument_data_handler('split-cidr-data-inverse', split_cidr_data_handler,
{'kwargs': {'value': '$value',
'data': '$data',
'dest_ip': '$dest-ip',
'dest_netmask': '$dest-netmask',
'neg' : True}})
command.add_argument_data_handler('alias-to-value', alias_to_value_handler,
{'kwargs': {'value': '$value',
'data': '$data',
'field': '$field',
'other': '$other',
'obj_type' : '$obj-type'}})
command.add_argument_data_handler('replace-value', replace_value_handler,
{'kwargs': {'value': '$value',
'data': '$data',
'field': '$field',
'other': '$other',
'obj_type' : '$obj-type'}})
command.add_argument_data_handler('enable-disable-to-boolean', enable_disable_to_boolean_handler,
{'kwargs': {'value': '$value',
'data': '$data',
'field': '$field'}})
command.add_argument_data_handler('date-to-integer', date_to_integer_handler,
{'kwargs': {'value' : '$value',
'data' : '$data',
'field' : '$field'}})
command.add_argument_data_handler('hex-to-integer', hex_to_integer_handler,
{'kwargs': {'value' : '$value',
'data' : '$data',
'field' : '$field'}})
command.add_argument_data_handler('convert-inverse-netmask', convert_inverse_netmask_handler,
{'kwargs': {'value' : '$value',
'data' : '$data',
'field' : '$field'}})
command.add_argument_data_handler('warn-missing-interface', warn_missing_interface,
{'kwargs': {'value' : '$value',
'data' : '$data',
'field' : '$field',
'is_no' : '$is-no-command',
'obj_type' : '$current-mode-obj-type',
'obj_value' : '$current-mode-obj-id'}})
command.add_argument_data_handler('convert-interface-to-port', convert_interface_to_port,
{'kwargs': {'value' : '$value',
'data' : '$data',
'field' : '$field',
'other' : '$other',
'scoped' : '$scoped'}})
command.add_argument_data_handler('convert-tag-to-parts', convert_tag_to_parts,
{'kwargs': {'value' : '$value',
'data' : '$data',
'namespace_key' : '$namespace-key',
'name_key' : '$name-key',
'value_key' : '$value-key'}})