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
@@ -15,6 +15,7 @@
15
15
  # limitations under the License.
16
16
 
17
17
  require_relative 'node_util'
18
+ require_relative 'logger'
18
19
 
19
20
  module Cisco
20
21
  # Vtp - node utility class for VTP configuration management
@@ -26,29 +27,22 @@ module Cisco
26
27
 
27
28
  # Constructor for Vtp
28
29
  def initialize(instantiate=true)
29
- enable if instantiate && !Vtp.enabled
30
+ Feature.vtp_enable if instantiate
30
31
  end
31
32
 
32
- def self.enabled
33
- config_get('vtp', 'feature')
34
- rescue Cisco::CliError => e
35
- # cmd will syntax reject when feature is not enabled
36
- raise unless e.clierror =~ /Syntax error/
37
- return false
38
- end
39
-
40
- def enable
41
- config_set('vtp', 'feature', '')
33
+ # Get vtp domain name
34
+ def self.domain
35
+ if Feature.vtp_enabled?
36
+ config_get('vtp', 'domain')
37
+ else
38
+ config_get_default('vtp', 'domain')
39
+ end
42
40
  end
43
41
 
44
- # Disable vtp feature
42
+ # The only way to remove a vtp domain is to turn the vtp
43
+ # feature off.
45
44
  def destroy
46
- config_set('vtp', 'feature', 'no')
47
- end
48
-
49
- # Get vtp domain name
50
- def self.domain
51
- enabled ? config_get('vtp', 'domain') : ''
45
+ Feature.vtp_disable
52
46
  end
53
47
 
54
48
  def domain
@@ -57,23 +51,22 @@ module Cisco
57
51
 
58
52
  # Set vtp domain name
59
53
  def domain=(d)
60
- fail ArgumentError unless d && d.is_a?(String) &&
61
- d.length.between?(1, MAX_VTP_DOMAIN_NAME_SIZE)
62
- enable unless Vtp.enabled
63
- begin
64
- config_set('vtp', 'domain', d)
65
- rescue Cisco::CliError => e
66
- # cmd will syntax reject when setting name to same name
67
- raise unless e.clierror =~ /ERROR: Domain name already set to /
68
- end
54
+ d = d.to_s
55
+ fail ArgumentError unless d.length.between?(1, MAX_VTP_DOMAIN_NAME_SIZE)
56
+ config_set('vtp', 'domain', domain: d)
69
57
  end
70
58
 
71
59
  # Get vtp password
72
60
  def password
73
61
  # Unfortunately nxapi returns "\\" when the password is not set
74
- password = config_get('vtp', 'password') if Vtp.enabled
62
+ password = config_get('vtp', 'password') if Feature.vtp_enabled?
75
63
  return '' if password.nil? || password == '\\'
76
64
  password
65
+ rescue Cisco::RequestNotSupported => e
66
+ # Certain platforms generate a Cisco::RequestNotSupported when the
67
+ # vtp password is not set. We catch this specific error and
68
+ # return empty '' for the password.
69
+ return '' if e.message[/Structured output not supported/]
77
70
  end
78
71
 
79
72
  # Set vtp password
@@ -81,19 +74,9 @@ module Cisco
81
74
  fail TypeError if password.nil?
82
75
  fail TypeError unless password.is_a? String
83
76
  fail ArgumentError if password.length > MAX_VTP_PASSWORD_SIZE
84
- enable unless Vtp.enabled
85
- begin
86
- if password == default_password
87
- config_set('vtp', 'password', 'no', '')
88
- else
89
- config_set('vtp', 'password', '', password)
90
- end
91
- rescue Cisco::CliError => e
92
- raise unless e.clierror =~ /password cannot be set for NULL domain/
93
- unless password == default_password
94
- raise 'Setting VTP password requires first setting VTP domain'
95
- end
96
- end
77
+ Feature.vtp_enable
78
+ state = (password == default_password) ? 'no' : ''
79
+ config_set('vtp', 'password', state: state, password: password)
97
80
  end
98
81
 
99
82
  # Get default vtp password
@@ -103,18 +86,17 @@ module Cisco
103
86
 
104
87
  # Get vtp filename
105
88
  def filename
106
- config_get('vtp', 'filename')
89
+ filename = config_get('vtp', 'filename') if Feature.vtp_enabled?
90
+ filename.nil? ? default_filename : filename
107
91
  end
108
92
 
109
93
  # Set vtp filename
110
94
  def filename=(uri)
111
95
  fail TypeError if uri.nil?
112
- enable unless Vtp.enabled
113
- if uri.empty?
114
- config_set('vtp', 'filename', 'no', '')
115
- else
116
- config_set('vtp', 'filename', '', uri)
117
- end
96
+ Feature.vtp_enable
97
+ uri = uri.to_s
98
+ state = uri.empty? ? 'no' : ''
99
+ config_set('vtp', 'filename', state: state, uri: uri)
118
100
  end
119
101
 
120
102
  # Get default vtp filename
@@ -124,13 +106,13 @@ module Cisco
124
106
 
125
107
  # Get vtp version
126
108
  def version
127
- Vtp.enabled ? config_get('vtp', 'version') : default_version
109
+ Feature.vtp_enabled? ? config_get('vtp', 'version') : default_version
128
110
  end
129
111
 
130
112
  # Set vtp version
131
- def version=(version)
132
- enable unless Vtp.enabled
133
- config_set('vtp', 'version', "#{version}")
113
+ def version=(v)
114
+ Feature.vtp_enable
115
+ config_set('vtp', 'version', version: v)
134
116
  end
135
117
 
136
118
  # Get default vtp version
@@ -55,15 +55,19 @@ module Cisco
55
55
  end
56
56
 
57
57
  def create
58
+ if FabricpathGlobal.fabricpath_feature == :enabled &&
59
+ node.product_id[/N(5|6)K/]
60
+ fail 'VxLAN cannot be enabled with Fabricpath configured'
61
+ end
58
62
  Feature.nv_overlay_enable
59
63
  Feature.vn_segment_vlan_based_enable if VxlanVtep.mt_lite_support
60
64
  # re-use the "interface command ref hooks"
61
- config_set('interface', 'create', @name)
65
+ config_set('interface', 'create', name: @name)
62
66
  end
63
67
 
64
68
  def destroy
65
69
  # re-use the "interface command ref hooks"
66
- config_set('interface', 'destroy', @name)
70
+ config_set('interface', 'destroy', name: @name)
67
71
  end
68
72
 
69
73
  def ==(other)
@@ -75,16 +79,14 @@ module Cisco
75
79
  ########################################################
76
80
 
77
81
  def description
78
- config_get('interface', 'description', @name)
82
+ config_get('interface', 'description', name: @name)
79
83
  end
80
84
 
81
85
  def description=(desc)
82
86
  fail TypeError unless desc.is_a?(String)
83
- if desc.empty?
84
- config_set('interface', 'description', @name, 'no', '')
85
- else
86
- config_set('interface', 'description', @name, '', desc)
87
- end
87
+ state = desc.empty? ? 'no' : ''
88
+ config_set('interface', 'description',
89
+ name: @name, state: state, desc: desc)
88
90
  end
89
91
 
90
92
  def default_description
@@ -135,6 +137,30 @@ module Cisco
135
137
  config_get_default('vxlan_vtep', 'source_intf')
136
138
  end
137
139
 
140
+ def source_interface_hold_down_time
141
+ config_get('vxlan_vtep', 'source_intf_hold_down_time', name: @name)
142
+ end
143
+
144
+ def source_interface_hold_down_time=(time)
145
+ state = time == default_source_interface_hold_down_time ? 'no' : ''
146
+ # Cli rejects removing hold-down-time without an argument, so make
147
+ # sure it is configured before attempting to remove it
148
+ if state == 'no'
149
+ time = source_interface_hold_down_time
150
+ unless time == default_source_interface_hold_down_time
151
+ config_set('vxlan_vtep', 'source_intf_hold_down_time', name: @name,
152
+ state: state, time: time)
153
+ end
154
+ else
155
+ config_set('vxlan_vtep', 'source_intf_hold_down_time', name: @name,
156
+ state: state, time: time)
157
+ end
158
+ end
159
+
160
+ def default_source_interface_hold_down_time
161
+ config_get_default('vxlan_vtep', 'source_intf_hold_down_time')
162
+ end
163
+
138
164
  def shutdown
139
165
  config_get('vxlan_vtep', 'shutdown', name: @name)
140
166
  end
@@ -104,12 +104,18 @@ module Cisco
104
104
  # PROPERTIES #
105
105
  ########################################################
106
106
 
107
+ def ingress_replication_supported?
108
+ node.cmd_ref.supports?('vxlan_vtep_vni', 'ingress_replication')
109
+ end
110
+
107
111
  def ingress_replication
108
112
  config_get('vxlan_vtep_vni', 'ingress_replication', @get_args)
109
113
  end
110
114
 
111
115
  def remove_add_ingress_replication(protocol)
112
- if ingress_replication.empty?
116
+ # Note: ingress-replication is not supported on all platforms.
117
+ # Use to_s.empty check to also handle nil check.
118
+ if ingress_replication.to_s.empty?
113
119
  set_args_keys(state: '', protocol: protocol)
114
120
  config_set('vxlan_vtep_vni', 'ingress_replication', @set_args)
115
121
  else
@@ -171,7 +177,7 @@ module Cisco
171
177
  ip_end = '' if ip_end.nil?
172
178
  # Since multicast group and ingress replication are exclusive
173
179
  # properties, remove ingress replication first
174
- unless ingress_replication.empty?
180
+ if ingress_replication_supported? && !ingress_replication.empty?
175
181
  set_args_keys(state: 'no', protocol: ingress_replication)
176
182
  config_set('vxlan_vtep_vni', 'ingress_replication', @set_args)
177
183
  end
@@ -191,7 +197,7 @@ module Cisco
191
197
  delta_hash = Utils.delta_add_remove(should_list, peer_list)
192
198
  return if delta_hash.values.flatten.empty?
193
199
  [:add, :remove].each do |action|
194
- CiscoLogger.debug('peer_list' \
200
+ Cisco::Logger.debug('peer_list' \
195
201
  "#{@get_args}\n #{action}: #{delta_hash[action]}")
196
202
  delta_hash[action].each do |peer|
197
203
  state = (action == :add) ? '' : 'no'
@@ -214,7 +220,9 @@ module Cisco
214
220
  if state
215
221
  set_args_keys(state: '')
216
222
  # Host reachability must be enabled for this property
217
- VxlanVtep.new(@name).host_reachability = 'evpn'
223
+ unless VxlanVtep.new(@name).host_reachability == 'evpn'
224
+ fail "Dependency: vxlan_vtep host_reachability must be 'evpn'."
225
+ end
218
226
  config_set('vxlan_vtep_vni', 'suppress_arp', @set_args)
219
227
  else
220
228
  set_args_keys(state: 'no')
@@ -230,5 +238,29 @@ module Cisco
230
238
  def default_suppress_arp
231
239
  config_get_default('vxlan_vtep_vni', 'suppress_arp')
232
240
  end
241
+
242
+ def suppress_uuc
243
+ config_get('vxlan_vtep_vni', 'suppress_uuc', @get_args)
244
+ end
245
+
246
+ def suppress_uuc=(state)
247
+ if state
248
+ set_args_keys(state: '')
249
+ # Host reachability must be enabled for this property
250
+ unless VxlanVtep.new(@name).host_reachability == 'evpn'
251
+ fail "Dependency: vxlan_vtep host_reachability must be 'evpn'"
252
+ end
253
+ config_set('vxlan_vtep_vni', 'suppress_uuc', @set_args)
254
+ else
255
+ set_args_keys(state: 'no')
256
+ # Remove suppress-uuc only if it is configured. Note that for
257
+ # suppress-uuc, default is 'false' which is no suppress-uuc.
258
+ config_set('vxlan_vtep_vni', 'suppress_uuc', @set_args) if suppress_uuc
259
+ end
260
+ end
261
+
262
+ def default_suppress_uuc
263
+ config_get_default('vxlan_vtep_vni', 'suppress_uuc')
264
+ end
233
265
  end
234
266
  end
@@ -0,0 +1,31 @@
1
+ # March 2016, Glenn F. Matthews
2
+ #
3
+ # Copyright (c) 2016 Cisco and/or its affiliates.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative '../cisco_node_utils/environment'
18
+
19
+ # Add environment option to minitest
20
+ module Minitest
21
+ def self.plugin_environment_options(opts, options)
22
+ opts.on('-e', '--environment NAME', 'Select environment by name') do |name|
23
+ options[:environment] = name
24
+ end
25
+ end
26
+
27
+ def self.plugin_environment_init(options)
28
+ name = options[:environment]
29
+ Cisco::Environment.default_environment_name = name unless name.nil?
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ # March 2016, Glenn F. Matthews
2
+ #
3
+ # Copyright (c) 2016 Cisco and/or its affiliates.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'logger'
18
+ require_relative '../cisco_node_utils/logger'
19
+
20
+ # Add logging level option to minitest
21
+ module Minitest
22
+ LEVEL_ALIASES = {
23
+ 'debug' => Logger::DEBUG,
24
+ 'info' => Logger::INFO,
25
+ 'warning' => Logger::WARN,
26
+ 'error' => Logger::ERROR,
27
+ }
28
+ def self.plugin_log_level_options(opts, options)
29
+ opts.on(
30
+ '-l', '--log-level LEVEL', LEVEL_ALIASES,
31
+ 'Configure logging level for tests',
32
+ "(#{LEVEL_ALIASES.keys.join(', ')})"
33
+ ) do |level|
34
+ options[:log_level] = level
35
+ end
36
+ end
37
+
38
+ def self.plugin_log_level_init(options)
39
+ Cisco::Logger.level = options[:log_level] if options[:log_level]
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'spec_helper.rb'
2
+ require 'shared_examples_for_clients'
3
+ require 'cisco_node_utils/client'
4
+
5
+ describe Cisco::Client do
6
+ it_behaves_like 'all clients'
7
+ end
@@ -0,0 +1,263 @@
1
+ require_relative 'spec_helper.rb'
2
+ require 'cisco_node_utils/environment'
3
+ require 'cisco_node_utils/client'
4
+
5
+ class << Cisco::Environment
6
+ attr_writer :environments
7
+ end
8
+
9
+ describe Cisco::Environment do
10
+ after(:each) do
11
+ # Revert to default environment data
12
+ Cisco::Environment.environments = {}
13
+ end
14
+
15
+ describe '.data_from_file' do
16
+ it 'handles File.expand_path errors' do
17
+ expect(File).to receive(:expand_path).and_raise(ArgumentError)
18
+ expect(Cisco::Environment.data_from_file('~/foo/bar.yaml')).to be_empty
19
+ end
20
+
21
+ it 'handles nonexistent files' do
22
+ expect(File).to receive(:file?).and_return(false)
23
+ expect(Cisco::Environment.data_from_file('/foo/bar.yaml')).to be_empty
24
+ end
25
+
26
+ it 'handles unreadable files' do
27
+ expect(File).to receive(:file?).and_return(true)
28
+ expect(File).to receive(:readable?).and_return(false)
29
+ expect(Cisco::Environment.data_from_file('/foo/bar.yaml')).to be_empty
30
+ end
31
+
32
+ it 'handles YAML errors' do
33
+ expect(File).to receive(:file?).and_return(true)
34
+ expect(File).to receive(:readable?).and_return(true)
35
+ error = Psych::SyntaxError.new('/foo/bar.yaml', 1, 1, 0, 'foo', 'bar')
36
+ expect(YAML).to receive(:load_file).and_raise(error)
37
+ # Catch the error log message Environment will generate:
38
+ expect(Cisco::Logger).to receive(:error).once
39
+ expect(Cisco::Environment.data_from_file('/foo/bar.yaml')).to eq({})
40
+ end
41
+ end
42
+
43
+ describe '.merge_config' do
44
+ it 'merges valid content' do
45
+ base = { 'hello' => {
46
+ host: '2.2.2.2',
47
+ port: 57_799,
48
+ username: nil,
49
+ password: nil,
50
+ } }
51
+ expect(Cisco::Environment).to receive(:data_from_file).and_return(
52
+ 'hello' => { host: '1.1.1.1' }, 'goodbye' => { password: 'foo' })
53
+ expect(Cisco::Environment.merge_config('/foo/bar.yaml', base)).to eq(
54
+ 'hello' => {
55
+ host: '1.1.1.1',
56
+ port: 57_799,
57
+ username: nil,
58
+ password: nil,
59
+ },
60
+ 'goodbye' => {
61
+ host: nil,
62
+ port: nil,
63
+ username: nil,
64
+ password: 'foo',
65
+ },
66
+ )
67
+ end
68
+ end
69
+
70
+ describe '.environments' do
71
+ before(:each) do
72
+ allow(Cisco::Environment).to receive(:data_from_file).and_return({})
73
+ end
74
+
75
+ it 'is empty by default' do
76
+ expect(Cisco::Environment.environments).to be_empty
77
+ end
78
+
79
+ global_config = {
80
+ 'default' => {
81
+ host: '127.0.0.1',
82
+ port: 57_400,
83
+ },
84
+ 'global' => {
85
+ username: 'global',
86
+ password: 'global',
87
+ },
88
+ }
89
+
90
+ user_config = {
91
+ 'default' => {
92
+ port: 57_799,
93
+ username: 'user',
94
+ },
95
+ 'user' => {
96
+ username: 'user',
97
+ password: 'user',
98
+ },
99
+ }
100
+
101
+ it 'loads data from global config if present' do
102
+ expect(Cisco::Environment).to receive(:data_from_file).with(
103
+ '/etc/cisco_node_utils.yaml').and_return(global_config)
104
+ env = Cisco::Environment.environments
105
+ env.each do |key, hash|
106
+ # The env hash should be fully populated with keys
107
+ # Any keys unspecified in the data should be nil
108
+ %I(host port username password).each do |hash_key|
109
+ expect(hash.fetch(hash_key)).to \
110
+ eq(global_config[key].fetch(hash_key, nil))
111
+ end
112
+ end
113
+ end
114
+
115
+ it 'loads data from user config if present' do
116
+ expect(Cisco::Environment).to receive(:data_from_file).with(
117
+ '~/cisco_node_utils.yaml').and_return(user_config)
118
+ env = Cisco::Environment.environments
119
+ env.each do |key, hash|
120
+ # The env hash should be fully populated with keys
121
+ # Any keys unspecified in the data should be nil
122
+ %I(host port username password).each do |hash_key|
123
+ expect(hash.fetch(hash_key)).to \
124
+ eq(user_config[key].fetch(hash_key, nil))
125
+ end
126
+ end
127
+ end
128
+
129
+ it 'uses both files if present but user data takes precedence' do
130
+ expect(Cisco::Environment).to receive(:data_from_file).with(
131
+ '/etc/cisco_node_utils.yaml').and_return(global_config)
132
+ expect(Cisco::Environment).to receive(:data_from_file).with(
133
+ '~/cisco_node_utils.yaml').and_return(user_config)
134
+ expect(Cisco::Environment.environments).to eq(
135
+ 'default' => {
136
+ host: '127.0.0.1', # global config
137
+ port: 57_799, # user overrides global
138
+ username: 'user', # user config
139
+ password: nil, # auto-populated with nil
140
+ },
141
+ 'global' => { # global config
142
+ host: nil,
143
+ port: nil,
144
+ username: 'global',
145
+ password: 'global',
146
+ },
147
+ 'user' => { # user config
148
+ host: nil,
149
+ port: nil,
150
+ username: 'user',
151
+ password: 'user',
152
+ },
153
+ )
154
+ end
155
+ end
156
+
157
+ context '.environment' do
158
+ context 'with no config files available' do
159
+ before(:each) do
160
+ allow(Cisco::Environment).to receive(:data_from_file).and_return({})
161
+ end
162
+
163
+ it 'returns DEFAULT_ENVIRONMENT when called with no args' do
164
+ expect(Cisco::Environment.environment).to \
165
+ eq(Cisco::Environment::DEFAULT_ENVIRONMENT)
166
+ end
167
+ it 'returns DEFAULT_ENVIRONMENT when requested by name as "default"' do
168
+ expect(Cisco::Environment.environment('default')).to \
169
+ eq(Cisco::Environment::DEFAULT_ENVIRONMENT)
170
+ end
171
+ end
172
+
173
+ context 'with examples in docs/cisco_node_utils.yaml.example' do
174
+ before(:each) do
175
+ allow(File).to receive(:file?).and_return(true)
176
+ allow(File).to receive(:readable?).and_return(true)
177
+ allow(YAML).to receive(:load_file).and_wrap_original do |orig|
178
+ orig.call(File.expand_path('docs/cisco_node_utils.yaml.example'))
179
+ end
180
+ end
181
+
182
+ context 'the "nxapi_local" example' do
183
+ expected = {
184
+ host: nil,
185
+ port: nil,
186
+ username: nil,
187
+ password: nil,
188
+ }
189
+ it 'can be loaded explicitly by name' do
190
+ expect(Cisco::Environment.environment('nxapi_local')).to eq(expected)
191
+ end
192
+ it 'can be specified as the default then loaded implicitly' do
193
+ Cisco::Environment.default_environment_name = 'nxapi_local'
194
+ expect(Cisco::Environment.environment).to eq(expected)
195
+ end
196
+ it 'is valid configuration for the NXAPI client' do
197
+ hash = Cisco::Environment.environment('nxapi_local')
198
+ Cisco::Client::NXAPI.validate_args(hash)
199
+ end
200
+ end
201
+
202
+ context 'the "nxapi_remote" example' do
203
+ expected = {
204
+ host: '192.168.1.100',
205
+ port: nil,
206
+ username: 'devops',
207
+ password: 'devops',
208
+ }
209
+ it 'can be loaded explicitly by name' do
210
+ expect(Cisco::Environment.environment('nxapi_remote')).to eq(expected)
211
+ end
212
+ it 'can be specified as the default then loaded implicitly' do
213
+ Cisco::Environment.default_environment_name = 'nxapi_remote'
214
+ expect(Cisco::Environment.environment).to eq(expected)
215
+ end
216
+ it 'is valid configuration for the NXAPI client' do
217
+ hash = Cisco::Environment.environment('nxapi_remote')
218
+ Cisco::Client::NXAPI.validate_args(hash)
219
+ end
220
+ end
221
+
222
+ context 'the "grpc_local" example' do
223
+ expected = {
224
+ host: nil,
225
+ port: 57_999,
226
+ username: 'admin',
227
+ password: 'admin',
228
+ }
229
+ it 'can be loaded explicitly by name' do
230
+ expect(Cisco::Environment.environment('grpc_local')).to eq(expected)
231
+ end
232
+ it 'can be specified as default then loaded implicitly' do
233
+ Cisco::Environment.default_environment_name = 'grpc_local'
234
+ expect(Cisco::Environment.environment).to eq(expected)
235
+ end
236
+ it 'is valid configuration for the gRPC client' do
237
+ hash = Cisco::Environment.environment('grpc_local')
238
+ Cisco::Client::GRPC.validate_args(hash)
239
+ end
240
+ end
241
+
242
+ context 'the "grpc_remote" example' do
243
+ expected = {
244
+ host: '192.168.1.100',
245
+ port: nil,
246
+ username: 'admin',
247
+ password: 'admin',
248
+ }
249
+ it 'can be loaded explicitly by name' do
250
+ expect(Cisco::Environment.environment('grpc_remote')).to eq(expected)
251
+ end
252
+ it 'can be specified as default then loaded implicitly' do
253
+ Cisco::Environment.default_environment_name = 'grpc_remote'
254
+ expect(Cisco::Environment.environment).to eq(expected)
255
+ end
256
+ it 'is valid configuration for the gRPC client' do
257
+ hash = Cisco::Environment.environment('grpc_remote')
258
+ Cisco::Client::GRPC.validate_args(hash)
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end