| # |
| # Copyright (c) 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. |
| # |
| |
| # sdnsh - The Controller Shell |
| |
| # |
| # show running-config |
| # and associated |
| # |
| |
| import datetime |
| import re |
| import utif |
| import modi |
| |
| from midw import * |
| from vnsw import * |
| |
| |
| def init_running_config(bs, modi): |
| global sdnsh, mi |
| sdnsh = bs |
| mi = modi |
| |
| |
| running_config_registry = {} |
| |
| running_config_command_choices = { |
| 'optional' : True, |
| 'choices' : ( |
| ) |
| } |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def register_running_config(name, order, feature, running_config_proc, command_tuple = None): |
| """ |
| Register a callback to manage the display of component running configs |
| |
| @feature a predicate to call, returns True/False to enable/disable this entry |
| """ |
| running_config_registry[name] = { 'order' : order, |
| 'feature' : feature, |
| 'proc' : running_config_proc } |
| if command_tuple: |
| global running_config_command_choices |
| running_config_command_choices['choices'] += command_tuple |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def registry_items_enabled(): |
| """ |
| Return a list of active running config entries, this is a subset of |
| the registered items, only items which are currently enabled via features |
| """ |
| return [name for name in running_config_registry.keys() |
| if running_config_registry[name]['feature'] == None or |
| running_config_registry[name]['feature'](sdnsh) == True] |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def perform_running_config(name, context, config, words): |
| """ |
| Callout to append to config |
| """ |
| if name in running_config_registry: |
| running_config_registry[name]['proc'](context, config, words) |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_include_alias(config, indent, obj_type, key): |
| """ |
| Given an obj_type and an id, add the alias for this obj_type |
| to the config. |
| |
| The table lookup is a bit difficult to understand; keep in |
| mind, for example, the 'host-alias' table has a primary |
| key called id, and a foreign key called 'host'. |
| |
| Return False if no alias exists, and True when one was added to 'config' |
| """ |
| if obj_type in mi.alias_obj_type_xref: |
| for alias in mi.alias_obj_type_xref[obj_type]: |
| field = mi.alias_obj_type_field(alias) |
| try: |
| row = sdnsh.get_table_from_store(alias, field, key) |
| except: |
| row = [] |
| |
| if len(row) > 1: |
| sdnsh.warning('%s %s: alias count > 1' % (alias, key)) |
| elif len(row) == 1: |
| config.append("%s%s %s\n" % |
| (' ' * indent, alias, row[0]['id'])) |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def not_default_value(obj_type, field, value): |
| """ |
| Return True when the value passed in is not the default value. |
| """ |
| default_value = mi.field_default_value(obj_type, field) |
| if (mi.is_null_allowed(obj_type, field) and value != None) or \ |
| (not mi.is_null_allowed(obj_type, field) and default_value != None |
| and (default_value != value)): |
| return True |
| return False |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_include_field(config, obj_type, field, value, |
| indent, prefix = ""): |
| """ |
| Identify fields of obj_types who's values differ from the default |
| values, since these need to be included into the running-config |
| """ |
| if mi.not_default_value(obj_type, field, value): |
| # |
| if mi.is_field_string(obj_type, field): |
| config.append(' ' * (indent + indent) + prefix + field + |
| " %s\n" % utif.quote_string(value)) |
| else: |
| config.append(' ' * (indent + indent) + prefix + field + |
| ' %s\n' % value) |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_vns_acl(config, vns_name, acl, vns_acl_entries,indent=0): |
| """ |
| factored out due to excessive indent level in do_show_running_config |
| and to allow re-use in 'show vns <n> running-config' |
| """ |
| key = mi.pk('vns-access-list') |
| |
| config.append(' ' *2*indent + 'access-list %s\n' % acl['name']) |
| |
| # |
| vns_acl_fields = ['priority', 'description'] |
| for field in vns_acl_fields: |
| running_config_include_field(config, |
| 'vns-access-list', field, |
| acl.get(field, ''), indent+1) |
| |
| vns_acl_entries = sorted(vns_acl_entries, |
| cmp=lambda x,y: int(x['rule']) - int(y['rule'])) |
| |
| for acl_entry in vns_acl_entries: |
| config.append(' ' *2*(indent+1) + '%s %s %s %s\n' % ( |
| acl_entry['rule'], |
| acl_entry['action'], |
| acl_entry['type'], |
| vns_acl_entry_to_text(acl_entry))) |
| |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_vns_if_rule(config, |
| vns_if_rule_entries,indent=0): |
| """ |
| factored out due to excessive indent level in do_show_running_config |
| and to allow re-use in 'show vns <n> running-config' |
| |
| 'vns_if_rule_entries' are all the entries to concatenate to the confi |
| """ |
| for rule in vns_if_rule_entries: |
| config.append(' ' *2*indent +'interface-rule %s\n' % rule['rule']) |
| |
| # |
| # mi.obj_type_config_fields('vns-interface-rule') isn't used |
| # here since switch/port is collected together (managed together) |
| for field in ['description', 'active', 'priority', ]: |
| running_config_include_field(config, |
| 'vns-interface-rule', field, |
| rule.get(field, ''), indent+1,) |
| |
| for field in [ 'mac', 'ip-subnet', 'vlans', 'tags']: |
| running_config_include_field(config, |
| 'vns-interface-rule', field, |
| rule.get(field, ''), indent+1, 'match ') |
| if rule.get('allow-multiple', False): |
| config.append(' ' * 2*(indent+1) + 'allow-multiple\n'); |
| if rule.get('vlan-tag-on-egress', False): |
| config.append(' ' * 2*(indent+1) + 'vlan-tag-on-egress\n'); |
| |
| # |
| # Manage switch and ports differently, placing both on the |
| # same line when ports exist, and replacing the switch alias |
| # when its available. |
| if 'switch' in rule: |
| dpid_or_alias = alias_lookup_with_foreign_key('switch-alias', |
| rule['switch']) |
| if dpid_or_alias == None: |
| dpid_or_alias = rule['switch'] # dpid |
| if 'ports' in rule: |
| config.append(' ' *2*(indent+1) + 'match switch %s %s\n' % |
| (dpid_or_alias, rule['ports'])) |
| else: |
| config.append(' ' *2*(indent+1) + 'match switch %s\n' % dpid_or_alias) |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_active_vns_interfaces(vns_name, vns_interface_acl): |
| """ |
| Return a list of interfaces which have configuration information needing |
| to be saved |
| """ |
| active = {} |
| key = mi.pk('vns-interface-access-list') |
| |
| for if_acl in vns_interface_acl: |
| id_fields = if_acl[key].split('|') |
| vns_id= id_fields[0] +'|' + id_fields[1] |
| if vns_id == vns_name: |
| active[id_fields[2]] = None |
| |
| return active.keys() |
| # |
| #-------------------------------------------------------------- |
| # |
| def running_config_tenant_details(config, tenant): |
| """ |
| Display the details for the fields of a tenant which may have |
| non-default values. |
| """ |
| if tenant['active'] != mi.field_default_value('tenant', 'active'): |
| config.append('no active\n') |
| # mi.obj_type_config_fields('vns-definition') only shows fields which are |
| # user editable, which is none, since only the command descriptions |
| # modify the fields. XXX func which returns fields which can be updated |
| # via command descriptions? |
| tenant_fields = ['description', 'origin'] |
| |
| for field in sorted(tenant_fields): |
| running_config_include_field(config, |
| 'tenant', field, |
| tenant.get(field,''), 1) |
| |
| # |
| #-------------------------------------------------------------- |
| # |
| def running_config_tenant_router_details(config, vrouter,indent=0): |
| """ |
| Display the details for the fields of a vrouter which may have |
| non-default values. |
| """ |
| vrouter_fields = ['description', 'origin'] |
| |
| for field in sorted(vrouter_fields): |
| running_config_include_field(config, |
| 'virtualrouter', field, |
| vrouter.get(field,''), indent+1) |
| |
| # |
| #-------------------------------------------------------------- |
| # |
| def running_config_router_interface_details(config, vr_interface, indent=0): |
| """ |
| Display the details for the fields of a router interface which may have |
| non-default values. |
| """ |
| if 'vns-connected' in vr_interface and vr_interface['vns-connected'] is not None: |
| vns=vr_interface['vns-connected'].split('|') |
| vns_name=vns[1] |
| config.append(' ' *2*indent + 'interface %s vns %s \n' % (vr_interface['vriname'],vns_name)) |
| elif 'router-connected' in vr_interface and vr_interface['router-connected'] is not None: |
| router=vr_interface['router-connected'].split('|') |
| tenant_name=router[0] |
| router_name=router[1] |
| config.append(' ' *2*indent +'interface %s tenant %s %s \n' % (vr_interface['vriname'],tenant_name, router_name)) |
| |
| if vr_interface['active'] != mi.field_default_value('virtualrouter-interface', 'active'): |
| config.append(' ' *2*(indent+1) + 'no active\n') |
| |
| vri_fields = ['origin'] |
| |
| for field in sorted(vri_fields): |
| running_config_include_field(config, |
| 'virtualrouter-interface', field, |
| vr_interface.get(field,''), indent+1) |
| try: |
| ip_address_pool = sdnsh.get_table_from_store('interface-address-pool','virtual-router-interface', vr_interface['id'], "exact") |
| except Exception: |
| ip_address_pool = {} |
| pass |
| for ip_address in ip_address_pool: |
| config.append(' ' *2*(indent+1) + 'ip %s\n' % (utif.ip_and_neg_mask(ip_address['ip-address'],ip_address['subnet-mask']))) |
| |
| # |
| #-------------------------------------------------------------- |
| # |
| def running_config_router_rule_details(config, vr_route,indent=0): |
| """ |
| Display the details for the fields of a router interface which may have |
| non-default values. |
| """ |
| config_str=' ' * 2*indent |
| if 'src-tenant' in vr_route and vr_route['src-tenant'] is not None: |
| config_str+=('route from tenant %s' % vr_route['src-tenant']) |
| if 'src-vns' in vr_route and vr_route['src-vns'] is not None: |
| vns=vr_route['src-vns'].split('|') |
| vns_name=vns[1] |
| config_str+=(' vns %s' % vns_name) |
| else: |
| if 'src-ip' in vr_route and vr_route['src-ip'] is not None: |
| config_str+=('route from %s' % (utif.ip_and_neg_mask(vr_route['src-ip'],vr_route['src-ip-mask']))) |
| else: |
| config_str+=('route from any') |
| |
| config_str+=(' to') |
| |
| if 'dst-tenant' in vr_route and vr_route['dst-tenant'] is not None: |
| config_str+=(' tenant %s' % vr_route['dst-tenant']) |
| if 'dst-vns' in vr_route and vr_route['dst-vns'] is not None: |
| vns=vr_route['dst-vns'].split('|') |
| vns_name=vns[1] |
| config_str+=(' vns %s' % vns_name) |
| else: |
| if 'dst-ip' in vr_route and vr_route['dst-ip'] is not None: |
| config_str+=(' %s' % (utif.ip_and_neg_mask(vr_route['dst-ip'],vr_route['dst-ip-mask']))) |
| else: |
| config_str+=(' any') |
| |
| if 'nh-ip' in vr_route and vr_route['nh-ip'] is not None: |
| config_str+=(' %s' % vr_route['nh-ip']) |
| if 'gateway-pool' in vr_route and vr_route['gateway-pool'] is not None: |
| gwpool= vr_route['gateway-pool'].split('|') |
| gwpool_name=gwpool[-1] |
| config_str+=(' gw-pool %s' % gwpool_name) |
| if 'outgoing-intf' in vr_route and vr_route['outgoing-intf'] is not None: |
| intf= vr_route['outgoing-intf'].split('|') |
| intf_name=intf[-1] |
| config_str+=(' %s' % intf_name) |
| |
| config_str+=(' %s\n' % vr_route['action']) |
| config.append(config_str) |
| |
| # |
| #-------------------------------------------------------------- |
| # |
| def running_config_router_gwpool_details(config, vr_gwpool, indent=0): |
| """ |
| Display the details for the fields of a router gateway pool which may have |
| non-default values. |
| """ |
| config.append(' ' *2*indent + 'gateway-pool %s\n' % (vr_gwpool['vrgwname'])) |
| |
| try: |
| gw_address_pool = sdnsh.get_table_from_store('gateway-address-pool','virtual-router-gwpool', vr_gwpool['id'], "exact") |
| except Exception: |
| gw_address_pool = {} |
| pass |
| for gw_address in gw_address_pool: |
| config.append(' ' *2*(indent+1) + 'ip %s\n' % gw_address['ip-address']) |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| |
| def running_config_vns_details(config, vns,indent=0): |
| """ |
| Display the details for the fields of a vns which may have |
| non-default values. |
| """ |
| if vns['active'] != mi.field_default_value('vns-definition', 'active'): |
| config.append(' ' *2*indent + 'no active\n') |
| # mi.obj_type_config_fields('vns-definition') only shows fields which are |
| # user editable, which is none, since only the command descriptions |
| # modify the fields. XXX func which returns fields which can be updated |
| # via command descriptions? |
| vns_fields = ['description', 'priority', 'origin', |
| 'arp-mode', 'dhcp-mode', 'dhcp-ip', 'broadcast'] |
| |
| for field in sorted(vns_fields): |
| running_config_include_field(config, |
| 'vns-definition', field, |
| vns.get(field,''), indent) |
| vns_use_fields = ['address-space'] |
| for field in sorted(vns_use_fields): |
| running_config_include_field(config, |
| 'vns-definition', field, |
| vns.get(field,''), indent, "use ") |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_vns_if_and_access_group(config, vns_name, if_name, vns_interface_acl,indent=0): |
| """ |
| Decorate the config with access_group details |
| """ |
| obj_type = 'vns-interface-access-list' |
| key = mi.pk(obj_type) |
| |
| for if_acl in vns_interface_acl: |
| # add compound key field names ('vns', 'name', etc) into if_acl |
| mi.split_compound_into_dict(obj_type, key, if_acl) |
| vns_id=if_acl['tenant'] +'|' + if_acl['vnsname'] |
| if vns_id == vns_name and if_acl['interface'] == if_name: |
| config.append(' '*2*indent + "access-group %s %s\n" % |
| (if_acl['name'], if_acl['in-out'])) |
| |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def firewall_rule(rule): |
| """ |
| Return a string, the command which enabled this particular firewall rule |
| """ |
| tcp_ports = {6633: "openflow", |
| 80: "web", |
| 443: "ssl", |
| 22: "ssh"} |
| |
| src = '' |
| if 'src-ip' in rule and rule['src-ip'] != '': |
| src = 'from %s ' % rule['src-ip'] |
| dst = '' |
| if 'vrrp-ip' in rule and rule['vrrp-ip'] != '': |
| dst = 'local-ip %s ' % rule['vrrp-ip'] |
| |
| if rule['proto'] == 'tcp' and rule['port'] in tcp_ports: |
| allow = tcp_ports[rule['port']] |
| return "firewall allow %s%s%s" % (src, dst, allow) |
| elif rule['proto'] == 'vrrp': |
| return "firewall allow %s%svrrp" % (src, dst) |
| else: |
| return ("firewall allow %s%s%s %s" % |
| (src, dst, rule['proto'], rule['port'])) |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_firewall_rule(config, rule): |
| """ |
| Return a string, the command which enabled this particular firewall rule |
| """ |
| config.append(" %s\n" % firewall_rule(rule)) |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_feature(context, config, words): |
| """ |
| Decorate config with the feature commands |
| """ |
| feature = sdnsh.get_table_from_store('feature') |
| |
| if len(feature) == 0: |
| return |
| |
| f_config = [] |
| |
| for field in mi.obj_type_fields('feature'): |
| if field == mi.pk('feature'): |
| continue |
| if mi.is_foreign_key('feature', field): |
| continue |
| default_value = mi.field_default_value('feature', field) |
| name = field.replace('-feature','') |
| if mi.not_default_value('feature', field, feature[0][field]): |
| if default_value == True: |
| f_config.append('no feature %s\n' % name) |
| else: |
| f_config.append('feature %s\n' % name) |
| |
| if len(f_config) > 0: |
| config.append('! features enabled/disabled\n') |
| config += f_config |
| |
| feature_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'feature', |
| 'short-help' : 'Configuration for enabled/disabled features', |
| 'doc' : 'running-config|show-feature', |
| }, |
| ), |
| ) |
| |
| register_running_config('feature', 1000, None, running_config_feature, feature_running_config_tuple) |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_controller_node(context, config, words): |
| """ |
| Decorate config with controller-node details. |
| """ |
| # |
| controller_nodes = sdnsh.get_table_from_store("controller-node") |
| controller_ifs = sdnsh.get_table_from_store("controller-interface") |
| controller_dnses = sdnsh.get_table_from_store("controller-domain-name-server") |
| firewall_rules = sdnsh.get_table_from_store("firewall-rule") |
| |
| controller_alias_dict = create_obj_type_dict('controller-alias', 'controller') |
| |
| key = mi.pk('controller-node') |
| if len(words) > 0: |
| if not words[0] in [c[key] for c in controller_nodes]: |
| return sdnsh.error_msg('No such controller "%s"' % words[0]) |
| |
| for c in controller_nodes: |
| if len(words) > 0 and c[key] != words[0]: |
| continue |
| |
| c_config = [] |
| |
| if c[key] in controller_alias_dict: |
| c_config.append(' controller-alias %s\n' % |
| controller_alias_dict[c[key]][0]['alias']) |
| |
| # mi.obj_type_config_fields() doesn't work, since |
| # these fields aren't directly editable. |
| for field in ['ntp-server', |
| 'time-zone', |
| 'domain-lookups-enabled', |
| 'domain-name']: |
| default_value = mi.field_default_value('controller-node', field) |
| if field == 'ntp-server' and c.get(field, '') != default_value: |
| c_config.append(' ntp server %s\n' % c.get(field,'')) |
| elif field == 'time-zone' and c.get(field, '') != default_value: |
| c_config.append(' clock timezone %s\n' % c.get(field, '')) |
| elif field == 'domain-lookups-enabled' and c.get(field, '') != default_value: |
| c_config.append(' ip domain lookup\n') |
| elif field == 'domain-name' and c.get(field, '') != default_value: |
| c_config.append(' ip domain name %s\n' % c.get(field, '')) |
| |
| dns_key = mi.pk('controller-domain-name-server') |
| related_dns = [x for x in controller_dnses if x[dns_key].startswith(c[key])] |
| if related_dns: |
| sorted_dns = sorted(related_dns, key=lambda x: x['timestamp'], |
| cmp=lambda x,y: cmp(int(x), int(y))) |
| for dns in sorted_dns: |
| c_config.append(' ip name-server %s\n' % dns['ip']) |
| |
| for field in ['default-gateway', |
| 'logging-enabled']: |
| default_value = mi.field_default_value('controller-node', field) |
| if field == 'logging-enabled' and c.get(field, '') != default_value: |
| c_config.append(' logging on\n') |
| elif field == 'default-gateway' and c.get(field, '') != default_value: |
| c_config.append(' ip default-gateway %s\n' % c.get(field, '')) |
| |
| default_logging_server = mi.field_default_value('controller-node', 'logging-server') |
| default_logging_level = mi.field_default_value('controller-node', 'logging-level') |
| logging_server = c.get('logging-server', '') |
| logging_level = c.get('logging-level', '') |
| if ((logging_server != default_logging_server) or |
| (logging_level != default_logging_level)): |
| c_config.append(' logging server %s' % logging_server) |
| if logging_level != default_logging_level: |
| c_config.append(' level %s' % logging_level) |
| c_config.append('\n') |
| |
| open_ports = [22, 6633] |
| cif_key = mi.pk('controller-interface') |
| fr_key = mi.pk('firewall-rule') |
| for cif in controller_ifs: |
| if cif[cif_key].startswith(c[key]): |
| fields = cif[cif_key].split('|') |
| cconfig = [] |
| if len(fields) > 2: |
| if_n = int(fields[2]) # interface number is the 3rd field |
| ip_mode = 'static' |
| if 'mode' in cif and cif['mode'] != '': |
| if cif['mode'] != mi.field_default_value('controller-interface', |
| 'mode'): |
| # |
| # currently no checks are made to warn |
| # the user when 'dhcp' mode has been |
| # selected, and ip/netmask are set. |
| cconfig.append(' ip mode %s\n' % cif['mode']) |
| ip_mode = cif['mode'] |
| |
| if (ip_mode == 'static' and |
| 'ip' in cif and cif['ip'] != '' and |
| 'netmask' in cif and cif['netmask'] != ''): |
| cconfig.append(' ip address %s %s\n' % |
| (cif['ip'], cif['netmask'])) |
| |
| rules_for_cif = [r for r in firewall_rules |
| if r['interface'] == cif[cif_key]] |
| # look for deleted default firewall rules for first interface |
| if if_n == 0: |
| default_rule_missing = list(open_ports) |
| for rule in rules_for_cif: |
| if rule['proto'] == 'tcp' and rule['port'] in default_rule_missing: |
| default_rule_missing = filter(lambda p: p != rule['port'], |
| default_rule_missing) |
| for dr in default_rule_missing: |
| cconfig.append(' no firewall %s tcp\n' % dr) |
| |
| for rule in rules_for_cif: |
| # ignore default rules |
| if if_n == 0 and rule['proto'] == 'tcp' and rule['port'] in open_ports: |
| continue |
| running_config_firewall_rule(cconfig, rule) |
| if len(cconfig) > 0 or cif[cif_key] != 'localhost|Ethernet|0': |
| # mi.obj_type_config_fields() doesn't work, since |
| # these fields aren't directly editable. |
| c_config.append(' interface %s %s\n' % |
| (fields[1], fields[2])) |
| |
| c_config += cconfig |
| if len(c_config) != 0 or c[key] != 'localhost': |
| config.append('!\ncontroller-node %s\n' % c[key]) |
| config.append(''.join(c_config)) |
| |
| controller_node_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'controller-node', |
| 'short-help' : 'Configuration for controller nodes', |
| 'doc' : 'running-config|show-controller-node', |
| }, |
| { |
| 'field' : 'word', |
| 'type' : 'identifier', |
| 'completion' : 'complete-from-another', |
| 'other' : 'controller-node|id', |
| 'parent-field' : None, |
| 'action' : 'legacy-cli', |
| 'optional' : True, |
| } |
| ), |
| ) |
| |
| register_running_config('controller-node', 2000, None, |
| running_config_controller_node, controller_node_running_config_tuple) |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_switch(context, config, words): |
| """ |
| Switch running-config |
| """ |
| |
| switches = sdnsh.get_table_from_store("switch-config") |
| flow_table_entries = sdnsh.get_table_from_store('flow-entry') |
| |
| switch_interface_configs_dict = create_obj_type_dict('switch-interface-config', |
| 'switch') |
| |
| switch_interface_alias_dict = create_obj_type_dict('switch-interface-alias', |
| 'switch-interface') |
| key = mi.pk('switch-config') |
| if len(words) > 0: |
| if not words[0] in [s[key] for s in switches]: |
| return sdnsh.error_msg('No such switch "%s"' % words[0]) |
| |
| for s in switches: |
| # |
| # |
| if len(words) > 0 and s[key] != words[0]: |
| continue |
| |
| sw_config = [] |
| sw_config.append('!\nswitch %s\n' % s[key]) |
| # |
| # add any alias for this id if it exists |
| running_config_include_alias(sw_config, 1, 'switch-config', s[key]) |
| |
| if mi.not_default_value('switch-config', 'core-switch', s.get('core-switch','')): |
| sw_config.append(" core-switch\n") # default is not enabled |
| |
| if mi.not_default_value('switch-config', 'tunnel-termination', s.get('tunnel-termination','')): |
| sw_config.append(" tunnel termination %s\n" % |
| s['tunnel-termination']) |
| |
| # |
| # Look for matching switch interface config's |
| sic_key = mi.pk('switch-interface-config') |
| for sic in switch_interface_configs_dict.get(s[key], []): |
| fields = sic[sic_key].split('|') |
| if len(fields) > 1: |
| sic_config = [] |
| # XXX perhaps mi.obj_type_config_fields() here? |
| running_config_include_field(sic_config, |
| 'switch-interface-config', |
| 'mode', |
| sic['mode'], 2, |
| 'switchport ') |
| |
| if sic[sic_key] in switch_interface_alias_dict: |
| sic_config.append(' interface-alias %s\n' % |
| switch_interface_alias_dict[sic[sic_key]][0]['id']) |
| |
| if len(sic_config) > 0: |
| sw_config.append(' interface %s\n' % fields[1]) |
| sw_config += sic_config |
| |
| for fte in flow_table_entries: # |
| # how to handle defaults? |
| if fte['switch'] == s[key]: |
| sw_config.append(' flow-entry %s\n' % fte['name']) |
| fields_to_print = mi.cli_model_info.get_fields_to_print('flow-entry') |
| for f in fields_to_print: |
| v = fte.get(f, "") |
| if v != "" and f != 'name' and f != 'switch': |
| field_info = mi.cli_model_info.get_field_info('flow-entry', f) |
| if v == field_info.get('default', None) and f != 'active': |
| pass # literally, don't print this one |
| else: |
| sw_config.append(' %s %s\n' % (f, v)) |
| config += sw_config |
| |
| switch_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'switch', |
| 'short-help' : 'Configuration for switches', |
| 'doc' : 'running-config|show-switch', |
| }, |
| { |
| 'field' : 'word', |
| 'type' : 'dpid', |
| 'completion' : 'complete-from-another', |
| 'other' : 'switch|dpid', |
| 'parent-field' : None, |
| 'action' : 'legacy-cli', |
| 'data-handler' : 'alias-to-value', |
| 'optional' : True, |
| } |
| ), |
| ) |
| |
| register_running_config('switch', 3000, None, running_config_switch, switch_running_config_tuple) |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def running_config_host(context, config, words): |
| """ |
| Add host details, including tags. |
| """ |
| |
| # When there are no tags, and no host-aliases, the hosts don't need |
| # to be read. If it were possible to determine the number of entries |
| # without querying, then it would make sense to consider enumerating |
| # the host-aliases and tags if they were substantially smaller than |
| # the number of hosts. |
| # |
| |
| host_alias_dict = create_obj_type_dict("host-alias", 'host') |
| host_security_ip = create_obj_type_dict("host-security-ip-address", 'host') |
| host_security_ap = create_obj_type_dict("host-security-attachment-point", 'host') |
| switch_config = create_obj_type_dict('switch-interface-config', 'id') |
| |
| # Notice that only hosts configured are enumerated here, since |
| # discovered hosts can't have configuration. All configuration |
| # is attached to configured hosts, which are joined after discovered |
| # hosts for show commands. |
| hosts = sdnsh.get_table_from_store("host-config") |
| key = mi.pk('host-config') |
| |
| if len(words) > 0: |
| if not words[0] in [h[key] for h in hosts]: |
| return sdnsh.error_msg('No such host "%s"' % words[0]) |
| |
| for h in hosts: |
| if len(words) > 0 and words[0] != h[key]: |
| continue |
| |
| # host_config holds 'config' only for host. |
| host_config = [] |
| a_s = '' |
| if h.get('address-space','') != 'default': |
| a_s = 'address-space %s ' % h['address-space'] |
| vlan = '' |
| if h.get('vlan', '') != '': |
| vlan = 'vlan %s ' % h['vlan'] |
| host_config.append('!\nhost %s%s%s\n' % (a_s, vlan, h['mac'])) |
| # |
| # add the host alias for this id if it exists |
| if h[key] in host_alias_dict: |
| host_config.append(" host-alias %s\n" % |
| host_alias_dict[h[key]][0]['id']) |
| |
| # |
| # check for security policies |
| if h[key] in host_security_ip: |
| for ip in host_security_ip[h[key]]: |
| host_config.append(" security policy bind ip-address %s\n" % ip['ip-address']) |
| |
| if h[key] in host_security_ap: |
| for ap in host_security_ap[h[key]]: |
| host_config.append(" security policy bind attachment-point %s %s\n" % |
| (ap.get('dpid', 'all'), |
| utif.quote_string(ap['if-name-regex']))) |
| |
| config += host_config |
| |
| |
| host_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'host', |
| 'short-help' : 'Configuration for hosts', |
| 'doc' : 'running-config|show-host', |
| }, |
| { |
| 'field' : 'word', |
| 'type' : 'host', |
| 'completion' : 'complete-from-another', |
| 'other' : 'host|mac', |
| 'parent-field' : None, |
| 'data-handler' : 'alias-to-value', |
| 'action' : 'legacy-cli', |
| 'optional' : True, |
| } |
| ), |
| ) |
| |
| register_running_config('host', 5000, None, running_config_host, host_running_config_tuple) |
| |
| def running_config_vns(context, config, words): |
| """ |
| Add the VNS configuration detils |
| this command will only generate vns under default tenant |
| other vnss will be generated under running_config_tenant |
| """ |
| #generate specific vns configuration, used in tenant config generation too |
| if len(words) > 0: |
| # XXX should the vns-definiiton also be included? |
| sdnsh.show_vns_definition_running_config(config, words[0]) |
| config += sdnsh.show_vns_running_config(words[0]) |
| return |
| |
| #generate all VNSs for default tenant, this is used backward compatible as "show running-config vns" |
| try: |
| vns_def = sdnsh.get_table_from_store('vns-definition') |
| except Exception: |
| vns_def = [] |
| |
| try: |
| vns_rules = create_obj_type_dict('vns-interface-rule', 'vns') |
| except Exception: |
| vns_rules = {} |
| |
| try: |
| vns_acls = create_obj_type_dict('vns-access-list', 'vns') |
| except Exception: |
| vns_acls = {} |
| |
| try: |
| vns_acl_entries = create_obj_type_dict('vns-access-list-entry', |
| 'vns-access-list') |
| except Exception: |
| vns_acl_entries = {} |
| |
| try: |
| vns_interface = create_obj_type_dict('vns-interface-config', 'vns') |
| except Exception: |
| vns_interface = {} |
| |
| try: |
| vns_interface_acl = create_obj_type_dict('vns-interface-access-list', |
| 'vns-interface') |
| except Exception: |
| vns_interface_acl = {} |
| |
| # |
| # vns-definition |
| for vns in vns_def: |
| if vns['tenant']=='default': |
| vns_name = vns['id'] |
| config.append('!\nvns-definition %s\n' % vns['vnsname']) |
| running_config_vns_details(config, vns) |
| if vns_name in vns_rules: |
| running_config_vns_if_rule(config, |
| vns_rules[vns_name],indent=1) |
| # |
| # vns |
| acl_key = mi.pk('vns-access-list') |
| if_key = mi.pk('vns-interface-config') |
| |
| for vns in vns_def: |
| if vns['tenant'] =='default': |
| vns_name = vns['id'] |
| vns_config = [] |
| |
| for acl in vns_acls.get(vns_name, []): |
| acl_id = acl[acl_key] # compound primary key value |
| running_config_vns_acl(vns_config, vns_name, acl, vns_acl_entries[acl_id],indent=1) |
| |
| for vns_if in vns_interface.get(vns_name, []): |
| vns_if_acl_config = [] |
| vns_if_id = vns_if[if_key] # compound primary key value |
| if vns_if_id in vns_interface_acl: |
| running_config_vns_if_and_access_group(vns_if_acl_config, |
| vns_name, |
| vns_if['interface'], |
| vns_interface_acl[vns_if_id],indent=1) |
| if vns_if_acl_config: |
| vns_config.append(" interface %s\n" % vns_if['interface']) |
| vns_config += vns_if_acl_config |
| |
| if len(vns_config) > 0: |
| config.append('!\nvns %s\n' % vns['vnsname']) |
| config += vns_config |
| vns_config = [] |
| |
| |
| def is_vns_enabled(context): |
| return context.netvirt_feature_enabled() |
| |
| vns_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'vns', |
| 'short-help' : 'Configuration for network virtualization', |
| 'doc' : 'running-config|show-vns', |
| }, |
| { |
| 'field' : 'word', |
| 'type' : 'identifier', |
| 'completion' : 'complete-from-another', |
| 'other' : 'vns-definition|vnsname', |
| 'parent-field' : None, |
| 'action' : 'legacy-cli', |
| 'optional' : True, |
| } |
| ), |
| ) |
| |
| register_running_config('vns', 7000, is_vns_enabled, running_config_vns, vns_running_config_tuple) |
| |
| #tenant running config |
| def running_config_tenant(context, config, words): |
| |
| if len(words) > 0: |
| # XXX should the vns-definiiton also be included? |
| sdnsh.show_tenant_running_config(config, words[0]) |
| return |
| |
| try: |
| tenants = sdnsh.get_table_from_store('tenant') |
| except Exception: |
| tenants = [] |
| |
| for tenant in tenants: |
| sdnsh.show_tenant_running_config(config, tenant['name']) |
| |
| |
| tenant_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'tenant', |
| 'short-help' : 'Configuration for Tenant', |
| 'doc' : 'running-config|show-tenant', |
| }, |
| { |
| 'field' : 'word', |
| 'type' : 'identifier', |
| 'completion' : 'complete-from-another', |
| 'other' : 'tenant|name', |
| 'parent-field' : None, |
| 'action' : 'legacy-cli', |
| 'optional' : True, |
| } |
| ), |
| ) |
| |
| register_running_config('tenant', 7000, is_vns_enabled, running_config_tenant, tenant_running_config_tuple) |
| |
| def running_config_static_arp(context, config, words): |
| |
| try: |
| staticARPs = sdnsh.get_table_from_store('static-arp') |
| except Exception: |
| staticARPs = [] |
| first=True |
| for arp in staticARPs: |
| if first: |
| config.append('!\narp %s %s\n' % (arp['ip'], arp['mac'])) |
| first=False |
| else: |
| config.append('arp %s %s\n' % (arp['ip'], arp['mac'])) |
| |
| staticARP_running_config_tuple = ( |
| ( |
| { |
| 'optional' : False, |
| 'field' : 'running-config', |
| 'type' : 'enum', |
| 'values' : 'static-arp', |
| 'short-help' : 'Configuration for static ARP entry', |
| 'doc' : 'running-config|show-static-arp', |
| }, |
| ), |
| ) |
| |
| register_running_config('static-arp', 6000, None, running_config_static_arp, staticARP_running_config_tuple) |
| |
| |
| # |
| # -------------------------------------------------------------------------------- |
| |
| def implement_show_running_config(words): |
| """ |
| Manager for the 'show running-config' command, which calls the |
| specific detail functions for any of the parameters. |
| """ |
| |
| # LOOK! hardwired - need to use the obj_type_info and the field_list |
| # LOOK! how are these sorted? |
| config = [] |
| if len(words) > 0: |
| # pick the word |
| choice = utif.full_word_from_choices(words[0], |
| registry_items_enabled()) |
| if sdnsh.netvirt_feature_enabled() and 'vns'.startswith(words[0]): |
| if choice: |
| return sdnsh.error_msg("%s and %s ambiguous" % (choice, 'vns')) |
| choice = 'vns' |
| |
| if choice: |
| perform_running_config(choice, sdnsh, config, words) |
| else: |
| return sdnsh.error_msg("unknown running-config item: %s" % words[0]) |
| # config[:-1] removes the last trailing newline |
| return ''.join(config)[:-1] |
| else: |
| # Create the order based on the registration value |
| running_config_order = sorted(registry_items_enabled(), |
| key=lambda item: running_config_registry[item]['order']) |
| exclude_list=['vns'] |
| for rc in running_config_order: |
| if rc not in exclude_list: |
| perform_running_config(rc, sdnsh, config, words) |
| |
| prefix = [] |
| if len(config) > 0: |
| date_string = datetime.datetime.now().strftime("%Y-%m-%d.%H:%M:%S %Z") |
| prefix.append("!\n! ") |
| prefix.append(sdnsh.do_show_version(words)) |
| prefix.append("\n! Current Time: ") |
| prefix.append(date_string) |
| prefix.append("\n!\n") |
| prefix.append("version 1.0\n") # need a better determination of command syntax version |
| |
| # config[:-1] removes the last trailing newline |
| return ''.join(prefix) + ''.join(config)[:-1] |
| |
| |