blob: e3fc80ca3c75b6f978b1c326c63366b09fab6973 [file] [log] [blame]
Srikanth Vavilapalli1725e492014-12-01 17:50:52 -08001#
2# Copyright (c) 2012,2013 Big Switch Networks, Inc.
3#
4# Licensed under the Eclipse Public License, Version 1.0 (the
5# "License"); you may not use this file except in compliance with the
6# License. You may obtain a copy of the License at
7#
8# http://www.eclipse.org/legal/epl-v10.html
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied. See the License for the specific language governing
14# permissions and limitations under the License.
15#
16
17# sdnsh - The Controller Shell
18
19#
20# show running-config
21# and associated
22#
23
24import datetime
25import re
26import utif
27import modi
28
29from midw import *
30from vnsw import *
31
32
33def init_running_config(bs, modi):
34 global sdnsh, mi
35 sdnsh = bs
36 mi = modi
37
38
39running_config_registry = {}
40
41running_config_command_choices = {
42 'optional' : True,
43 'choices' : (
44 )
45}
46
47#
48# --------------------------------------------------------------------------------
49
50def register_running_config(name, order, feature, running_config_proc, command_tuple = None):
51 """
52 Register a callback to manage the display of component running configs
53
54 @feature a predicate to call, returns True/False to enable/disable this entry
55 """
56 running_config_registry[name] = { 'order' : order,
57 'feature' : feature,
58 'proc' : running_config_proc }
59 if command_tuple:
60 global running_config_command_choices
61 running_config_command_choices['choices'] += command_tuple
62
63#
64# --------------------------------------------------------------------------------
65
66def registry_items_enabled():
67 """
68 Return a list of active running config entries, this is a subset of
69 the registered items, only items which are currently enabled via features
70 """
71 return [name for name in running_config_registry.keys()
72 if running_config_registry[name]['feature'] == None or
73 running_config_registry[name]['feature'](sdnsh) == True]
74
75
76#
77# --------------------------------------------------------------------------------
78
79def perform_running_config(name, context, config, words):
80 """
81 Callout to append to config
82 """
83 if name in running_config_registry:
84 running_config_registry[name]['proc'](context, config, words)
85
86#
87# --------------------------------------------------------------------------------
88
89def running_config_include_alias(config, indent, obj_type, key):
90 """
91 Given an obj_type and an id, add the alias for this obj_type
92 to the config.
93
94 The table lookup is a bit difficult to understand; keep in
95 mind, for example, the 'host-alias' table has a primary
96 key called id, and a foreign key called 'host'.
97
98 Return False if no alias exists, and True when one was added to 'config'
99 """
100 if obj_type in mi.alias_obj_type_xref:
101 for alias in mi.alias_obj_type_xref[obj_type]:
102 field = mi.alias_obj_type_field(alias)
103 try:
104 row = sdnsh.get_table_from_store(alias, field, key)
105 except:
106 row = []
107
108 if len(row) > 1:
109 sdnsh.warning('%s %s: alias count > 1' % (alias, key))
110 elif len(row) == 1:
111 config.append("%s%s %s\n" %
112 (' ' * indent, alias, row[0]['id']))
113
114
115#
116# --------------------------------------------------------------------------------
117
118def not_default_value(obj_type, field, value):
119 """
120 Return True when the value passed in is not the default value.
121 """
122 default_value = mi.field_default_value(obj_type, field)
123 if (mi.is_null_allowed(obj_type, field) and value != None) or \
124 (not mi.is_null_allowed(obj_type, field) and default_value != None
125 and (default_value != value)):
126 return True
127 return False
128
129
130#
131# --------------------------------------------------------------------------------
132
133def running_config_include_field(config, obj_type, field, value,
134 indent, prefix = ""):
135 """
136 Identify fields of obj_types who's values differ from the default
137 values, since these need to be included into the running-config
138 """
139 if mi.not_default_value(obj_type, field, value):
140 #
141 if mi.is_field_string(obj_type, field):
142 config.append(' ' * (indent + indent) + prefix + field +
143 " %s\n" % utif.quote_string(value))
144 else:
145 config.append(' ' * (indent + indent) + prefix + field +
146 ' %s\n' % value)
147
148
149#
150# --------------------------------------------------------------------------------
151
152def running_config_vns_acl(config, vns_name, acl, vns_acl_entries,indent=0):
153 """
154 factored out due to excessive indent level in do_show_running_config
155 and to allow re-use in 'show vns <n> running-config'
156 """
157 key = mi.pk('vns-access-list')
158
159 config.append(' ' *2*indent + 'access-list %s\n' % acl['name'])
160
161 #
162 vns_acl_fields = ['priority', 'description']
163 for field in vns_acl_fields:
164 running_config_include_field(config,
165 'vns-access-list', field,
166 acl.get(field, ''), indent+1)
167
168 vns_acl_entries = sorted(vns_acl_entries,
169 cmp=lambda x,y: int(x['rule']) - int(y['rule']))
170
171 for acl_entry in vns_acl_entries:
172 config.append(' ' *2*(indent+1) + '%s %s %s %s\n' % (
173 acl_entry['rule'],
174 acl_entry['action'],
175 acl_entry['type'],
176 vns_acl_entry_to_text(acl_entry)))
177
178
179
180#
181# --------------------------------------------------------------------------------
182
183def running_config_vns_if_rule(config,
184 vns_if_rule_entries,indent=0):
185 """
186 factored out due to excessive indent level in do_show_running_config
187 and to allow re-use in 'show vns <n> running-config'
188
189 'vns_if_rule_entries' are all the entries to concatenate to the confi
190 """
191 for rule in vns_if_rule_entries:
192 config.append(' ' *2*indent +'interface-rule %s\n' % rule['rule'])
193
194 #
195 # mi.obj_type_config_fields('vns-interface-rule') isn't used
196 # here since switch/port is collected together (managed together)
197 for field in ['description', 'active', 'priority', ]:
198 running_config_include_field(config,
199 'vns-interface-rule', field,
200 rule.get(field, ''), indent+1,)
201
202 for field in [ 'mac', 'ip-subnet', 'vlans', 'tags']:
203 running_config_include_field(config,
204 'vns-interface-rule', field,
205 rule.get(field, ''), indent+1, 'match ')
206 if rule.get('allow-multiple', False):
207 config.append(' ' * 2*(indent+1) + 'allow-multiple\n');
208 if rule.get('vlan-tag-on-egress', False):
209 config.append(' ' * 2*(indent+1) + 'vlan-tag-on-egress\n');
210
211 #
212 # Manage switch and ports differently, placing both on the
213 # same line when ports exist, and replacing the switch alias
214 # when its available.
215 if 'switch' in rule:
216 dpid_or_alias = alias_lookup_with_foreign_key('switch-alias',
217 rule['switch'])
218 if dpid_or_alias == None:
219 dpid_or_alias = rule['switch'] # dpid
220 if 'ports' in rule:
221 config.append(' ' *2*(indent+1) + 'match switch %s %s\n' %
222 (dpid_or_alias, rule['ports']))
223 else:
224 config.append(' ' *2*(indent+1) + 'match switch %s\n' % dpid_or_alias)
225
226#
227# --------------------------------------------------------------------------------
228
229def running_config_active_vns_interfaces(vns_name, vns_interface_acl):
230 """
231 Return a list of interfaces which have configuration information needing
232 to be saved
233 """
234 active = {}
235 key = mi.pk('vns-interface-access-list')
236
237 for if_acl in vns_interface_acl:
238 id_fields = if_acl[key].split('|')
239 vns_id= id_fields[0] +'|' + id_fields[1]
240 if vns_id == vns_name:
241 active[id_fields[2]] = None
242
243 return active.keys()
244#
245#--------------------------------------------------------------
246#
247def running_config_tenant_details(config, tenant):
248 """
249 Display the details for the fields of a tenant which may have
250 non-default values.
251 """
252 if tenant['active'] != mi.field_default_value('tenant', 'active'):
253 config.append('no active\n')
254 # mi.obj_type_config_fields('vns-definition') only shows fields which are
255 # user editable, which is none, since only the command descriptions
256 # modify the fields. XXX func which returns fields which can be updated
257 # via command descriptions?
258 tenant_fields = ['description', 'origin']
259
260 for field in sorted(tenant_fields):
261 running_config_include_field(config,
262 'tenant', field,
263 tenant.get(field,''), 1)
264
265#
266#--------------------------------------------------------------
267#
268def running_config_tenant_router_details(config, vrouter,indent=0):
269 """
270 Display the details for the fields of a vrouter which may have
271 non-default values.
272 """
273 vrouter_fields = ['description', 'origin']
274
275 for field in sorted(vrouter_fields):
276 running_config_include_field(config,
277 'virtualrouter', field,
278 vrouter.get(field,''), indent+1)
279
280#
281#--------------------------------------------------------------
282#
283def running_config_router_interface_details(config, vr_interface, indent=0):
284 """
285 Display the details for the fields of a router interface which may have
286 non-default values.
287 """
288 if 'vns-connected' in vr_interface and vr_interface['vns-connected'] is not None:
289 vns=vr_interface['vns-connected'].split('|')
290 vns_name=vns[1]
291 config.append(' ' *2*indent + 'interface %s vns %s \n' % (vr_interface['vriname'],vns_name))
292 elif 'router-connected' in vr_interface and vr_interface['router-connected'] is not None:
293 router=vr_interface['router-connected'].split('|')
294 tenant_name=router[0]
295 router_name=router[1]
296 config.append(' ' *2*indent +'interface %s tenant %s %s \n' % (vr_interface['vriname'],tenant_name, router_name))
297
298 if vr_interface['active'] != mi.field_default_value('virtualrouter-interface', 'active'):
299 config.append(' ' *2*(indent+1) + 'no active\n')
300
301 vri_fields = ['origin']
302
303 for field in sorted(vri_fields):
304 running_config_include_field(config,
305 'virtualrouter-interface', field,
306 vr_interface.get(field,''), indent+1)
307 try:
308 ip_address_pool = sdnsh.get_table_from_store('interface-address-pool','virtual-router-interface', vr_interface['id'], "exact")
309 except Exception:
310 ip_address_pool = {}
311 pass
312 for ip_address in ip_address_pool:
313 config.append(' ' *2*(indent+1) + 'ip %s\n' % (utif.ip_and_neg_mask(ip_address['ip-address'],ip_address['subnet-mask'])))
314
315#
316#--------------------------------------------------------------
317#
318def running_config_router_rule_details(config, vr_route,indent=0):
319 """
320 Display the details for the fields of a router interface which may have
321 non-default values.
322 """
323 config_str=' ' * 2*indent
324 if 'src-tenant' in vr_route and vr_route['src-tenant'] is not None:
325 config_str+=('route from tenant %s' % vr_route['src-tenant'])
326 if 'src-vns' in vr_route and vr_route['src-vns'] is not None:
327 vns=vr_route['src-vns'].split('|')
328 vns_name=vns[1]
329 config_str+=(' vns %s' % vns_name)
330 else:
331 if 'src-ip' in vr_route and vr_route['src-ip'] is not None:
332 config_str+=('route from %s' % (utif.ip_and_neg_mask(vr_route['src-ip'],vr_route['src-ip-mask'])))
333 else:
334 config_str+=('route from any')
335
336 config_str+=(' to')
337
338 if 'dst-tenant' in vr_route and vr_route['dst-tenant'] is not None:
339 config_str+=(' tenant %s' % vr_route['dst-tenant'])
340 if 'dst-vns' in vr_route and vr_route['dst-vns'] is not None:
341 vns=vr_route['dst-vns'].split('|')
342 vns_name=vns[1]
343 config_str+=(' vns %s' % vns_name)
344 else:
345 if 'dst-ip' in vr_route and vr_route['dst-ip'] is not None:
346 config_str+=(' %s' % (utif.ip_and_neg_mask(vr_route['dst-ip'],vr_route['dst-ip-mask'])))
347 else:
348 config_str+=(' any')
349
350 if 'nh-ip' in vr_route and vr_route['nh-ip'] is not None:
351 config_str+=(' %s' % vr_route['nh-ip'])
352 if 'gateway-pool' in vr_route and vr_route['gateway-pool'] is not None:
353 gwpool= vr_route['gateway-pool'].split('|')
354 gwpool_name=gwpool[-1]
355 config_str+=(' gw-pool %s' % gwpool_name)
356 if 'outgoing-intf' in vr_route and vr_route['outgoing-intf'] is not None:
357 intf= vr_route['outgoing-intf'].split('|')
358 intf_name=intf[-1]
359 config_str+=(' %s' % intf_name)
360
361 config_str+=(' %s\n' % vr_route['action'])
362 config.append(config_str)
363
364#
365#--------------------------------------------------------------
366#
367def running_config_router_gwpool_details(config, vr_gwpool, indent=0):
368 """
369 Display the details for the fields of a router gateway pool which may have
370 non-default values.
371 """
372 config.append(' ' *2*indent + 'gateway-pool %s\n' % (vr_gwpool['vrgwname']))
373
374 try:
375 gw_address_pool = sdnsh.get_table_from_store('gateway-address-pool','virtual-router-gwpool', vr_gwpool['id'], "exact")
376 except Exception:
377 gw_address_pool = {}
378 pass
379 for gw_address in gw_address_pool:
380 config.append(' ' *2*(indent+1) + 'ip %s\n' % gw_address['ip-address'])
381
382#
383# --------------------------------------------------------------------------------
384
385
386def running_config_vns_details(config, vns,indent=0):
387 """
388 Display the details for the fields of a vns which may have
389 non-default values.
390 """
391 if vns['active'] != mi.field_default_value('vns-definition', 'active'):
392 config.append(' ' *2*indent + 'no active\n')
393 # mi.obj_type_config_fields('vns-definition') only shows fields which are
394 # user editable, which is none, since only the command descriptions
395 # modify the fields. XXX func which returns fields which can be updated
396 # via command descriptions?
397 vns_fields = ['description', 'priority', 'origin',
398 'arp-mode', 'dhcp-mode', 'dhcp-ip', 'broadcast']
399
400 for field in sorted(vns_fields):
401 running_config_include_field(config,
402 'vns-definition', field,
403 vns.get(field,''), indent)
404 vns_use_fields = ['address-space']
405 for field in sorted(vns_use_fields):
406 running_config_include_field(config,
407 'vns-definition', field,
408 vns.get(field,''), indent, "use ")
409
410
411#
412# --------------------------------------------------------------------------------
413
414def running_config_vns_if_and_access_group(config, vns_name, if_name, vns_interface_acl,indent=0):
415 """
416 Decorate the config with access_group details
417 """
418 obj_type = 'vns-interface-access-list'
419 key = mi.pk(obj_type)
420
421 for if_acl in vns_interface_acl:
422 # add compound key field names ('vns', 'name', etc) into if_acl
423 mi.split_compound_into_dict(obj_type, key, if_acl)
424 vns_id=if_acl['tenant'] +'|' + if_acl['vnsname']
425 if vns_id == vns_name and if_acl['interface'] == if_name:
426 config.append(' '*2*indent + "access-group %s %s\n" %
427 (if_acl['name'], if_acl['in-out']))
428
429
430
431#
432# --------------------------------------------------------------------------------
433
434def firewall_rule(rule):
435 """
436 Return a string, the command which enabled this particular firewall rule
437 """
438 tcp_ports = {6633: "openflow",
439 80: "web",
440 443: "ssl",
441 22: "ssh"}
442
443 src = ''
444 if 'src-ip' in rule and rule['src-ip'] != '':
445 src = 'from %s ' % rule['src-ip']
446 dst = ''
447 if 'vrrp-ip' in rule and rule['vrrp-ip'] != '':
448 dst = 'local-ip %s ' % rule['vrrp-ip']
449
450 if rule['proto'] == 'tcp' and rule['port'] in tcp_ports:
451 allow = tcp_ports[rule['port']]
452 return "firewall allow %s%s%s" % (src, dst, allow)
453 elif rule['proto'] == 'vrrp':
454 return "firewall allow %s%svrrp" % (src, dst)
455 else:
456 return ("firewall allow %s%s%s %s" %
457 (src, dst, rule['proto'], rule['port']))
458
459#
460# --------------------------------------------------------------------------------
461
462def running_config_firewall_rule(config, rule):
463 """
464 Return a string, the command which enabled this particular firewall rule
465 """
466 config.append(" %s\n" % firewall_rule(rule))
467
468#
469# --------------------------------------------------------------------------------
470
471def running_config_feature(context, config, words):
472 """
473 Decorate config with the feature commands
474 """
475 feature = sdnsh.get_table_from_store('feature')
476
477 if len(feature) == 0:
478 return
479
480 f_config = []
481
482 for field in mi.obj_type_fields('feature'):
483 if field == mi.pk('feature'):
484 continue
485 if mi.is_foreign_key('feature', field):
486 continue
487 default_value = mi.field_default_value('feature', field)
488 name = field.replace('-feature','')
489 if mi.not_default_value('feature', field, feature[0][field]):
490 if default_value == True:
491 f_config.append('no feature %s\n' % name)
492 else:
493 f_config.append('feature %s\n' % name)
494
495 if len(f_config) > 0:
496 config.append('! features enabled/disabled\n')
497 config += f_config
498
499feature_running_config_tuple = (
500(
501 {
502 'optional' : False,
503 'field' : 'running-config',
504 'type' : 'enum',
505 'values' : 'feature',
506 'short-help' : 'Configuration for enabled/disabled features',
507 'doc' : 'running-config|show-feature',
508 },
509 ),
510)
511
512register_running_config('feature', 1000, None, running_config_feature, feature_running_config_tuple)
513
514
515#
516# --------------------------------------------------------------------------------
517
518def running_config_controller_node(context, config, words):
519 """
520 Decorate config with controller-node details.
521 """
522 #
523 controller_nodes = sdnsh.get_table_from_store("controller-node")
524 controller_ifs = sdnsh.get_table_from_store("controller-interface")
525 controller_dnses = sdnsh.get_table_from_store("controller-domain-name-server")
526 firewall_rules = sdnsh.get_table_from_store("firewall-rule")
527
528 controller_alias_dict = create_obj_type_dict('controller-alias', 'controller')
529
530 key = mi.pk('controller-node')
531 if len(words) > 0:
532 if not words[0] in [c[key] for c in controller_nodes]:
533 return sdnsh.error_msg('No such controller "%s"' % words[0])
534
535 for c in controller_nodes:
536 if len(words) > 0 and c[key] != words[0]:
537 continue
538
539 c_config = []
540
541 if c[key] in controller_alias_dict:
542 c_config.append(' controller-alias %s\n' %
543 controller_alias_dict[c[key]][0]['alias'])
544
545 # mi.obj_type_config_fields() doesn't work, since
546 # these fields aren't directly editable.
547 for field in ['ntp-server',
548 'time-zone',
549 'domain-lookups-enabled',
550 'domain-name']:
551 default_value = mi.field_default_value('controller-node', field)
552 if field == 'ntp-server' and c.get(field, '') != default_value:
553 c_config.append(' ntp server %s\n' % c.get(field,''))
554 elif field == 'time-zone' and c.get(field, '') != default_value:
555 c_config.append(' clock timezone %s\n' % c.get(field, ''))
556 elif field == 'domain-lookups-enabled' and c.get(field, '') != default_value:
557 c_config.append(' ip domain lookup\n')
558 elif field == 'domain-name' and c.get(field, '') != default_value:
559 c_config.append(' ip domain name %s\n' % c.get(field, ''))
560
561 dns_key = mi.pk('controller-domain-name-server')
562 related_dns = [x for x in controller_dnses if x[dns_key].startswith(c[key])]
563 if related_dns:
564 sorted_dns = sorted(related_dns, key=lambda x: x['timestamp'],
565 cmp=lambda x,y: cmp(int(x), int(y)))
566 for dns in sorted_dns:
567 c_config.append(' ip name-server %s\n' % dns['ip'])
568
569 for field in ['default-gateway',
570 'logging-enabled']:
571 default_value = mi.field_default_value('controller-node', field)
572 if field == 'logging-enabled' and c.get(field, '') != default_value:
573 c_config.append(' logging on\n')
574 elif field == 'default-gateway' and c.get(field, '') != default_value:
575 c_config.append(' ip default-gateway %s\n' % c.get(field, ''))
576
577 default_logging_server = mi.field_default_value('controller-node', 'logging-server')
578 default_logging_level = mi.field_default_value('controller-node', 'logging-level')
579 logging_server = c.get('logging-server', '')
580 logging_level = c.get('logging-level', '')
581 if ((logging_server != default_logging_server) or
582 (logging_level != default_logging_level)):
583 c_config.append(' logging server %s' % logging_server)
584 if logging_level != default_logging_level:
585 c_config.append(' level %s' % logging_level)
586 c_config.append('\n')
587
588 open_ports = [22, 6633]
589 cif_key = mi.pk('controller-interface')
590 fr_key = mi.pk('firewall-rule')
591 for cif in controller_ifs:
592 if cif[cif_key].startswith(c[key]):
593 fields = cif[cif_key].split('|')
594 cconfig = []
595 if len(fields) > 2:
596 if_n = int(fields[2]) # interface number is the 3rd field
597 ip_mode = 'static'
598 if 'mode' in cif and cif['mode'] != '':
599 if cif['mode'] != mi.field_default_value('controller-interface',
600 'mode'):
601 #
602 # currently no checks are made to warn
603 # the user when 'dhcp' mode has been
604 # selected, and ip/netmask are set.
605 cconfig.append(' ip mode %s\n' % cif['mode'])
606 ip_mode = cif['mode']
607
608 if (ip_mode == 'static' and
609 'ip' in cif and cif['ip'] != '' and
610 'netmask' in cif and cif['netmask'] != ''):
611 cconfig.append(' ip address %s %s\n' %
612 (cif['ip'], cif['netmask']))
613
614 rules_for_cif = [r for r in firewall_rules
615 if r['interface'] == cif[cif_key]]
616 # look for deleted default firewall rules for first interface
617 if if_n == 0:
618 default_rule_missing = list(open_ports)
619 for rule in rules_for_cif:
620 if rule['proto'] == 'tcp' and rule['port'] in default_rule_missing:
621 default_rule_missing = filter(lambda p: p != rule['port'],
622 default_rule_missing)
623 for dr in default_rule_missing:
624 cconfig.append(' no firewall %s tcp\n' % dr)
625
626 for rule in rules_for_cif:
627 # ignore default rules
628 if if_n == 0 and rule['proto'] == 'tcp' and rule['port'] in open_ports:
629 continue
630 running_config_firewall_rule(cconfig, rule)
631 if len(cconfig) > 0 or cif[cif_key] != 'localhost|Ethernet|0':
632 # mi.obj_type_config_fields() doesn't work, since
633 # these fields aren't directly editable.
634 c_config.append(' interface %s %s\n' %
635 (fields[1], fields[2]))
636
637 c_config += cconfig
638 if len(c_config) != 0 or c[key] != 'localhost':
639 config.append('!\ncontroller-node %s\n' % c[key])
640 config.append(''.join(c_config))
641
642controller_node_running_config_tuple = (
643 (
644 {
645 'optional' : False,
646 'field' : 'running-config',
647 'type' : 'enum',
648 'values' : 'controller-node',
649 'short-help' : 'Configuration for controller nodes',
650 'doc' : 'running-config|show-controller-node',
651 },
652 {
653 'field' : 'word',
654 'type' : 'identifier',
655 'completion' : 'complete-from-another',
656 'other' : 'controller-node|id',
657 'parent-field' : None,
658 'action' : 'legacy-cli',
659 'optional' : True,
660 }
661 ),
662)
663
664register_running_config('controller-node', 2000, None,
665 running_config_controller_node, controller_node_running_config_tuple)
666
667
668#
669# --------------------------------------------------------------------------------
670
671def running_config_switch(context, config, words):
672 """
673 Switch running-config
674 """
675
676 switches = sdnsh.get_table_from_store("switch-config")
677 flow_table_entries = sdnsh.get_table_from_store('flow-entry')
678
679 switch_interface_configs_dict = create_obj_type_dict('switch-interface-config',
680 'switch')
681
682 switch_interface_alias_dict = create_obj_type_dict('switch-interface-alias',
683 'switch-interface')
684 key = mi.pk('switch-config')
685 if len(words) > 0:
686 if not words[0] in [s[key] for s in switches]:
687 return sdnsh.error_msg('No such switch "%s"' % words[0])
688
689 for s in switches:
690 #
691 #
692 if len(words) > 0 and s[key] != words[0]:
693 continue
694
695 sw_config = []
696 sw_config.append('!\nswitch %s\n' % s[key])
697 #
698 # add any alias for this id if it exists
699 running_config_include_alias(sw_config, 1, 'switch-config', s[key])
700
701 if mi.not_default_value('switch-config', 'core-switch', s.get('core-switch','')):
702 sw_config.append(" core-switch\n") # default is not enabled
703
704 if mi.not_default_value('switch-config', 'tunnel-termination', s.get('tunnel-termination','')):
705 sw_config.append(" tunnel termination %s\n" %
706 s['tunnel-termination'])
707
708 #
709 # Look for matching switch interface config's
710 sic_key = mi.pk('switch-interface-config')
711 for sic in switch_interface_configs_dict.get(s[key], []):
712 fields = sic[sic_key].split('|')
713 if len(fields) > 1:
714 sic_config = []
715 # XXX perhaps mi.obj_type_config_fields() here?
716 running_config_include_field(sic_config,
717 'switch-interface-config',
718 'mode',
719 sic['mode'], 2,
720 'switchport ')
721
722 if sic[sic_key] in switch_interface_alias_dict:
723 sic_config.append(' interface-alias %s\n' %
724 switch_interface_alias_dict[sic[sic_key]][0]['id'])
725
726 if len(sic_config) > 0:
727 sw_config.append(' interface %s\n' % fields[1])
728 sw_config += sic_config
729
730 for fte in flow_table_entries: #
731 # how to handle defaults?
732 if fte['switch'] == s[key]:
733 sw_config.append(' flow-entry %s\n' % fte['name'])
734 fields_to_print = mi.cli_model_info.get_fields_to_print('flow-entry')
735 for f in fields_to_print:
736 v = fte.get(f, "")
737 if v != "" and f != 'name' and f != 'switch':
738 field_info = mi.cli_model_info.get_field_info('flow-entry', f)
739 if v == field_info.get('default', None) and f != 'active':
740 pass # literally, don't print this one
741 else:
742 sw_config.append(' %s %s\n' % (f, v))
743 config += sw_config
744
745switch_running_config_tuple = (
746 (
747 {
748 'optional' : False,
749 'field' : 'running-config',
750 'type' : 'enum',
751 'values' : 'switch',
752 'short-help' : 'Configuration for switches',
753 'doc' : 'running-config|show-switch',
754 },
755 {
756 'field' : 'word',
757 'type' : 'dpid',
758 'completion' : 'complete-from-another',
759 'other' : 'switch|dpid',
760 'parent-field' : None,
761 'action' : 'legacy-cli',
762 'data-handler' : 'alias-to-value',
763 'optional' : True,
764 }
765 ),
766)
767
768register_running_config('switch', 3000, None, running_config_switch, switch_running_config_tuple)
769
770
771#
772# --------------------------------------------------------------------------------
773
774def running_config_host(context, config, words):
775 """
776 Add host details, including tags.
777 """
778
779 # When there are no tags, and no host-aliases, the hosts don't need
780 # to be read. If it were possible to determine the number of entries
781 # without querying, then it would make sense to consider enumerating
782 # the host-aliases and tags if they were substantially smaller than
783 # the number of hosts.
784 #
785
786 host_alias_dict = create_obj_type_dict("host-alias", 'host')
787 host_security_ip = create_obj_type_dict("host-security-ip-address", 'host')
788 host_security_ap = create_obj_type_dict("host-security-attachment-point", 'host')
789 switch_config = create_obj_type_dict('switch-interface-config', 'id')
790
791 # Notice that only hosts configured are enumerated here, since
792 # discovered hosts can't have configuration. All configuration
793 # is attached to configured hosts, which are joined after discovered
794 # hosts for show commands.
795 hosts = sdnsh.get_table_from_store("host-config")
796 key = mi.pk('host-config')
797
798 if len(words) > 0:
799 if not words[0] in [h[key] for h in hosts]:
800 return sdnsh.error_msg('No such host "%s"' % words[0])
801
802 for h in hosts:
803 if len(words) > 0 and words[0] != h[key]:
804 continue
805
806 # host_config holds 'config' only for host.
807 host_config = []
808 a_s = ''
809 if h.get('address-space','') != 'default':
810 a_s = 'address-space %s ' % h['address-space']
811 vlan = ''
812 if h.get('vlan', '') != '':
813 vlan = 'vlan %s ' % h['vlan']
814 host_config.append('!\nhost %s%s%s\n' % (a_s, vlan, h['mac']))
815 #
816 # add the host alias for this id if it exists
817 if h[key] in host_alias_dict:
818 host_config.append(" host-alias %s\n" %
819 host_alias_dict[h[key]][0]['id'])
820
821 #
822 # check for security policies
823 if h[key] in host_security_ip:
824 for ip in host_security_ip[h[key]]:
825 host_config.append(" security policy bind ip-address %s\n" % ip['ip-address'])
826
827 if h[key] in host_security_ap:
828 for ap in host_security_ap[h[key]]:
829 host_config.append(" security policy bind attachment-point %s %s\n" %
830 (ap.get('dpid', 'all'),
831 utif.quote_string(ap['if-name-regex'])))
832
833 config += host_config
834
835
836host_running_config_tuple = (
837 (
838 {
839 'optional' : False,
840 'field' : 'running-config',
841 'type' : 'enum',
842 'values' : 'host',
843 'short-help' : 'Configuration for hosts',
844 'doc' : 'running-config|show-host',
845 },
846 {
847 'field' : 'word',
848 'type' : 'host',
849 'completion' : 'complete-from-another',
850 'other' : 'host|mac',
851 'parent-field' : None,
852 'data-handler' : 'alias-to-value',
853 'action' : 'legacy-cli',
854 'optional' : True,
855 }
856 ),
857)
858
859register_running_config('host', 5000, None, running_config_host, host_running_config_tuple)
860
861def running_config_vns(context, config, words):
862 """
863 Add the VNS configuration detils
864 this command will only generate vns under default tenant
865 other vnss will be generated under running_config_tenant
866 """
867 #generate specific vns configuration, used in tenant config generation too
868 if len(words) > 0:
869 # XXX should the vns-definiiton also be included?
870 sdnsh.show_vns_definition_running_config(config, words[0])
871 config += sdnsh.show_vns_running_config(words[0])
872 return
873
874 #generate all VNSs for default tenant, this is used backward compatible as "show running-config vns"
875 try:
876 vns_def = sdnsh.get_table_from_store('vns-definition')
877 except Exception:
878 vns_def = []
879
880 try:
881 vns_rules = create_obj_type_dict('vns-interface-rule', 'vns')
882 except Exception:
883 vns_rules = {}
884
885 try:
886 vns_acls = create_obj_type_dict('vns-access-list', 'vns')
887 except Exception:
888 vns_acls = {}
889
890 try:
891 vns_acl_entries = create_obj_type_dict('vns-access-list-entry',
892 'vns-access-list')
893 except Exception:
894 vns_acl_entries = {}
895
896 try:
897 vns_interface = create_obj_type_dict('vns-interface-config', 'vns')
898 except Exception:
899 vns_interface = {}
900
901 try:
902 vns_interface_acl = create_obj_type_dict('vns-interface-access-list',
903 'vns-interface')
904 except Exception:
905 vns_interface_acl = {}
906
907 #
908 # vns-definition
909 for vns in vns_def:
910 if vns['tenant']=='default':
911 vns_name = vns['id']
912 config.append('!\nvns-definition %s\n' % vns['vnsname'])
913 running_config_vns_details(config, vns)
914 if vns_name in vns_rules:
915 running_config_vns_if_rule(config,
916 vns_rules[vns_name],indent=1)
917 #
918 # vns
919 acl_key = mi.pk('vns-access-list')
920 if_key = mi.pk('vns-interface-config')
921
922 for vns in vns_def:
923 if vns['tenant'] =='default':
924 vns_name = vns['id']
925 vns_config = []
926
927 for acl in vns_acls.get(vns_name, []):
928 acl_id = acl[acl_key] # compound primary key value
929 running_config_vns_acl(vns_config, vns_name, acl, vns_acl_entries[acl_id],indent=1)
930
931 for vns_if in vns_interface.get(vns_name, []):
932 vns_if_acl_config = []
933 vns_if_id = vns_if[if_key] # compound primary key value
934 if vns_if_id in vns_interface_acl:
935 running_config_vns_if_and_access_group(vns_if_acl_config,
936 vns_name,
937 vns_if['interface'],
938 vns_interface_acl[vns_if_id],indent=1)
939 if vns_if_acl_config:
940 vns_config.append(" interface %s\n" % vns_if['interface'])
941 vns_config += vns_if_acl_config
942
943 if len(vns_config) > 0:
944 config.append('!\nvns %s\n' % vns['vnsname'])
945 config += vns_config
946 vns_config = []
947
948
949def is_vns_enabled(context):
950 return context.netvirt_feature_enabled()
951
952vns_running_config_tuple = (
953 (
954 {
955 'optional' : False,
956 'field' : 'running-config',
957 'type' : 'enum',
958 'values' : 'vns',
959 'short-help' : 'Configuration for network virtualization',
960 'doc' : 'running-config|show-vns',
961 },
962 {
963 'field' : 'word',
964 'type' : 'identifier',
965 'completion' : 'complete-from-another',
966 'other' : 'vns-definition|vnsname',
967 'parent-field' : None,
968 'action' : 'legacy-cli',
969 'optional' : True,
970 }
971 ),
972)
973
974register_running_config('vns', 7000, is_vns_enabled, running_config_vns, vns_running_config_tuple)
975
976#tenant running config
977def running_config_tenant(context, config, words):
978
979 if len(words) > 0:
980 # XXX should the vns-definiiton also be included?
981 sdnsh.show_tenant_running_config(config, words[0])
982 return
983
984 try:
985 tenants = sdnsh.get_table_from_store('tenant')
986 except Exception:
987 tenants = []
988
989 for tenant in tenants:
990 sdnsh.show_tenant_running_config(config, tenant['name'])
991
992
993tenant_running_config_tuple = (
994 (
995 {
996 'optional' : False,
997 'field' : 'running-config',
998 'type' : 'enum',
999 'values' : 'tenant',
1000 'short-help' : 'Configuration for Tenant',
1001 'doc' : 'running-config|show-tenant',
1002 },
1003 {
1004 'field' : 'word',
1005 'type' : 'identifier',
1006 'completion' : 'complete-from-another',
1007 'other' : 'tenant|name',
1008 'parent-field' : None,
1009 'action' : 'legacy-cli',
1010 'optional' : True,
1011 }
1012 ),
1013)
1014
1015register_running_config('tenant', 7000, is_vns_enabled, running_config_tenant, tenant_running_config_tuple)
1016
1017def running_config_static_arp(context, config, words):
1018
1019 try:
1020 staticARPs = sdnsh.get_table_from_store('static-arp')
1021 except Exception:
1022 staticARPs = []
1023 first=True
1024 for arp in staticARPs:
1025 if first:
1026 config.append('!\narp %s %s\n' % (arp['ip'], arp['mac']))
1027 first=False
1028 else:
1029 config.append('arp %s %s\n' % (arp['ip'], arp['mac']))
1030
1031staticARP_running_config_tuple = (
1032 (
1033 {
1034 'optional' : False,
1035 'field' : 'running-config',
1036 'type' : 'enum',
1037 'values' : 'static-arp',
1038 'short-help' : 'Configuration for static ARP entry',
1039 'doc' : 'running-config|show-static-arp',
1040 },
1041 ),
1042)
1043
1044register_running_config('static-arp', 6000, None, running_config_static_arp, staticARP_running_config_tuple)
1045
1046
1047#
1048# --------------------------------------------------------------------------------
1049
1050def implement_show_running_config(words):
1051 """
1052 Manager for the 'show running-config' command, which calls the
1053 specific detail functions for any of the parameters.
1054 """
1055
1056 # LOOK! hardwired - need to use the obj_type_info and the field_list
1057 # LOOK! how are these sorted?
1058 config = []
1059 if len(words) > 0:
1060 # pick the word
1061 choice = utif.full_word_from_choices(words[0],
1062 registry_items_enabled())
1063 if sdnsh.netvirt_feature_enabled() and 'vns'.startswith(words[0]):
1064 if choice:
1065 return sdnsh.error_msg("%s and %s ambiguous" % (choice, 'vns'))
1066 choice = 'vns'
1067
1068 if choice:
1069 perform_running_config(choice, sdnsh, config, words)
1070 else:
1071 return sdnsh.error_msg("unknown running-config item: %s" % words[0])
1072 # config[:-1] removes the last trailing newline
1073 return ''.join(config)[:-1]
1074 else:
1075 # Create the order based on the registration value
1076 running_config_order = sorted(registry_items_enabled(),
1077 key=lambda item: running_config_registry[item]['order'])
1078 exclude_list=['vns']
1079 for rc in running_config_order:
1080 if rc not in exclude_list:
1081 perform_running_config(rc, sdnsh, config, words)
1082
1083 prefix = []
1084 if len(config) > 0:
1085 date_string = datetime.datetime.now().strftime("%Y-%m-%d.%H:%M:%S %Z")
1086 prefix.append("!\n! ")
1087 prefix.append(sdnsh.do_show_version(words))
1088 prefix.append("\n! Current Time: ")
1089 prefix.append(date_string)
1090 prefix.append("\n!\n")
1091 prefix.append("version 1.0\n") # need a better determination of command syntax version
1092
1093 # config[:-1] removes the last trailing newline
1094 return ''.join(prefix) + ''.join(config)[:-1]
1095
1096