cisco_node_utils 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +4 -1
  6. data/CHANGELOG.md +81 -2
  7. data/CONTRIBUTING.md +2 -17
  8. data/Gemfile +5 -0
  9. data/README.md +92 -47
  10. data/Rakefile +23 -1
  11. data/bin/git/hooks/hook_lib +7 -0
  12. data/bin/git/hooks/pre-commit/check_unstaged_changes +18 -0
  13. data/bin/git/hooks/pre-commit/rubocop +7 -2
  14. data/bin/git/hooks/pre-commit/validate-diffs +18 -4
  15. data/bin/git/hooks/pre-commit/validate-yaml +18 -0
  16. data/bin/git/update-hooks +64 -6
  17. data/cisco_node_utils.gemspec +9 -6
  18. data/docs/README-develop-best-practices.md +149 -50
  19. data/docs/README-develop-node-utils-APIs.md +92 -42
  20. data/docs/README-maintainers.md +7 -4
  21. data/docs/README-test-execution.md +57 -0
  22. data/docs/cisco_node_utils.yaml.example +30 -0
  23. data/docs/template-router.rb +4 -0
  24. data/ext/mkrf_conf.rb +63 -0
  25. data/lib/.rubocop.yml +2 -2
  26. data/lib/cisco_node_utils.rb +5 -0
  27. data/lib/cisco_node_utils/aaa_authentication_login.rb +5 -6
  28. data/lib/cisco_node_utils/aaa_authorization_service.rb +1 -1
  29. data/lib/cisco_node_utils/ace.rb +165 -12
  30. data/lib/cisco_node_utils/acl.rb +2 -1
  31. data/lib/cisco_node_utils/bgp.rb +184 -21
  32. data/lib/cisco_node_utils/bgp_af.rb +94 -249
  33. data/lib/cisco_node_utils/bgp_neighbor.rb +94 -14
  34. data/lib/cisco_node_utils/bgp_neighbor_af.rb +75 -8
  35. data/lib/cisco_node_utils/bridge_domain.rb +183 -0
  36. data/lib/cisco_node_utils/bridge_domain_vni.rb +206 -0
  37. data/lib/cisco_node_utils/cisco_cmn_utils.rb +85 -2
  38. data/lib/cisco_node_utils/client.rb +35 -0
  39. data/lib/cisco_node_utils/client/client.rb +234 -0
  40. data/lib/cisco_node_utils/client/grpc.rb +33 -0
  41. data/lib/cisco_node_utils/client/grpc/client.rb +311 -0
  42. data/lib/cisco_node_utils/client/grpc/ems.proto +148 -0
  43. data/lib/cisco_node_utils/client/grpc/ems.rb +111 -0
  44. data/lib/cisco_node_utils/client/grpc/ems_services.rb +49 -0
  45. data/lib/cisco_node_utils/client/nxapi.rb +31 -0
  46. data/lib/cisco_node_utils/client/nxapi/client.rb +305 -0
  47. data/lib/cisco_node_utils/client/utils.rb +164 -0
  48. data/lib/cisco_node_utils/cmd_ref/README_YAML.md +222 -254
  49. data/lib/cisco_node_utils/cmd_ref/aaa_auth_login_service.yaml +11 -8
  50. data/lib/cisco_node_utils/cmd_ref/aaa_authentication_login.yaml +22 -15
  51. data/lib/cisco_node_utils/cmd_ref/aaa_authorization_service.yaml +11 -8
  52. data/lib/cisco_node_utils/cmd_ref/acl.yaml +21 -16
  53. data/lib/cisco_node_utils/cmd_ref/bgp.yaml +239 -109
  54. data/lib/cisco_node_utils/cmd_ref/bgp_af.yaml +114 -55
  55. data/lib/cisco_node_utils/cmd_ref/bgp_neighbor.yaml +76 -52
  56. data/lib/cisco_node_utils/cmd_ref/bgp_neighbor_af.yaml +106 -62
  57. data/lib/cisco_node_utils/cmd_ref/bridge_domain.yaml +71 -0
  58. data/lib/cisco_node_utils/cmd_ref/bridge_domain_vni.yaml +33 -0
  59. data/lib/cisco_node_utils/cmd_ref/dnsclient.yaml +35 -14
  60. data/lib/cisco_node_utils/cmd_ref/encapsulation.yaml +25 -0
  61. data/lib/cisco_node_utils/cmd_ref/evpn_vni.yaml +23 -17
  62. data/lib/cisco_node_utils/cmd_ref/fabricpath.yaml +94 -83
  63. data/lib/cisco_node_utils/cmd_ref/fabricpath_topology.yaml +22 -17
  64. data/lib/cisco_node_utils/cmd_ref/feature.yaml +76 -26
  65. data/lib/cisco_node_utils/cmd_ref/images.yaml +3 -2
  66. data/lib/cisco_node_utils/cmd_ref/interface.yaml +381 -153
  67. data/lib/cisco_node_utils/cmd_ref/interface_channel_group.yaml +21 -11
  68. data/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml +21 -21
  69. data/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml +30 -21
  70. data/lib/cisco_node_utils/cmd_ref/interface_service_vni.yaml +18 -13
  71. data/lib/cisco_node_utils/cmd_ref/inventory.yaml +26 -31
  72. data/lib/cisco_node_utils/cmd_ref/itd_device_group.yaml +83 -0
  73. data/lib/cisco_node_utils/cmd_ref/itd_service.yaml +119 -0
  74. data/lib/cisco_node_utils/cmd_ref/memory.yaml +17 -6
  75. data/lib/cisco_node_utils/cmd_ref/ntp_config.yaml +10 -3
  76. data/lib/cisco_node_utils/cmd_ref/ntp_server.yaml +17 -5
  77. data/lib/cisco_node_utils/cmd_ref/ospf.yaml +33 -29
  78. data/lib/cisco_node_utils/cmd_ref/overlay_global.yaml +12 -10
  79. data/lib/cisco_node_utils/cmd_ref/pim.yaml +16 -19
  80. data/lib/cisco_node_utils/cmd_ref/portchannel_global.yaml +40 -25
  81. data/lib/cisco_node_utils/cmd_ref/radius_global.yaml +17 -12
  82. data/lib/cisco_node_utils/cmd_ref/radius_server.yaml +71 -35
  83. data/lib/cisco_node_utils/cmd_ref/radius_server_group.yaml +10 -5
  84. data/lib/cisco_node_utils/cmd_ref/show_system.yaml +6 -2
  85. data/lib/cisco_node_utils/cmd_ref/show_version.yaml +47 -43
  86. data/lib/cisco_node_utils/cmd_ref/snmp_community.yaml +13 -11
  87. data/lib/cisco_node_utils/cmd_ref/snmp_group.yaml +4 -2
  88. data/lib/cisco_node_utils/cmd_ref/snmp_notification_receiver.yaml +23 -21
  89. data/lib/cisco_node_utils/cmd_ref/snmp_server.yaml +26 -22
  90. data/lib/cisco_node_utils/cmd_ref/snmp_user.yaml +19 -17
  91. data/lib/cisco_node_utils/cmd_ref/snmpnotification.yaml +18 -6
  92. data/lib/cisco_node_utils/cmd_ref/stp_global.yaml +234 -0
  93. data/lib/cisco_node_utils/cmd_ref/syslog_server.yaml +24 -9
  94. data/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml +5 -3
  95. data/lib/cisco_node_utils/cmd_ref/system.yaml +4 -3
  96. data/lib/cisco_node_utils/cmd_ref/tacacs_server.yaml +22 -20
  97. data/lib/cisco_node_utils/cmd_ref/tacacs_server_group.yaml +27 -15
  98. data/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml +45 -16
  99. data/lib/cisco_node_utils/cmd_ref/vdc.yaml +21 -11
  100. data/lib/cisco_node_utils/cmd_ref/virtual_service.yaml +3 -2
  101. data/lib/cisco_node_utils/cmd_ref/vlan.yaml +60 -32
  102. data/lib/cisco_node_utils/cmd_ref/vpc.yaml +118 -101
  103. data/lib/cisco_node_utils/cmd_ref/vrf.yaml +54 -58
  104. data/lib/cisco_node_utils/cmd_ref/vrf_af.yaml +118 -0
  105. data/lib/cisco_node_utils/cmd_ref/vtp.yaml +19 -25
  106. data/lib/cisco_node_utils/cmd_ref/vxlan_vtep.yaml +28 -18
  107. data/lib/cisco_node_utils/cmd_ref/vxlan_vtep_vni.yaml +34 -17
  108. data/lib/cisco_node_utils/cmd_ref/yum.yaml +6 -4
  109. data/lib/cisco_node_utils/command_reference.rb +261 -142
  110. data/lib/cisco_node_utils/constants.rb +33 -0
  111. data/lib/cisco_node_utils/encapsulation.rb +112 -0
  112. data/lib/cisco_node_utils/environment.rb +102 -0
  113. data/lib/cisco_node_utils/evpn_vni.rb +5 -3
  114. data/lib/cisco_node_utils/exceptions.rb +111 -0
  115. data/lib/cisco_node_utils/fabricpath_global.rb +52 -35
  116. data/lib/cisco_node_utils/fabricpath_topology.rb +44 -57
  117. data/lib/cisco_node_utils/feature.rb +165 -3
  118. data/lib/cisco_node_utils/interface.rb +1051 -260
  119. data/lib/cisco_node_utils/interface_channel_group.rb +11 -10
  120. data/lib/cisco_node_utils/interface_ospf.rb +1 -2
  121. data/lib/cisco_node_utils/interface_portchannel.rb +4 -12
  122. data/lib/cisco_node_utils/interface_service_vni.rb +7 -7
  123. data/lib/cisco_node_utils/itd_device_group.rb +248 -0
  124. data/lib/cisco_node_utils/itd_device_group_node.rb +144 -0
  125. data/lib/cisco_node_utils/itd_service.rb +523 -0
  126. data/lib/cisco_node_utils/logger.rb +75 -0
  127. data/lib/cisco_node_utils/node.rb +62 -192
  128. data/lib/cisco_node_utils/node_util.rb +56 -10
  129. data/lib/cisco_node_utils/overlay_global.rb +2 -2
  130. data/lib/cisco_node_utils/pim.rb +2 -13
  131. data/lib/cisco_node_utils/pim_group_list.rb +1 -1
  132. data/lib/cisco_node_utils/pim_rp_address.rb +1 -1
  133. data/lib/cisco_node_utils/platform.rb +52 -21
  134. data/lib/cisco_node_utils/portchannel_global.rb +89 -19
  135. data/lib/cisco_node_utils/radius_server.rb +168 -37
  136. data/lib/cisco_node_utils/router_ospf.rb +20 -35
  137. data/lib/cisco_node_utils/router_ospf_vrf.rb +4 -4
  138. data/lib/cisco_node_utils/snmpserver.rb +1 -6
  139. data/lib/cisco_node_utils/snmpuser.rb +6 -4
  140. data/lib/cisco_node_utils/stp_global.rb +676 -0
  141. data/lib/cisco_node_utils/syslog_server.rb +77 -18
  142. data/lib/cisco_node_utils/syslog_settings.rb +1 -1
  143. data/lib/cisco_node_utils/tacacs_server_group.rb +8 -4
  144. data/lib/cisco_node_utils/tacacs_server_host.rb +115 -25
  145. data/lib/cisco_node_utils/vdc.rb +12 -0
  146. data/lib/cisco_node_utils/version.rb +1 -1
  147. data/lib/cisco_node_utils/vlan.rb +147 -29
  148. data/lib/cisco_node_utils/vpc.rb +55 -3
  149. data/lib/cisco_node_utils/vrf.rb +72 -11
  150. data/lib/cisco_node_utils/vrf_af.rb +114 -29
  151. data/lib/cisco_node_utils/vtp.rb +34 -52
  152. data/lib/cisco_node_utils/vxlan_vtep.rb +34 -8
  153. data/lib/cisco_node_utils/vxlan_vtep_vni.rb +36 -4
  154. data/lib/minitest/environment_plugin.rb +31 -0
  155. data/lib/minitest/log_level_plugin.rb +41 -0
  156. data/spec/client_spec.rb +7 -0
  157. data/spec/environment_spec.rb +263 -0
  158. data/spec/grpc_client_spec.rb +23 -0
  159. data/spec/isolate/all_clients_spec.rb +9 -0
  160. data/spec/isolate/grpc_only_spec.rb +16 -0
  161. data/spec/isolate/no_clients_spec.rb +26 -0
  162. data/spec/isolate/nxapi_only_spec.rb +16 -0
  163. data/spec/nxapi_client_spec.rb +42 -0
  164. data/spec/schema.yaml +75 -0
  165. data/spec/shared_examples_for_clients.rb +14 -0
  166. data/spec/spec_helper.rb +91 -0
  167. data/spec/whitespace_spec.rb +10 -0
  168. data/spec/yaml_spec.rb +42 -0
  169. data/tests/.rubocop.yml +2 -2
  170. data/tests/CSCuxdublin-1.0.0-7.0.3.I3.1.lib32_n9000.rpm +0 -0
  171. data/tests/basetest.rb +96 -36
  172. data/tests/ciscotest.rb +220 -12
  173. data/tests/cmd_config.yaml +71 -49
  174. data/tests/cmd_config_invalid.yaml +1 -1
  175. data/tests/test_aaa_authentication_login.rb +1 -0
  176. data/tests/test_aaa_authentication_login_service.rb +9 -0
  177. data/tests/test_aaa_authorization_service.rb +173 -367
  178. data/tests/test_ace.rb +171 -100
  179. data/tests/test_acl.rb +10 -1
  180. data/tests/test_bgp_af.rb +395 -728
  181. data/tests/test_bgp_neighbor.rb +274 -115
  182. data/tests/test_bgp_neighbor_af.rb +178 -77
  183. data/tests/test_bridge_domain.rb +191 -0
  184. data/tests/test_bridge_domain_vni.rb +116 -0
  185. data/tests/test_client_utils.rb +111 -0
  186. data/tests/test_command_config.rb +9 -5
  187. data/tests/test_command_reference.rb +380 -102
  188. data/tests/test_dns_domain.rb +13 -3
  189. data/tests/test_domain_name.rb +13 -3
  190. data/tests/test_encapsulation.rb +77 -0
  191. data/tests/test_evpn_vni.rb +25 -7
  192. data/tests/test_fabricpath_global.rb +167 -163
  193. data/tests/test_fabricpath_topology.rb +12 -33
  194. data/tests/test_feature.rb +215 -0
  195. data/tests/test_grpc.rb +166 -0
  196. data/tests/test_interface.rb +585 -344
  197. data/tests/test_interface_bdi.rb +80 -0
  198. data/tests/test_interface_channel_group.rb +6 -3
  199. data/tests/test_interface_ospf.rb +26 -24
  200. data/tests/test_interface_portchannel.rb +1 -0
  201. data/tests/test_interface_private_vlan.rb +724 -0
  202. data/tests/test_interface_service_vni.rb +37 -66
  203. data/tests/test_interface_svi.rb +98 -101
  204. data/tests/test_interface_switchport.rb +419 -549
  205. data/tests/test_itd_device_group.rb +145 -0
  206. data/tests/test_itd_device_group_node.rb +199 -0
  207. data/tests/test_itd_service.rb +298 -0
  208. data/tests/test_logger.rb +43 -0
  209. data/tests/test_name_server.rb +11 -2
  210. data/tests/test_node.rb +16 -75
  211. data/tests/test_node_ext.rb +174 -163
  212. data/tests/test_node_util.rb +119 -0
  213. data/tests/test_ntp_config.rb +5 -1
  214. data/tests/test_ntp_server.rb +2 -2
  215. data/tests/test_nxapi.rb +221 -0
  216. data/tests/test_overlay_global.rb +47 -38
  217. data/tests/test_pim.rb +2 -0
  218. data/tests/test_pim_group_list.rb +2 -0
  219. data/tests/test_pim_rp_address.rb +2 -0
  220. data/tests/test_platform.rb +86 -39
  221. data/tests/test_portchannel_global.rb +211 -135
  222. data/tests/test_radius_global.rb +13 -5
  223. data/tests/test_radius_server.rb +256 -104
  224. data/tests/test_radius_server_group.rb +2 -0
  225. data/tests/test_router_bgp.rb +781 -485
  226. data/tests/test_router_ospf.rb +26 -103
  227. data/tests/test_router_ospf_vrf.rb +52 -57
  228. data/tests/test_snmp_notification_receiver.rb +2 -0
  229. data/tests/test_snmpcommunity.rb +2 -0
  230. data/tests/test_snmpgroup.rb +2 -0
  231. data/tests/test_snmpnotification.rb +40 -21
  232. data/tests/test_snmpserver.rb +2 -0
  233. data/tests/test_snmpuser.rb +2 -0
  234. data/tests/test_stp_global.rb +563 -0
  235. data/tests/test_syslog_server.rb +32 -8
  236. data/tests/test_syslog_settings.rb +22 -9
  237. data/tests/test_tacacs_server.rb +32 -27
  238. data/tests/test_tacacs_server_group.rb +100 -45
  239. data/tests/test_tacacs_server_host.rb +135 -43
  240. data/tests/test_vdc.rb +2 -16
  241. data/tests/test_vlan.rb +106 -54
  242. data/tests/test_vlan_mt_full.rb +11 -21
  243. data/tests/test_vlan_private.rb +669 -0
  244. data/tests/test_vpc.rb +312 -159
  245. data/tests/test_vrf.rb +122 -113
  246. data/tests/test_vrf_af.rb +238 -0
  247. data/tests/test_vtp.rb +58 -102
  248. data/tests/test_vxlan_vtep.rb +38 -17
  249. data/tests/test_vxlan_vtep_vni.rb +61 -9
  250. data/tests/test_yum.rb +49 -25
  251. metadata +122 -36
  252. data/lib/cisco_node_utils/cmd_ref/fex.yaml +0 -9
  253. data/lib/cisco_node_utils/cmd_ref/vni.yaml +0 -76
  254. data/lib/cisco_node_utils/vni.rb +0 -227
  255. data/tests/test_vni.rb +0 -106
@@ -23,6 +23,16 @@ module Cisco
23
23
  class SyslogServer < NodeUtil
24
24
  attr_reader :name, :level, :vrf
25
25
 
26
+ LEVEL_TO_NUM = { 'emergencies' => 0,
27
+ 'alerts' => 1,
28
+ 'critical' => 2,
29
+ 'error' => 3,
30
+ 'warning' => 4,
31
+ 'notifications' => 5,
32
+ 'info' => 6,
33
+ 'debugging' => 7 }.freeze
34
+ NUM_TO_LEVEL = LEVEL_TO_NUM.invert.freeze
35
+
26
36
  def initialize(name,
27
37
  level=nil,
28
38
  vrf=nil,
@@ -42,14 +52,24 @@ module Cisco
42
52
 
43
53
  def self.syslogservers
44
54
  hash = {}
45
-
46
55
  syslogservers_list = config_get('syslog_server', 'server')
47
56
  return hash if syslogservers_list.nil?
48
57
 
49
58
  syslogservers_list.each do |id|
59
+ # The YAML regex isn't specific enough for some platforms,
60
+ # so we have to do further checking.
61
+ begin
62
+ IPAddr.new(id)
63
+ rescue
64
+ next
65
+ end
66
+
50
67
  level = config_get('syslog_server', 'level', id)
68
+ level = level[0] if level.is_a?(Array)
69
+ level = LEVEL_TO_NUM[level] if platform == :ios_xr
51
70
 
52
71
  vrf = config_get('syslog_server', 'vrf', id)
72
+ vrf = vrf[0] if vrf.is_a?(Array)
53
73
 
54
74
  hash[id] = SyslogServer.new(id, level, vrf, false)
55
75
  end
@@ -62,25 +82,64 @@ module Cisco
62
82
  end
63
83
 
64
84
  def create
65
- # Set timestamp units
66
- config_set('syslog_server',
67
- 'server',
68
- state: '',
69
- ip: "#{name}",
70
- level: level.nil? ? '' : "#{level}",
71
- vrf: vrf.nil? ? '' : "use-vrf #{vrf}",
72
- )
85
+ if platform == :ios_xr
86
+
87
+ # This provider only support a 1-1 mapping between host and VRF.
88
+ # Thus, we must remove the other entries on different VRFs.
89
+ all_vrfs = config_get('syslog_server', 'vrf', name)
90
+ destroy(all_vrfs) if all_vrfs.is_a?(Array) && all_vrfs.count > 1
91
+
92
+ config_set('syslog_server',
93
+ 'server',
94
+ state: '',
95
+ ip: "#{name}",
96
+ level: level.nil? ? '' : "severity #{NUM_TO_LEVEL[level]}",
97
+ vrf: vrf.nil? ? '' : "vrf #{vrf}",
98
+ )
99
+ else
100
+ config_set('syslog_server',
101
+ 'server',
102
+ state: '',
103
+ ip: "#{name}",
104
+ level: level.nil? ? '' : "#{level}",
105
+ vrf: vrf.nil? ? '' : "use-vrf #{vrf}",
106
+ )
107
+ end
73
108
  end
74
109
 
75
- def destroy
76
- # Set timestamp units
77
- config_set('syslog_server',
78
- 'server',
79
- state: 'no',
80
- ip: "#{name}",
81
- level: '',
82
- vrf: '',
83
- )
110
+ def destroy(duplicate_vrfs=[])
111
+ if platform == :ios_xr
112
+ if duplicate_vrfs.empty?
113
+ config_set('syslog_server',
114
+ 'server',
115
+ state: 'no',
116
+ ip: "#{name}",
117
+ level: '',
118
+ vrf: vrf.nil? ? '' : "vrf #{vrf}",
119
+ )
120
+ else
121
+ warn("#{name} is configured multiple times on the device" \
122
+ ' (possibly in different VRFs). This is unsupported by this' \
123
+ ' API and the duplicate entries are being deleted.')
124
+ duplicate_vrfs.each do |dup|
125
+ config_set('syslog_server',
126
+ 'server',
127
+ state: 'no',
128
+ ip: "#{name}",
129
+ level: '',
130
+ vrf: "vrf #{dup}",
131
+ )
132
+ end
133
+ end
134
+ else
135
+ config_set('syslog_server',
136
+ 'server',
137
+ state: 'no',
138
+ ip: "#{name}",
139
+ level: '',
140
+ vrf: '',
141
+ )
142
+ end
84
143
  end
85
144
  end # class
86
145
  end # module
@@ -49,7 +49,7 @@ module Cisco
49
49
  def timestamp=(val)
50
50
  fail TypeError unless val.is_a?(String)
51
51
  fail TypeError \
52
- unless %w(seconds milliseconds).include?(timestamp)
52
+ unless %w(seconds milliseconds).include?(val)
53
53
 
54
54
  # There is no unset version as timestamp has a default value
55
55
  config_set('syslog_settings',
@@ -31,7 +31,7 @@ module Cisco
31
31
 
32
32
  return unless create
33
33
 
34
- TacacsServer.new.enable unless TacacsServer.enabled
34
+ TacacsServer.new.enable if platform != :ios_xr && !TacacsServer.enabled
35
35
  config_set('tacacs_server_group', 'group', state: '', name: name)
36
36
  end
37
37
 
@@ -81,8 +81,12 @@ module Cisco
81
81
 
82
82
  def self.groups
83
83
  grps = {}
84
- tacgroups = config_get('tacacs_server_group', 'group') if
85
- TacacsServer.enabled
84
+ if platform == :ios_xr
85
+ tacgroups = config_get('tacacs_server_group', 'group')
86
+ else
87
+ tacgroups = config_get('tacacs_server_group', 'group') if
88
+ TacacsServer.enabled
89
+ end
86
90
  unless tacgroups.nil?
87
91
  tacgroups.each { |s| grps[s] = TacacsServerGroup.new(s, false) }
88
92
  end
@@ -125,7 +129,7 @@ module Cisco
125
129
 
126
130
  def source_interface
127
131
  i = config_get('tacacs_server_group', 'source_interface', @name)
128
- i.nil? ? default_source_interface : i
132
+ i.nil? ? default_source_interface : i.downcase
129
133
  end
130
134
 
131
135
  def source_interface=(s)
@@ -23,41 +23,104 @@ module Cisco
23
23
  attr_reader :name
24
24
  @hosts = {}
25
25
 
26
- def initialize(name, create=true)
26
+ def initialize(name, instantiate=true, host_port=nil)
27
27
  fail TypeError unless name.is_a? String
28
28
  fail ArgumentError if name.empty?
29
29
  @name = name
30
- return unless create
31
- # 'feature tacacs+' must be enabled to create a host
32
- TacacsServer.new.enable unless TacacsServer.enabled
33
- config_set('tacacs_server_host', 'host', '', name)
30
+
31
+ if platform == :ios_xr
32
+ if host_port.nil?
33
+ @port = config_get_default('tacacs_server_host', 'port')
34
+ else
35
+ fail ArgumentError, 'host_port must be an Integer' \
36
+ unless host_port.is_a?(Integer)
37
+ @port = host_port
38
+ end
39
+ end
40
+
41
+ create if instantiate
42
+
43
+ return if platform == :ios_xr
44
+
45
+ return if host_port.nil?
46
+ fail ArgumentError, 'host_port must be an Integer' \
47
+ unless host_port.is_a?(Integer)
48
+ self.port = host_port
34
49
  end
35
50
 
36
51
  def self.hosts
37
- @hosts = {}
52
+ hosts = {}
53
+ return hosts unless Feature.tacacs_enabled?
38
54
 
39
- return @hosts unless TacacsServer.enabled
55
+ hosts_list = config_get('tacacs_server_host', 'hosts')
56
+ return hosts if hosts_list.nil? || hosts_list.empty?
40
57
 
41
- hosts = config_get('tacacs_server_host', 'hosts')
42
- unless hosts.nil?
43
- hosts = [hosts] if hosts.is_a?(Hash)
44
- hosts.each do |name|
45
- @hosts[name] = TacacsServerHost.new(name, false) if @hosts[name].nil?
58
+ hosts_list.each do |name|
59
+ if platform == :ios_xr
60
+ host_port = config_get('tacacs_server_host', 'port', ip: name)
61
+ host_port = host_port[0] if host_port.is_a?(Array)
62
+ host_port = host_port.to_i
63
+
64
+ hosts[name] = TacacsServerHost.new(name, false, host_port)
65
+ else
66
+ hosts[name] = TacacsServerHost.new(name, false) if @hosts[name].nil?
46
67
  end
47
68
  end
48
- @hosts
69
+ hosts
70
+ end
71
+
72
+ def create
73
+ destroy if platform == :ios_xr
74
+ Feature.tacacs_enable
75
+ config_set('tacacs_server_host',
76
+ 'host',
77
+ state: '',
78
+ ip: name,
79
+ port: @port)
49
80
  end
50
81
 
51
82
  def destroy
52
- config_set('tacacs_server_host', 'host', 'no', @name)
83
+ if platform == :ios_xr
84
+ # This provider only support a 1-1 mapping between host and ports.
85
+ # Thus, we must remove the other entries on different ports.
86
+ all_hosts = config_get('tacacs_server_host',
87
+ 'host_port_pairs',
88
+ ip: @name)
89
+ return unless all_hosts.is_a?(Array)
90
+
91
+ warn("#{name} is configured multiple times on the device" \
92
+ ' (possibly using different ports). This is unsupported by this' \
93
+ ' API and the duplicate entries are being deleted.') \
94
+ if all_hosts.count > 1
95
+
96
+ all_hosts.each do |host_port|
97
+ config_set('tacacs_server_host',
98
+ 'host',
99
+ state: 'no',
100
+ ip: @name,
101
+ port: host_port)
102
+ end
103
+ else
104
+ config_set('tacacs_server_host',
105
+ 'host',
106
+ state: 'no',
107
+ ip: @name,
108
+ port: @port)
109
+ end
53
110
  end
54
111
 
55
112
  def port
56
- config_get('tacacs_server_host', 'port', @name)
113
+ platform == :ios_xr ? @port : config_get('tacacs_server_host',
114
+ 'port',
115
+ ip: @name)
57
116
  end
58
117
 
59
118
  def port=(n)
60
- config_set('tacacs_server_host', 'port', @name, n.to_i)
119
+ fail("'port' setter method not applicable for this platform." \
120
+ 'port must be passed in to the constructor.') \
121
+ if platform == :ios_xr
122
+
123
+ config_set('tacacs_server_host', 'port', ip: @name, port: n.to_i)
61
124
  end
62
125
 
63
126
  def self.default_port
@@ -65,7 +128,10 @@ module Cisco
65
128
  end
66
129
 
67
130
  def encryption_type
68
- type = config_get('tacacs_server_host', 'encryption_type', @name)
131
+ type = config_get('tacacs_server_host',
132
+ 'encryption_type',
133
+ ip: @name,
134
+ port: @port)
69
135
  type.nil? ? TACACS_SERVER_ENC_UNKNOWN : type.to_i
70
136
  end
71
137
 
@@ -74,7 +140,10 @@ module Cisco
74
140
  end
75
141
 
76
142
  def encryption_password
77
- config_get('tacacs_server_host', 'encryption_password', @name)
143
+ config_get('tacacs_server_host',
144
+ 'encryption_password',
145
+ ip: @name,
146
+ port: @port)
78
147
  end
79
148
 
80
149
  def self.default_encryption_password
@@ -93,29 +162,50 @@ module Cisco
93
162
  # to unset the key value. Otherwise, the box is not configured with key,
94
163
  # thus we don't need to do anything
95
164
  if encryption_type != TACACS_SERVER_ENC_UNKNOWN
96
- config_set('tacacs_server_host', 'encryption', 'no', @name,
97
- encryption_type,
98
- encryption_password)
165
+ config_set('tacacs_server_host',
166
+ 'encryption',
167
+ state: 'no',
168
+ ip: @name,
169
+ port: @port,
170
+ enc_type: encryption_type,
171
+ password: encryption_password)
99
172
  end
100
173
  else
101
- config_set('tacacs_server_host', 'encryption',
102
- '', @name, enctype, password)
174
+ config_set('tacacs_server_host',
175
+ 'encryption',
176
+ state: '',
177
+ ip: @name,
178
+ port: @port,
179
+ enc_type: enctype,
180
+ password: password)
103
181
  end
104
182
  end
105
183
 
106
184
  def timeout
107
- config_get('tacacs_server_host', 'timeout', @name)
185
+ config_get('tacacs_server_host',
186
+ 'timeout',
187
+ ip: @name,
188
+ port: @port)
108
189
  end
109
190
 
110
191
  def timeout=(t)
111
192
  fail TypeError unless t.is_a? Fixnum
112
193
  return if t == timeout
113
194
 
114
- config_set('tacacs_server_host', 'timeout', '', @name, t)
195
+ config_set('tacacs_server_host',
196
+ 'timeout',
197
+ state: '',
198
+ ip: @name,
199
+ port: @port,
200
+ timeout: t)
115
201
  end
116
202
 
117
203
  def self.default_timeout
118
204
  config_get_default('tacacs_server_host', 'timeout')
119
205
  end
206
+
207
+ def ==(other)
208
+ name == other.name
209
+ end
120
210
  end
121
211
  end
@@ -84,5 +84,17 @@ module Cisco
84
84
  def default_limit_resource_module_type
85
85
  config_get_default('vdc', 'limit_resource_module_type')
86
86
  end
87
+
88
+ def interface_membership
89
+ config_get('vdc', 'membership', vdc: @vdc)
90
+ end
91
+
92
+ def interface_membership=(intf)
93
+ config_set('vdc', 'membership', vdc: @vdc, intf: intf)
94
+ end
95
+
96
+ def default_interface_membership
97
+ config_get_default('vdc', 'membership')
98
+ end
87
99
  end # Class
88
100
  end # Module
@@ -14,7 +14,7 @@
14
14
 
15
15
  # Container module for version number only.
16
16
  module CiscoNodeUtils
17
- VERSION = '1.2.0'
17
+ VERSION = '1.3.0'
18
18
  gem_version = Gem::Version.new(Gem::VERSION)
19
19
  min_gem_version = Gem::Version.new('2.1.0')
20
20
  fail 'Required rubygems version >= 2.1.0' if gem_version < min_gem_version
@@ -14,9 +14,11 @@
14
14
  # See the License for the specific language governing permissions and
15
15
  # limitations under the License.
16
16
 
17
+ require_relative 'cisco_cmn_utils'
17
18
  require_relative 'node_util'
18
19
  require_relative 'interface'
19
20
  require_relative 'fabricpath_global'
21
+ require_relative 'feature'
20
22
 
21
23
  # Add some Vlan-specific constants to the Cisco namespace
22
24
  module Cisco
@@ -24,7 +26,7 @@ module Cisco
24
26
 
25
27
  # Vlan - node utility class for VLAN configuration management
26
28
  class Vlan < NodeUtil
27
- attr_reader :name, :vlan_id
29
+ attr_reader :vlan_id
28
30
 
29
31
  def initialize(vlan_id, instantiate=true)
30
32
  @vlan_id = vlan_id.to_s
@@ -34,6 +36,10 @@ module Cisco
34
36
  create if instantiate
35
37
  end
36
38
 
39
+ def to_s
40
+ "VLAN #{vlan_id}"
41
+ end
42
+
37
43
  def self.vlans
38
44
  hash = {}
39
45
  vlan_list = config_get('vlan', 'all_vlans')
@@ -53,19 +59,49 @@ module Cisco
53
59
  config_set('vlan', 'destroy', @vlan_id)
54
60
  end
55
61
 
56
- def cli_error_check(result)
62
+ def cli_error_check(result, ignore_message=nil)
57
63
  # The NXOS vlan cli does not raise an exception in some conditions and
58
64
  # instead just displays a STDOUT error message; thus NXAPI does not detect
59
65
  # the failure and we must catch it by inspecting the "body" hash entry
60
66
  # returned by NXAPI. This vlan cli behavior is unlikely to change.
61
- fail result[2]['body'] if
62
- result[2].is_a?(Hash) &&
63
- /(ERROR:|Warning:)/.match(result[2]['body'].to_s)
67
+ # Check for messages that can be safely ignored.
68
+
69
+ errors = /(ERROR:|VLAN:|Warning:)/
70
+
71
+ return unless
72
+ result[2].is_a?(Hash) && errors.match(result[2]['body'].to_s)
73
+ # Split errors into a list, but keep the delimiter as part of the message.
74
+ error_list =
75
+ (result[2]['body'].split(errors) - ['']).each_slice(2).map(&:join)
76
+ error_list.each do |msg|
77
+ next if ignore_message && msg.to_s.include?(ignore_message)
78
+ fail result[2]['body']
79
+ end
80
+ end
81
+
82
+ def set_args_keys_default
83
+ keys = { vlan: @vlan_id }
84
+ @set_args = keys
85
+ end
86
+
87
+ def set_args_keys(hash={}) # rubocop:disable Style/AccessorMethodName
88
+ set_args_keys_default
89
+ @set_args = @set_args.merge!(hash) unless hash.empty?
90
+ end
91
+
92
+ def fabric_control
93
+ config_get('vlan', 'fabric_control', vlan: @vlan_id)
94
+ end
64
95
 
65
- # Some test environments get result[2] as a string instead of a hash
66
- fail result[2] if
67
- result[2].is_a?(String) &&
68
- /(ERROR:|Warning:)/.match(result[2])
96
+ def fabric_control=(val)
97
+ no_cmd = (val) ? '' : 'no'
98
+ result = config_set('vlan', 'fabric_control', vlan: @vlan_id,
99
+ state: no_cmd)
100
+ cli_error_check(result)
101
+ end
102
+
103
+ def default_fabric_control
104
+ config_get_default('vlan', 'fabric_control')
69
105
  end
70
106
 
71
107
  def fabricpath_feature
@@ -79,28 +115,24 @@ module Cisco
79
115
  def mode
80
116
  result = config_get('vlan', 'mode', @vlan_id)
81
117
  return default_mode if result.nil?
82
- case result
83
- when /fabricpath/i
84
- return 'fabricpath'
85
- when /ce/i
86
- return 'ce'
87
- end
118
+ # Note: The yaml definition for this property
119
+ # uses 'multiple' as a workaround for a bug
120
+ # in the N7k nxapi code which displays
121
+ # the 'show vlan' output twice.
122
+ result[0].downcase! if result[0][/FABRICPATH/]
123
+ result[0]
88
124
  end
89
125
 
90
126
  def mode=(str)
91
- str = str.to_s
92
- if str.empty?
93
- result = config_set('vlan', 'mode', @vlan_id, 'no', '')
127
+ if str == default_mode
128
+ config_set('vlan', 'mode', @vlan_id, 'no', '')
94
129
  else
95
130
  if 'fabricpath' == str
96
131
  fabricpath_feature_set(:enabled) unless
97
132
  :enabled == fabricpath_feature
98
133
  end
99
- result = config_set('vlan', 'mode', @vlan_id, '', str)
134
+ config_set('vlan', 'mode', @vlan_id, '', str)
100
135
  end
101
- cli_error_check(result)
102
- rescue CliError => e
103
- raise "[vlan #{@vlan_id}] '#{e.command}' : #{e.clierror}"
104
136
  end
105
137
 
106
138
  def default_mode
@@ -120,8 +152,6 @@ module Cisco
120
152
  result = config_set('vlan', 'name', @vlan_id, '', str)
121
153
  end
122
154
  cli_error_check(result)
123
- rescue CliError => e
124
- raise "[vlan #{@vlan_id}] '#{e.command}' : #{e.clierror}"
125
155
  end
126
156
 
127
157
  def default_vlan_name
@@ -146,8 +176,6 @@ module Cisco
146
176
  result = config_set('vlan', 'state', @vlan_id, '', str)
147
177
  end
148
178
  cli_error_check(result)
149
- rescue CliError => e
150
- raise "[vlan #{@vlan_id}] '#{e.command}' : #{e.clierror}"
151
179
  end
152
180
 
153
181
  def default_state
@@ -164,8 +192,6 @@ module Cisco
164
192
  no_cmd = (val) ? '' : 'no'
165
193
  result = config_set('vlan', 'shutdown', @vlan_id, no_cmd)
166
194
  cli_error_check(result)
167
- rescue CliError => e
168
- raise "[vlan #{@vlan_id}] '#{e.command}' : #{e.clierror}"
169
195
  end
170
196
 
171
197
  def default_shutdown
@@ -185,7 +211,7 @@ module Cisco
185
211
  interfaces = {}
186
212
  all_interfaces.each do |name, i|
187
213
  next unless i.switchport_mode == :access
188
- next unless i.access_vlan == @vlan_id
214
+ next unless i.access_vlan.to_i == @vlan_id.to_i
189
215
  interfaces[name] = i
190
216
  end
191
217
  interfaces
@@ -209,5 +235,97 @@ module Cisco
209
235
  def default_mapped_vni
210
236
  config_get_default('vlan', 'mapped_vni')
211
237
  end
238
+
239
+ def private_vlan_type
240
+ return nil unless Feature.private_vlan_enabled?
241
+ config_get('vlan', 'private_vlan_type', id: @vlan_id)
242
+ end
243
+
244
+ def private_vlan_type=(type)
245
+ Feature.private_vlan_enable
246
+ fail TypeError unless type && type.is_a?(String)
247
+
248
+ if type == default_private_vlan_type
249
+ return if private_vlan_type.empty?
250
+ set_args_keys(state: 'no', type: private_vlan_type)
251
+ ignore_msg = 'Warning: Private-VLAN CLI removed'
252
+ else
253
+ set_args_keys(state: '', type: type)
254
+ ignore_msg = 'Warning: Private-VLAN CLI entered'
255
+ end
256
+ result = config_set('vlan', 'private_vlan_type', @set_args)
257
+ cli_error_check(result, ignore_msg)
258
+ end
259
+
260
+ def default_private_vlan_type
261
+ config_get_default('vlan', 'private_vlan_type')
262
+ end
263
+
264
+ def private_vlan_association
265
+ return nil unless Feature.private_vlan_enabled?
266
+ config_get('vlan', 'private_vlan_association', id: @vlan_id)
267
+ end
268
+
269
+ def private_vlan_association=(vlan_list)
270
+ Feature.private_vlan_enable
271
+ vlan_list_delta(private_vlan_association, vlan_list)
272
+ end
273
+
274
+ def default_private_vlan_association
275
+ config_get_default('vlan', 'private_vlan_association')
276
+ end
277
+
278
+ # --------------------------
279
+ # vlan_list_delta is a helper function for the private_vlan_association
280
+ # property. It walks the delta hash and adds/removes each target private
281
+ # vlan.
282
+ # This api is used by private vlan to prepare the input to the setter
283
+ # method. The input can be in the following formats for vlans:
284
+ # 10-12,14. Prepare_array api is transforming this input into a flat array.
285
+ # In the example above the returned array will be 10, 11, 12, 14. Prepare
286
+ # array is first splitting the input on ',' and the than expanding the vlan
287
+ # range element like 10-12 into a flat array. The final result will
288
+ # be a flat array.
289
+ # This way we can later used the lib utility to check the delta from
290
+ # the input vlan value and the vlan configured to apply the right config.
291
+
292
+ def vlan_list_delta(is_list, should_list)
293
+ new_list = []
294
+ should_list.each do |item|
295
+ if item.include?(',')
296
+ new_list.push(item.split(','))
297
+ else
298
+ new_list.push(item)
299
+ end
300
+ end
301
+ new_list.flatten!
302
+ new_list.sort!
303
+
304
+ new_list.each { |item| item.gsub!('-', '..') }
305
+
306
+ should_list_new = []
307
+ new_list.each do |elem|
308
+ if elem.include?('..')
309
+ elema = elem.split('..').map { |d| Integer(d) }
310
+ elema.sort!
311
+ tr = elema[0]..elema[1]
312
+ tr.to_a.each do |item|
313
+ should_list_new.push(item.to_s)
314
+ end
315
+ else
316
+ should_list_new.push(elem)
317
+ end
318
+ end
319
+
320
+ delta_hash = Utils.delta_add_remove(should_list_new, is_list)
321
+ [:add, :remove].each do |action|
322
+ delta_hash[action].each do |vlans|
323
+ state = (action == :add) ? '' : 'no'
324
+ set_args_keys(state: state, vlans: vlans)
325
+ result = config_set('vlan', 'private_vlan_association', @set_args)
326
+ cli_error_check(result)
327
+ end
328
+ end
329
+ end
212
330
  end # class
213
331
  end # module