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
@@ -26,7 +26,6 @@ module Cisco
26
26
 
27
27
  def initialize(topo_id, instantiate=true)
28
28
  @topo_id = topo_id.to_s
29
- @set_params = {}
30
29
  fail ArgumentError, "Invalid value(non-numeric
31
30
  Topo id #{@topo_id})" unless @topo_id[/^\d+$/]
32
31
 
@@ -35,8 +34,8 @@ module Cisco
35
34
 
36
35
  def self.topos
37
36
  hash = {}
38
- fabricpath = config_get('fabricpath', 'feature')
39
- return hash if (:enabled != fabricpath.to_sym)
37
+ feature = config_get('fabricpath', 'feature')
38
+ return hash if feature.nil? || feature.to_sym != :enabled
40
39
  topo_list = config_get('fabricpath_topology', 'all_topos')
41
40
  return hash if topo_list.nil?
42
41
 
@@ -44,24 +43,20 @@ module Cisco
44
43
  hash[id] = FabricpathTopo.new(id, false)
45
44
  end
46
45
  hash
46
+ rescue Cisco::CliError => e
47
+ # cmd will syntax reject when feature is not enabled
48
+ raise unless e.clierror =~ /Syntax error/
49
+ return {}
47
50
  end
48
51
 
49
52
  def create
50
53
  fabricpath_feature_set(:enabled) unless :enabled == fabricpath_feature
51
54
  config_set('fabricpath_topology', 'create',
52
- topo: @topo_id) unless @topo_id == '0'
53
- end
54
-
55
- def cli_error_check(result)
56
- # The NXOS vlan cli does not raise an exception in some conditions and
57
- # instead just displays a STDOUT error message; thus NXAPI does not detect
58
- # the failure and we must catch it by inspecting the "body" hash entry
59
- # returned by NXAPI. This vlan cli behavior is unlikely to change.
60
- fail result[2]['body'] unless result[2]['body'].empty?
55
+ topo_id: @topo_id) unless @topo_id == '0'
61
56
  end
62
57
 
63
58
  def destroy
64
- config_set('fabricpath_topology', 'destroy', topo: @topo_id)
59
+ config_set('fabricpath_topology', 'destroy', topo_id: @topo_id)
65
60
  end
66
61
 
67
62
  def fabricpath_feature
@@ -72,54 +67,50 @@ module Cisco
72
67
  FabricpathGlobal.fabricpath_feature_set(fabricpath_set)
73
68
  end
74
69
 
75
- def state
76
- result = config_get('fabricpath_topology', 'state', @topo_id)
77
- return default_state if result.nil?
78
- case result
79
- when /Up/
80
- return 'up'
81
- when /Down/
82
- return 'default'
70
+ def member_vlans
71
+ config_get('fabricpath_topology', 'member_vlans',
72
+ @topo_id).gsub(/\s+/, '')
73
+ end
74
+
75
+ def member_vlans=(str)
76
+ if str.empty?
77
+ state = 'no'
78
+ range = ''
79
+ else
80
+ state = ''
81
+ range = str
82
+ # reset existing range since we don't want incremental sets
83
+ config_set('fabricpath_topology', 'member_vlans', topo_id: @topo_id,
84
+ state: 'no', vlan_range: '') if member_vlans != ''
83
85
  end
86
+ config_set('fabricpath_topology', 'member_vlans', topo_id: @topo_id,
87
+ state: state, vlan_range: range)
84
88
  end
85
89
 
86
- def default_state
87
- config_get_default('fabricpath_topology', 'state')
90
+ def default_member_vlans
91
+ config_get_default('fabricpath_topology', 'member_vlans')
88
92
  end
89
93
 
90
- def member_vlans
91
- str = config_get('fabricpath_topology', 'member_vlans', @topo_id)
92
- return [] if str == '--'
93
- str.gsub!('-', '..')
94
- if /,/.match(str)
95
- str.split(/\s*,\s*/)
96
- else
97
- str.lines.to_a
98
- end
94
+ def member_vnis
95
+ config_get('fabricpath_topology', 'member_vnis', @topo_id).gsub(/\s+/, '')
99
96
  end
100
97
 
101
- def member_vlans=(str)
98
+ def member_vnis=(str)
102
99
  debug "str is #{str} whose class is #{str.class}"
103
- @set_params = {}
104
100
  str = str.join(',') unless str.empty?
105
101
  if str.empty?
106
- @set_params[:topo] = @topo_id
107
- @set_params[:state] = 'no'
108
- @set_params[:vlan_range] = ''
102
+ state = 'no'
103
+ range = ''
109
104
  else
110
- str.gsub!('..', '-')
111
- @set_params[:topo] = @topo_id
112
- @set_params[:state] = ''
113
- @set_params[:vlan_range] = str
105
+ state = ''
106
+ range = str
114
107
  end
115
- result = config_set('fabricpath_topology', 'member_vlans', @set_params)
116
- cli_error_check(result)
117
- rescue CliError => e
118
- raise "[topo #{@topo_id}] '#{e.command}' : #{e.clierror}"
108
+ config_set('fabricpath_topology', 'member_vnis', topo_id: @topo_id,
109
+ state: state, vni_range: range)
119
110
  end
120
111
 
121
- def default_member_vlans
122
- []
112
+ def default_member_vnis
113
+ config_get_default('fabricpath_topology', 'member_vlans')
123
114
  end
124
115
 
125
116
  def topo_name
@@ -128,19 +119,15 @@ module Cisco
128
119
 
129
120
  def topo_name=(desc)
130
121
  fail TypeError unless desc.is_a?(String)
131
- @set_params = {}
132
122
  if desc.empty?
133
- @set_params[:topo] = @topo_id
134
- @set_params[:state] = 'no'
135
- @set_params[:name] = ''
123
+ state = 'no'
124
+ name = ''
136
125
  else
137
- @set_params[:topo] = @topo_id
138
- @set_params[:state] = ''
139
- @set_params[:name] = desc
126
+ state = ''
127
+ name = desc
140
128
  end
141
- config_set('fabricpath_topology', 'description', @set_params)
142
- rescue Cisco::CliError => e
143
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
129
+ config_set('fabricpath_topology', 'description', topo_id: @topo_id,
130
+ state: state, name: name)
144
131
  end
145
132
 
146
133
  def default_topo_name
@@ -63,7 +63,7 @@ module Cisco
63
63
  begin
64
64
  config_set('feature', 'fabric_forwarding')
65
65
  rescue Cisco::CliError
66
- CiscoLogger.debug '"feature fabric forwarding" CLI was rejected'
66
+ Cisco::Logger.debug '"feature fabric forwarding" CLI was rejected'
67
67
  end
68
68
  end
69
69
 
@@ -71,11 +71,54 @@ module Cisco
71
71
  config_get('feature', 'fabric_forwarding')
72
72
  end
73
73
 
74
+ # ---------------------------
75
+ def self.fex_enable
76
+ # install feature-set and enable it
77
+ return if fex_enabled?
78
+ config_set('feature', 'fex', state: 'install') unless fex_installed?
79
+ config_set('feature', 'fex', state: '')
80
+ end
81
+
82
+ def self.fex_enabled?
83
+ config_get('feature', 'fex') =~ /^enabled/
84
+ end
85
+
86
+ def self.fex_installed?
87
+ config_get('feature', 'fex') !~ /^uninstalled/
88
+ end
89
+
90
+ def self.fex_supported?
91
+ config_get('feature', 'fex')
92
+ end
93
+
94
+ # ---------------------------
95
+ def self.itd_enable
96
+ return if itd_enabled?
97
+ config_set('feature', 'itd')
98
+ end
99
+
100
+ def self.itd_enabled?
101
+ config_get('feature', 'itd')
102
+ rescue Cisco::CliError => e
103
+ # cmd will syntax reject when feature is not enabled.
104
+ raise unless e.clierror =~ /Syntax error/
105
+ return false
106
+ end
107
+
74
108
  # ---------------------------
75
109
  def self.nv_overlay_enable
76
110
  # Note: vdc platforms restrict this feature to F3 or newer linecards
77
111
  return if nv_overlay_enabled?
78
- config_set('feature', 'nv_overlay')
112
+ config_set('feature', 'nv_overlay', state: '')
113
+ sleep 1
114
+ end
115
+
116
+ def self.nv_overlay_disable
117
+ # Note: vdc platforms restrict this feature to F3 or newer linecards
118
+ # Note: this is for test purposes only
119
+ return unless nv_overlay_enabled?
120
+ config_set('feature', 'nv_overlay', state: 'no')
121
+ sleep 1
79
122
  end
80
123
 
81
124
  def self.nv_overlay_enabled?
@@ -86,6 +129,10 @@ module Cisco
86
129
  return false
87
130
  end
88
131
 
132
+ def self.nv_overlay_supported?
133
+ node.cmd_ref.supports?('feature', 'nv_overlay')
134
+ end
135
+
89
136
  # ---------------------------
90
137
  def self.nv_overlay_evpn_enable
91
138
  return if nv_overlay_evpn_enabled?
@@ -96,10 +143,55 @@ module Cisco
96
143
  config_get('feature', 'nv_overlay_evpn')
97
144
  end
98
145
 
146
+ def self.nv_overlay_evpn_supported?
147
+ node.cmd_ref.supports?('feature', 'nv_overlay_evpn')
148
+ end
149
+
150
+ # ---------------------------
151
+ def self.ospf_enable
152
+ return if ospf_enabled?
153
+ config_set('feature', 'ospf')
154
+ end
155
+
156
+ def self.ospf_enabled?
157
+ config_get('feature', 'ospf')
158
+ end
159
+
160
+ # ---------------------------
161
+ def self.pim_enable
162
+ return if pim_enabled?
163
+ config_set('feature', 'pim')
164
+ end
165
+
166
+ def self.pim_enabled?
167
+ config_get('feature', 'pim')
168
+ end
169
+
170
+ # ---------------------------
171
+ def self.private_vlan_enable
172
+ return if private_vlan_enabled?
173
+ config_set('feature', 'private_vlan')
174
+ end
175
+
176
+ def self.private_vlan_enabled?
177
+ config_get('feature', 'private_vlan')
178
+ end
179
+
180
+ # ---------------------------
181
+ def self.tacacs_enable
182
+ return if tacacs_enabled? || platform == :ios_xr
183
+ config_set('feature', 'tacacs')
184
+ end
185
+
186
+ def self.tacacs_enabled?
187
+ config_get('feature', 'tacacs')
188
+ end
189
+
99
190
  # ---------------------------
100
191
  def self.vn_segment_vlan_based_enable
101
192
  return if vn_segment_vlan_based_enabled?
102
- config_set('feature', 'vn_segment_vlan_based')
193
+ result = config_set('feature', 'vn_segment_vlan_based')
194
+ cli_error_check(result)
103
195
  end
104
196
 
105
197
  def self.vn_segment_vlan_based_enabled?
@@ -107,5 +199,75 @@ module Cisco
107
199
  end
108
200
 
109
201
  # ---------------------------
202
+ def self.vni_enable
203
+ return if vni_enabled?
204
+ result = config_set('feature', 'vni')
205
+ cli_error_check(result)
206
+ end
207
+
208
+ def self.vni_enabled?
209
+ config_get('feature', 'vni')
210
+ end
211
+
212
+ # ---------------------------
213
+ def self.vtp_enable
214
+ return if vtp_enabled?
215
+ result = config_set('feature', 'vtp', state: '')
216
+ cli_error_check(result)
217
+ end
218
+
219
+ # Special Case: The only way to remove a vtp instance
220
+ # is by disabling the feature.
221
+ def self.vtp_disable
222
+ return unless vtp_enabled?
223
+ config_set('feature', 'vtp', state: 'no')
224
+ end
225
+
226
+ def self.vtp_enabled?
227
+ config_get('feature', 'vtp')
228
+ rescue Cisco::CliError => e
229
+ # cmd will syntax reject when feature is not enabled.
230
+ raise unless e.clierror =~ /Syntax error/
231
+ return false
232
+ end
233
+
234
+ # ---------------------------
235
+ def self.cli_error_check(result)
236
+ # The NXOS feature cli may not raise an exception in some conditions and
237
+ # instead just displays a STDOUT error message; thus NXAPI does not detect
238
+ # the failure and we must catch it by inspecting the "body" hash entry
239
+ # returned by NXAPI. This cli behavior is unlikely to change soon.
240
+ fail result[2]['body'] if
241
+ result[2].is_a?(Hash) &&
242
+ /Hardware is not capable of supporting/.match(result[2]['body'].to_s)
243
+
244
+ # Some test environments get result as a string instead of a hash
245
+ fail result if
246
+ result.is_a?(String) &&
247
+ /Hardware is not capable of supporting/.match(result)
248
+ end
249
+
250
+ # ---------------------------
251
+ def self.compatible_interfaces(feature, property='supported_module_pids')
252
+ # Figure out the interfaces in a modular switch that are
253
+ # compatible with the given feature (or property within a feature)
254
+ # and return an array of such interfaces
255
+ module_pids = config_get(feature, property)
256
+ return [] if module_pids.nil?
257
+ module_regex = Regexp.new module_pids
258
+ # first get the compatible modules present in the switch
259
+ slots = Platform.slots.select do |_slot, filt_mod|
260
+ filt_mod['pid'] =~ module_regex
261
+ end
262
+ return [] if slots.empty?
263
+ # get the slot numbers only into filtered slots array
264
+ filt_slots = slots.keys.map { |key| key[/\d+/] }
265
+ # now filter interfaces in the vdc based on compatible slots
266
+ vdc = Vdc.new(Vdc.default_vdc_name)
267
+ filt_intfs = vdc.interface_membership.select do |intf|
268
+ filt_slots.include? intf[/\d+/]
269
+ end
270
+ filt_intfs
271
+ end
110
272
  end
111
273
  end
@@ -16,145 +16,151 @@
16
16
 
17
17
  require_relative 'cisco_cmn_utils'
18
18
  require_relative 'node_util'
19
- require_relative 'pim'
20
19
  require_relative 'vrf'
21
- require_relative 'vni'
22
20
  require_relative 'overlay_global'
23
21
 
24
22
  # Add some interface-specific constants to the Cisco namespace
25
23
  module Cisco
26
24
  IF_SWITCHPORT_MODE = {
27
- disabled: '',
28
- access: 'access',
29
- trunk: 'trunk',
30
- fex_fabric: 'fex-fabric',
31
- tunnel: 'dot1q-tunnel',
32
- fabricpath: 'fabricpath',
25
+ disabled: '',
26
+ access: 'access',
27
+ trunk: 'trunk',
28
+ fex_fabric: 'fex-fabric',
29
+ tunnel: 'dot1q-tunnel',
30
+ fabricpath: 'fabricpath',
31
+ host: 'host',
32
+ promiscuous: 'promiscuous',
33
+ secondary: 'secondary',
34
+ }
35
+
36
+ PVLAN_PROPERTY = {
37
+ host_promisc: 'switchport_mode_private_vlan_host_promiscous',
38
+ allow_vlan: 'switchport_private_vlan_trunk_allowed_vlan',
39
+ trunk_assoc: 'switchport_private_vlan_association_trunk',
40
+ mapping_trunk: 'switchport_private_vlan_mapping_trunk',
41
+ vlan_mapping: 'private_vlan_mapping',
33
42
  }
34
43
 
35
44
  # Interface - node utility class for general interface config management
36
45
  class Interface < NodeUtil
46
+ # Regexp to match various Ethernet interface variants:
47
+ # Ethernet
48
+ # GigabitEthernet
49
+ # TenGigE
50
+ # HundredGigE
51
+ # MgmtEth
52
+ ETHERNET = Regexp.new('(Ethernet|GigE|MgmtEth)', Regexp::IGNORECASE)
53
+ # Regexp to match various link bundle interface variants
54
+ PORTCHANNEL = Regexp.new('(port-channel|Bundle-Ether)', Regexp::IGNORECASE)
55
+
37
56
  attr_reader :name
38
57
 
39
58
  def initialize(name, instantiate=true)
40
59
  fail TypeError unless name.is_a?(String)
41
60
  fail ArgumentError unless name.length > 0
42
61
  @name = name.downcase
43
-
62
+ @smr = config_get('interface', 'stp_mst_range')
63
+ @svr = config_get('interface', 'stp_vlan_range')
64
+ @match_found = false
44
65
  create if instantiate
45
66
  end
46
67
 
47
- def self.interfaces
68
+ def to_s
69
+ "interface #{name}"
70
+ end
71
+
72
+ def self.interfaces(opt=nil)
48
73
  hash = {}
49
74
  intf_list = config_get('interface', 'all_interfaces')
50
75
  return hash if intf_list.nil?
51
76
 
52
77
  intf_list.each do |id|
53
78
  id = id.downcase
79
+ next if opt && filter(opt, id)
54
80
  hash[id] = Interface.new(id, false)
55
81
  end
56
82
  hash
57
83
  end
58
84
 
59
- def create
60
- feature_vlan_set(true) if @name[/vlan/i]
61
- config_set('interface', 'create', @name)
62
- end
63
-
64
- def destroy
65
- config_set('interface', 'destroy', @name)
66
- end
67
-
68
- ########################################################
69
- # PROPERTIES #
70
- ########################################################
71
-
72
- def access_vlan
73
- config_get('interface', 'access_vlan', @name)
74
- end
75
-
76
- def access_vlan=(vlan)
77
- config_set('interface', 'access_vlan', @name, vlan)
78
- rescue Cisco::CliError => e
79
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
80
- end
81
-
82
- def ipv4_acl_in
83
- config_get('interface', 'ipv4_acl_in', @name)
84
- end
85
-
86
- def ipv4_acl_in=(val)
87
- if val != ''
88
- state = ''
89
- else
90
- state = 'no'
91
- val = ipv4_acl_in
85
+ # General-purpose filter for Interface.interfaces().
86
+ # opt: Identifies the filter. This may be overloaded in the future to
87
+ # allow a hash of filter conditions.
88
+ # id: The interface name
89
+ # Return: true if the interface should be filtered out, false to keep it.
90
+ def self.filter(opt, id)
91
+ case opt
92
+ when :private_vlan_any
93
+ return false if config_get('interface', 'private_vlan_any', name: id)
92
94
  end
93
-
94
- return unless val && val != ''
95
- config_set('interface', 'ipv4_acl_in', @name, state, val)
96
- end
97
-
98
- def default_ipv4_acl_in
99
- config_get_default('interface', 'ipv4_acl_in')
95
+ true
100
96
  end
101
97
 
102
- def ipv4_acl_out
103
- config_get('interface', 'ipv4_acl_out', @name)
104
- end
105
-
106
- def ipv4_acl_out=(val)
107
- if val != ''
108
- state = ''
109
- else
110
- state = 'no'
111
- val = ipv4_acl_out
98
+ # 'capabilities' is a getter-only helper for minitest and beaker.
99
+ # mode values:
100
+ # :hash = Transform the output into a hash
101
+ # :raw = The raw output from 'show int capabilities'. Some multi-line
102
+ # values do not translate easily so this option allows the
103
+ # caller to extract the data it needs.
104
+ #
105
+ # Sample cli output:
106
+ # Model: N7K-M132XP-12L
107
+ # Type (SFP capable): 10Gbase-SR
108
+ # Speed: 10,100,1000
109
+ #
110
+ # Sample hash output:
111
+ # {"Model"=>"N7K-M132XP-12L", "Type"=>"10Gbase-SR", "Speed"=>"10,100,1000"}
112
+ #
113
+ def self.capabilities(intf, mode=:hash)
114
+ array = config_get('interface', 'capabilities', name: intf)
115
+ return array if mode == :raw
116
+ hash = {}
117
+ if array
118
+ array.delete('')
119
+ array.each do |line|
120
+ k, v = line.split(':')
121
+ next if k.nil? || v.nil?
122
+ k.gsub!(/ \(.*\)/, '') # Remove any parenthetical text from key
123
+ v.strip!
124
+ hash[k] = v
125
+ end
112
126
  end
113
-
114
- return unless val && val != ''
115
- config_set('interface', 'ipv4_acl_out', @name, state, val)
116
- end
117
-
118
- def default_ipv4_acl_out
119
- config_get_default('interface', 'ipv4_acl_out')
127
+ hash
120
128
  end
121
129
 
122
- def ipv6_acl_in
123
- config_get('interface', 'ipv6_acl_in', @name)
130
+ def create
131
+ feature_vlan_set(true) if @name[/(vlan|bdi)/i]
132
+ config_set('interface', 'create', name: @name)
133
+ rescue Cisco::CliError
134
+ # Some XR platforms do not support channel-group configuration
135
+ # on some OS versions. Since this is an OS version difference and not
136
+ # a platform difference, we can't handle this in the YAML.
137
+ raise unless PORTCHANNEL =~ @name && platform == :ios_xr
138
+ raise Cisco::UnsupportedError.new('interface', @name, 'create')
124
139
  end
125
140
 
126
- def ipv6_acl_in=(val)
127
- if val != ''
128
- state = ''
129
- else
130
- state = 'no'
131
- val = ipv6_acl_in
132
- end
133
- return unless val && val != ''
134
- config_set('interface', 'ipv6_acl_in', @name, state, val)
141
+ def destroy
142
+ config_set('interface', 'destroy', name: @name)
135
143
  end
136
144
 
137
- def default_ipv6_acl_in
138
- config_get_default('interface', 'ipv6_acl_in')
139
- end
145
+ ########################################################
146
+ # PROPERTIES #
147
+ ########################################################
140
148
 
141
- def ipv6_acl_out
142
- config_get('interface', 'ipv6_acl_out', @name)
143
- end
149
+ # For range based attributes, a new attribute purge will
150
+ # be added in future. When purge is set to true, all the
151
+ # ranges which are specified in the manifest will be set
152
+ # to the desired values and those which are not specified
153
+ # in the manifest will be set to default. When purge is
154
+ # false, only the ranges specified in the manifest will
155
+ # be set to the values given in the manifest and others
156
+ # are left untouched.
144
157
 
145
- def ipv6_acl_out=(val)
146
- if val != ''
147
- state = ''
148
- else
149
- state = 'no'
150
- val = ipv6_acl_out
151
- end
152
- return unless val && val != ''
153
- config_set('interface', 'ipv6_acl_out', @name, state, val)
158
+ def access_vlan
159
+ config_get('interface', 'access_vlan', name: @name)
154
160
  end
155
161
 
156
- def default_ipv6_acl_out
157
- config_get_default('interface', 'ipv6_acl_out')
162
+ def access_vlan=(vlan)
163
+ config_set('interface', 'access_vlan', name: @name, vlan: vlan)
158
164
  end
159
165
 
160
166
  def default_access_vlan
@@ -162,18 +168,18 @@ module Cisco
162
168
  end
163
169
 
164
170
  def description
165
- config_get('interface', 'description', @name)
171
+ config_get('interface', 'description', name: @name)
166
172
  end
167
173
 
168
174
  def description=(desc)
169
175
  fail TypeError unless desc.is_a?(String)
170
176
  if desc.strip.empty?
171
- config_set('interface', 'description', @name, 'no', '')
177
+ config_set('interface', 'description',
178
+ name: @name, state: 'no', desc: '')
172
179
  else
173
- config_set('interface', 'description', @name, '', desc)
180
+ config_set('interface', 'description',
181
+ name: @name, state: '', desc: desc)
174
182
  end
175
- rescue Cisco::CliError => e
176
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
177
183
  end
178
184
 
179
185
  def default_description
@@ -181,17 +187,17 @@ module Cisco
181
187
  end
182
188
 
183
189
  def encapsulation_dot1q
184
- config_get('interface', 'encapsulation_dot1q', @name)
190
+ config_get('interface', 'encapsulation_dot1q', name: @name)
185
191
  end
186
192
 
187
193
  def encapsulation_dot1q=(val)
188
194
  if val.to_s.empty?
189
- config_set('interface', 'encapsulation_dot1q', @name, 'no', '')
195
+ config_set('interface', 'encapsulation_dot1q',
196
+ name: @name, state: 'no', vlan: '')
190
197
  else
191
- config_set('interface', 'encapsulation_dot1q', @name, '', val)
198
+ config_set('interface', 'encapsulation_dot1q',
199
+ name: @name, state: '', vlan: val)
192
200
  end
193
- rescue Cisco::CliError => e
194
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
195
201
  end
196
202
 
197
203
  def default_encapsulation_dot1q
@@ -207,59 +213,74 @@ module Cisco
207
213
  end
208
214
 
209
215
  def fabric_forwarding_anycast_gateway
210
- config_get('interface', 'fabric_forwarding_anycast_gateway', @name)
216
+ config_get('interface', 'fabric_forwarding_anycast_gateway', name: @name)
211
217
  end
212
218
 
213
219
  def fabric_forwarding_anycast_gateway=(state)
214
220
  no_cmd = (state ? '' : 'no')
215
221
  config_set('interface',
216
- 'fabric_forwarding_anycast_gateway', @name, no_cmd)
222
+ 'fabric_forwarding_anycast_gateway',
223
+ name: @name, state: no_cmd)
217
224
  fail if fabric_forwarding_anycast_gateway.to_s != state.to_s
218
225
  rescue Cisco::CliError => e
219
- info = "[#{@name}] '#{e.command}' : #{e.clierror}"
220
- raise "#{info} 'fabric_forwarding_anycast_gateway' can only be " \
226
+ raise "#{e} 'fabric_forwarding_anycast_gateway' can only be " \
221
227
  'configured on a vlan interface' unless /vlan/.match(@name)
222
228
  anycast_gateway_mac = OverlayGlobal.new.anycast_gateway_mac
223
229
  if anycast_gateway_mac.nil? || anycast_gateway_mac.empty?
224
- raise "#{info} Anycast gateway mac must be configured " \
230
+ raise "#{e} Anycast gateway mac must be configured " \
225
231
  'before configuring forwarding mode under interface'
226
232
  end
227
- raise info
233
+ raise
228
234
  end
229
235
 
230
236
  def default_fabric_forwarding_anycast_gateway
231
237
  config_get_default('interface', 'fabric_forwarding_anycast_gateway')
232
238
  end
233
239
 
234
- def fex_feature
235
- fex = config_get('fex', 'feature')
236
- fail 'fex_feature not found' if fex.nil?
237
- fex.to_sym
240
+ def ipv4_acl_in
241
+ config_get('interface', 'ipv4_acl_in', name: @name)
238
242
  end
239
243
 
240
- def fex_feature_set(fex_set)
241
- curr = fex_feature
242
- return if curr == fex_set
244
+ def ipv4_acl_in=(val)
245
+ if val != ''
246
+ state = ''
247
+ else
248
+ state = 'no'
249
+ val = ipv4_acl_in
250
+ end
243
251
 
244
- case fex_set
245
- when :enabled
246
- config_set('fex', 'feature_install', '') if curr == :uninstalled
247
- config_set('fex', 'feature', '')
248
- when :disabled
249
- config_set('fex', 'feature', 'no') if curr == :enabled
250
- return
251
- when :installed
252
- config_set('fex', 'feature_install', '') if curr == :uninstalled
253
- when :uninstalled
254
- config_set('fex', 'feature', 'no') if curr == :enabled
255
- config_set('fex', 'feature_install', 'no')
252
+ return unless val && val != ''
253
+ config_set('interface', 'ipv4_acl_in',
254
+ name: @name, state: state, acl: val)
255
+ end
256
+
257
+ def default_ipv4_acl_in
258
+ config_get_default('interface', 'ipv4_acl_in')
259
+ end
260
+
261
+ def ipv4_acl_out
262
+ config_get('interface', 'ipv4_acl_out', name: @name)
263
+ end
264
+
265
+ def ipv4_acl_out=(val)
266
+ if val != ''
267
+ state = ''
268
+ else
269
+ state = 'no'
270
+ val = ipv4_acl_out
256
271
  end
257
- rescue Cisco::CliError => e
258
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
272
+
273
+ return unless val && val != ''
274
+ config_set('interface', 'ipv4_acl_out',
275
+ name: @name, state: state, acl: val)
276
+ end
277
+
278
+ def default_ipv4_acl_out
279
+ config_get_default('interface', 'ipv4_acl_out')
259
280
  end
260
281
 
261
282
  def ipv4_addr_mask_set(addr, mask, secondary=false)
262
- check_switchport_disabled
283
+ check_switchport(:disabled)
263
284
  sec = secondary ? 'secondary' : ''
264
285
  if addr.nil? || addr == default_ipv4_address
265
286
  state = 'no'
@@ -275,13 +296,22 @@ module Cisco
275
296
  state = ''
276
297
  am = "#{addr}/#{mask}"
277
298
  end
278
- config_set('interface', 'ipv4_addr_mask', @name, state, am, sec)
279
- rescue Cisco::CliError => e
280
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
299
+ config_set('interface', 'ipv4_addr_mask',
300
+ name: @name, state: state, addr: am, secondary: sec)
281
301
  end
282
302
 
283
303
  def ipv4_addr_mask
284
- config_get('interface', 'ipv4_addr_mask', @name)
304
+ val = config_get('interface', 'ipv4_addr_mask', name: @name)
305
+ if val && platform == :ios_xr
306
+ # IOS XR reports address as <address> <bitmask> [secondary] but we
307
+ # want <address>/<length> [secondary]
308
+ val.each_with_index do |entry, i|
309
+ mask = entry[1].split(' ')
310
+ mask[0] = Utils.bitmask_to_length(mask[0])
311
+ val[i][1] = mask.join(' ')
312
+ end
313
+ end
314
+ val
285
315
  end
286
316
 
287
317
  def select_ipv4_attribute(attribute)
@@ -346,31 +376,43 @@ module Cisco
346
376
  end
347
377
 
348
378
  def ipv4_arp_timeout
349
- config_get('interface', ipv4_arp_timeout_lookup_string, @name)
379
+ config_get('interface', ipv4_arp_timeout_lookup_string, name: @name)
350
380
  end
351
381
 
352
382
  def ipv4_arp_timeout=(timeout)
353
383
  fail "'ipv4 arp timeout' can ony be configured on a vlan interface" unless
354
384
  /vlan/.match(@name)
355
385
  state = (timeout == default_ipv4_arp_timeout) ? 'no' : ''
356
- config_set('interface', 'ipv4_arp_timeout', @name, state, timeout)
386
+ config_set('interface', 'ipv4_arp_timeout',
387
+ name: @name, state: state, timeout: timeout)
357
388
  end
358
389
 
359
390
  def default_ipv4_arp_timeout
360
391
  config_get_default('interface', ipv4_arp_timeout_lookup_string)
361
392
  end
362
393
 
394
+ def ipv4_forwarding
395
+ config_get('interface', 'ipv4_forwarding', name: @name)
396
+ end
397
+
398
+ def ipv4_forwarding=(state)
399
+ config_set('interface', 'ipv4_forwarding',
400
+ name: @name, state: state ? '' : 'no')
401
+ end
402
+
403
+ def default_ipv4_forwarding
404
+ config_get_default('interface', 'ipv4_forwarding')
405
+ end
406
+
363
407
  def ipv4_pim_sparse_mode
364
- config_get('interface', 'ipv4_pim_sparse_mode', @name)
408
+ config_get('interface', 'ipv4_pim_sparse_mode', name: @name)
365
409
  end
366
410
 
367
411
  def ipv4_pim_sparse_mode=(state)
368
- check_switchport_disabled
369
- Pim.feature_enable unless Pim.feature_enabled
370
- config_set('interface', 'ipv4_pim_sparse_mode', @name,
371
- state ? '' : 'no')
372
- rescue Cisco::CliError => e
373
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
412
+ check_switchport(:disabled)
413
+ Feature.pim_enable unless platform == :ios_xr
414
+ config_set('interface', 'ipv4_pim_sparse_mode',
415
+ name: @name, state: state ? '' : 'no')
374
416
  end
375
417
 
376
418
  def default_ipv4_pim_sparse_mode
@@ -378,13 +420,13 @@ module Cisco
378
420
  end
379
421
 
380
422
  def ipv4_proxy_arp
381
- config_get('interface', 'ipv4_proxy_arp', @name)
423
+ config_get('interface', 'ipv4_proxy_arp', name: @name)
382
424
  end
383
425
 
384
426
  def ipv4_proxy_arp=(proxy_arp)
385
- check_switchport_disabled
427
+ check_switchport(:disabled)
386
428
  no_cmd = (proxy_arp ? '' : 'no')
387
- config_set('interface', 'ipv4_proxy_arp', @name, no_cmd)
429
+ config_set('interface', 'ipv4_proxy_arp', name: @name, state: no_cmd)
388
430
  end
389
431
 
390
432
  def default_ipv4_proxy_arp
@@ -401,54 +443,98 @@ module Cisco
401
443
  end
402
444
 
403
445
  def ipv4_redirects
404
- config_get('interface', ipv4_redirects_lookup_string, @name)
446
+ config_get('interface', ipv4_redirects_lookup_string, name: @name)
405
447
  end
406
448
 
407
449
  def ipv4_redirects=(redirects)
408
- check_switchport_disabled
450
+ check_switchport(:disabled)
409
451
  no_cmd = (redirects ? '' : 'no')
410
- config_set('interface', ipv4_redirects_lookup_string, @name, no_cmd)
452
+ config_set('interface', ipv4_redirects_lookup_string,
453
+ name: @name, state: no_cmd)
411
454
  end
412
455
 
413
456
  def default_ipv4_redirects
414
457
  config_get_default('interface', ipv4_redirects_lookup_string)
415
458
  end
416
459
 
460
+ def ipv6_acl_in
461
+ config_get('interface', 'ipv6_acl_in', name: @name)
462
+ end
463
+
464
+ def ipv6_acl_in=(val)
465
+ if val != ''
466
+ state = ''
467
+ else
468
+ state = 'no'
469
+ val = ipv6_acl_in
470
+ end
471
+ return unless val && val != ''
472
+ config_set('interface', 'ipv6_acl_in',
473
+ name: @name, state: state, acl: val)
474
+ end
475
+
476
+ def default_ipv6_acl_in
477
+ config_get_default('interface', 'ipv6_acl_in')
478
+ end
479
+
480
+ def ipv6_acl_out
481
+ config_get('interface', 'ipv6_acl_out', name: @name)
482
+ end
483
+
484
+ def ipv6_acl_out=(val)
485
+ if val != ''
486
+ state = ''
487
+ else
488
+ state = 'no'
489
+ val = ipv6_acl_out
490
+ end
491
+ return unless val && val != ''
492
+ config_set('interface', 'ipv6_acl_out',
493
+ name: @name, state: state, acl: val)
494
+ end
495
+
496
+ def default_ipv6_acl_out
497
+ config_get_default('interface', 'ipv6_acl_out')
498
+ end
499
+
417
500
  def feature_lacp?
418
501
  config_get('interface', 'feature_lacp')
419
502
  end
420
503
 
421
504
  def feature_lacp_set(val)
422
505
  return if feature_lacp? == val
423
- config_set('interface', 'feature_lacp', val ? '' : 'no')
506
+ config_set('interface', 'feature_lacp', state: val ? '' : 'no')
507
+ end
508
+
509
+ def mtu_lookup_string
510
+ case @name
511
+ when /loopback/i
512
+ return 'mtu_loopback'
513
+ else
514
+ return 'mtu_other_interfaces'
515
+ end
424
516
  end
425
517
 
426
518
  def mtu
427
- config_get('interface', 'mtu', @name)
519
+ config_get('interface', mtu_lookup_string, name: @name)
428
520
  end
429
521
 
430
522
  def mtu=(val)
431
- check_switchport_disabled
432
- config_set('interface', 'mtu', @name, '', val)
433
- rescue Cisco::CliError => e
434
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
523
+ check_switchport(:disabled)
524
+ config_set('interface', mtu_lookup_string,
525
+ name: @name, state: '', mtu: val)
435
526
  end
436
527
 
437
528
  def default_mtu
438
- config_get_default('interface', 'mtu')
529
+ config_get_default('interface', mtu_lookup_string)
439
530
  end
440
531
 
441
532
  def speed
442
- config_get('interface', 'speed', @name)
533
+ config_get('interface', 'speed', name: @name)
443
534
  end
444
535
 
445
536
  def speed=(val)
446
- if node.product_id =~ /C31\d\d/
447
- fail 'Changing interface speed is not permitted on this platform'
448
- end
449
- config_set('interface', 'speed', @name, val)
450
- rescue Cisco::CliError => e
451
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
537
+ config_set('interface', 'speed', name: @name, speed: val)
452
538
  end
453
539
 
454
540
  def default_speed
@@ -456,16 +542,11 @@ module Cisco
456
542
  end
457
543
 
458
544
  def duplex
459
- config_get('interface', 'duplex', @name)
545
+ config_get('interface', 'duplex', name: @name)
460
546
  end
461
547
 
462
548
  def duplex=(val)
463
- if node.product_id =~ /C31\d\d/
464
- fail 'Changing interface duplex is not permitted on this platform'
465
- end
466
- config_set('interface', 'duplex', @name, val)
467
- rescue Cisco::CliError => e
468
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
549
+ config_set('interface', 'duplex', name: @name, duplex: val)
469
550
  end
470
551
 
471
552
  def default_duplex
@@ -474,9 +555,9 @@ module Cisco
474
555
 
475
556
  def negotiate_auto_lookup_string
476
557
  case @name
477
- when /Ethernet/i
558
+ when ETHERNET
478
559
  return 'negotiate_auto_ethernet'
479
- when /port-channel/i # Ether-channel
560
+ when PORTCHANNEL
480
561
  return 'negotiate_auto_portchannel'
481
562
  else
482
563
  return 'negotiate_auto_other_interfaces'
@@ -484,13 +565,13 @@ module Cisco
484
565
  end
485
566
 
486
567
  def negotiate_auto
487
- config_get('interface', negotiate_auto_lookup_string, @name)
568
+ config_get('interface', negotiate_auto_lookup_string, name: @name)
488
569
  end
489
570
 
490
571
  def negotiate_auto=(negotiate_auto)
491
572
  lookup = negotiate_auto_lookup_string
492
573
  no_cmd = (negotiate_auto ? '' : 'no')
493
- config_set('interface', lookup, @name, no_cmd)
574
+ config_set('interface', lookup, name: @name, state: no_cmd)
494
575
  end
495
576
 
496
577
  def default_negotiate_auto
@@ -498,19 +579,17 @@ module Cisco
498
579
  end
499
580
 
500
581
  def shutdown
501
- config_get('interface', 'shutdown', @name)
582
+ config_get('interface', 'shutdown', name: @name)
502
583
  end
503
584
 
504
585
  def shutdown=(state)
505
586
  no_cmd = (state ? '' : 'no')
506
- config_set('interface', 'shutdown', @name, no_cmd)
507
- rescue Cisco::CliError => e
508
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
587
+ config_set('interface', 'shutdown', name: @name, state: no_cmd)
509
588
  end
510
589
 
511
590
  def default_shutdown
512
591
  case @name
513
- when /Ethernet/i
592
+ when ETHERNET
514
593
  def_sw = system_default_switchport
515
594
  def_shut = system_default_switchport_shutdown
516
595
 
@@ -529,7 +608,7 @@ module Cisco
529
608
  when /loopback/i
530
609
  lookup = 'shutdown_loopback'
531
610
 
532
- when /port-channel/i # EtherChannel
611
+ when PORTCHANNEL
533
612
  lookup = 'shutdown_ether_channel'
534
613
 
535
614
  when /Vlan/i
@@ -541,28 +620,236 @@ module Cisco
541
620
  config_get_default('interface', lookup)
542
621
  end
543
622
 
623
+ def stp_bpdufilter
624
+ config_get('interface', 'stp_bpdufilter', name: @name)
625
+ end
626
+
627
+ def stp_bpdufilter=(val)
628
+ check_switchport([:access, :trunk])
629
+ if val
630
+ state = ''
631
+ else
632
+ state = 'no'
633
+ val = ''
634
+ end
635
+ config_set('interface',
636
+ 'stp_bpdufilter', name: @name, state: state, filter: val)
637
+ end
638
+
639
+ def default_stp_bpdufilter
640
+ config_get_default('interface', 'stp_bpdufilter')
641
+ end
642
+
643
+ def stp_bpduguard
644
+ config_get('interface', 'stp_bpduguard', name: @name)
645
+ end
646
+
647
+ def stp_bpduguard=(val)
648
+ if val
649
+ state = ''
650
+ else
651
+ state = 'no'
652
+ val = ''
653
+ end
654
+ config_set('interface',
655
+ 'stp_bpduguard', name: @name, state: state, guard: val)
656
+ end
657
+
658
+ def default_stp_bpduguard
659
+ config_get_default('interface', 'stp_bpduguard')
660
+ end
661
+
662
+ def stp_cost
663
+ cost = config_get('interface', 'stp_cost', name: @name)
664
+ cost == 'auto' ? cost : cost.to_i
665
+ end
666
+
667
+ def stp_cost=(val)
668
+ check_switchport([:access, :trunk])
669
+ config_set('interface', 'stp_cost', name: @name, cost: val)
670
+ end
671
+
672
+ def default_stp_cost
673
+ config_get_default('interface', 'stp_cost')
674
+ end
675
+
676
+ def stp_guard
677
+ config_get('interface', 'stp_guard', name: @name)
678
+ end
679
+
680
+ def stp_guard=(val)
681
+ check_switchport([:access, :trunk])
682
+ if val
683
+ state = ''
684
+ else
685
+ state = 'no'
686
+ val = ''
687
+ end
688
+ config_set('interface', 'stp_guard', name: @name, state: state,
689
+ guard: val)
690
+ end
691
+
692
+ def default_stp_guard
693
+ config_get_default('interface', 'stp_guard')
694
+ end
695
+
696
+ def stp_link_type
697
+ config_get('interface', 'stp_link_type', name: @name)
698
+ end
699
+
700
+ def stp_link_type=(val)
701
+ check_switchport([:access, :trunk])
702
+ config_set('interface', 'stp_link_type', name: @name, type: val)
703
+ end
704
+
705
+ def default_stp_link_type
706
+ config_get_default('interface', 'stp_link_type')
707
+ end
708
+
709
+ def stp_port_priority
710
+ config_get('interface', 'stp_port_priority', name: @name)
711
+ end
712
+
713
+ def stp_port_priority=(val)
714
+ check_switchport([:access, :trunk])
715
+ config_set('interface', 'stp_port_priority', name: @name, pp: val)
716
+ end
717
+
718
+ def default_stp_port_priority
719
+ config_get_default('interface', 'stp_port_priority')
720
+ end
721
+
722
+ # Getter: Builds an array of mst cost commands currently
723
+ # on the device.
724
+ # cli: spanning-tree mst 0,2-4,6,8-12 cost 1000
725
+ # spanning-tree mst 4000-4020 cost 2568
726
+ # array: [['0,2-4,6,8-12', '1000'], ['4000-4020', '2568']]
727
+ #
728
+ def stp_mst_cost
729
+ config_get('interface', 'stp_mst_cost', name: @name)
730
+ end
731
+
732
+ def stp_mst_cost=(list)
733
+ check_switchport([:access, :trunk])
734
+ config_set('interface', 'stp_mst_cost',
735
+ name: @name, state: 'no', range: @smr,
736
+ val: '') if list.empty?
737
+ set_range_based_params(list, 'stp_mst_cost')
738
+ end
739
+
740
+ def default_stp_mst_cost
741
+ config_get_default('interface', 'stp_mst_cost')
742
+ end
743
+
744
+ # Getter: Builds an array of mst port-priority commands
745
+ # currently on the device.
746
+ # cli: spanning-tree mst 0,2-4,6,8-12 port-priority 64
747
+ # spanning-tree mst 4000-4020 port-priority 160
748
+ # array: [['0,2-4,6,8-12', '64'], ['4000-4020', '160']]
749
+ #
750
+ def stp_mst_port_priority
751
+ config_get('interface', 'stp_mst_port_priority', name: @name)
752
+ end
753
+
754
+ def stp_mst_port_priority=(list)
755
+ check_switchport([:access, :trunk])
756
+ config_set('interface', 'stp_mst_port_priority',
757
+ name: @name, state: 'no', range: @smr,
758
+ val: '') if list.empty?
759
+ set_range_based_params(list, 'stp_mst_port_priority')
760
+ end
761
+
762
+ def default_stp_mst_port_priority
763
+ config_get_default('interface', 'stp_mst_port_priority')
764
+ end
765
+
766
+ def stp_port_type
767
+ config_get('interface', 'stp_port_type', name: @name)
768
+ end
769
+
770
+ def stp_port_type=(val)
771
+ if val
772
+ state = ''
773
+ else
774
+ state = 'no'
775
+ val = ''
776
+ end
777
+ config_set('interface', 'stp_port_type', name: @name,
778
+ state: state, type: val)
779
+ end
780
+
781
+ def default_stp_port_type
782
+ config_get_default('interface', 'stp_port_type')
783
+ end
784
+
785
+ # Getter: Builds an array of vlan cost commands currently
786
+ # on the device.
787
+ # cli: spanning-tree vlan 1-4,6,8-12 cost 1000
788
+ # spanning-tree vlan 3000-3960 cost 2568
789
+ # array: [['1-4,6,8-12', '1000'], ['3000-3960', '2568']]
790
+ #
791
+ def stp_vlan_cost
792
+ config_get('interface', 'stp_vlan_cost', name: @name)
793
+ end
794
+
795
+ def stp_vlan_cost=(list)
796
+ check_switchport([:access, :trunk])
797
+ config_set('interface', 'stp_vlan_cost',
798
+ name: @name, state: 'no',
799
+ range: @svr, val: '') if list.empty?
800
+ set_range_based_params(list, 'stp_vlan_cost')
801
+ end
802
+
803
+ def default_stp_vlan_cost
804
+ config_get_default('interface', 'stp_vlan_cost')
805
+ end
806
+
807
+ # Getter: Builds an array of vlan port-priority commands
808
+ # currently on the device.
809
+ # cli: spanning-tree vlan 1-4,6,8-12 port-priority 64
810
+ # spanning-tree vlan 3000-3960 port-priority 160
811
+ # array: [['1-4,6,8-12', '64'], ['3000-3960', '160']]
812
+ #
813
+ def stp_vlan_port_priority
814
+ config_get('interface', 'stp_vlan_port_priority', name: @name)
815
+ end
816
+
817
+ def stp_vlan_port_priority=(list)
818
+ check_switchport([:access, :trunk])
819
+ config_set('interface', 'stp_vlan_port_priority',
820
+ name: @name, state: 'no',
821
+ range: @svr, val: '') if list.empty?
822
+ set_range_based_params(list, 'stp_vlan_port_priority')
823
+ end
824
+
825
+ def default_stp_vlan_port_priority
826
+ config_get_default('interface', 'stp_vlan_port_priority')
827
+ end
828
+
544
829
  def switchport
545
830
  # This is "switchport", not "switchport mode"
546
- config_get('interface', 'switchport', @name)
831
+ config_get('interface', 'switchport', name: @name)
547
832
  end
548
833
 
549
834
  def switchport_enable(val=true)
550
- config_set('interface', 'switchport', @name, val ? '' : 'no')
835
+ config_set('interface', 'switchport', name: @name, state: val ? '' : 'no')
551
836
  end
552
837
 
553
838
  # switchport_autostate_exclude is exclusive to switchport interfaces
554
839
  def switchport_autostate_exclude
555
840
  config_get('interface',
556
- 'switchport_autostate_exclude', @name)
841
+ 'switchport_autostate_exclude', name: @name)
557
842
  end
558
843
 
559
844
  def switchport_autostate_exclude=(val)
560
- # cannot configure autostate unless feature vlan is enabled
561
- fail('switchport mode must be configured before ' \
562
- 'switchport autostate') unless switchport
563
- feature_vlan_set(true)
845
+ if platform == :nexus
846
+ # cannot configure autostate unless feature vlan is enabled
847
+ fail('switchport mode must be configured before ' \
848
+ 'switchport autostate') unless switchport
849
+ feature_vlan_set(true)
850
+ end
564
851
  config_set('interface', 'switchport_autostate_exclude',
565
- @name, val ? '' : 'no')
852
+ name: @name, state: val ? '' : 'no')
566
853
  end
567
854
 
568
855
  def default_switchport_autostate_exclude
@@ -571,9 +858,9 @@ module Cisco
571
858
 
572
859
  def switchport_mode_lookup_string
573
860
  case @name
574
- when /Ethernet/i
861
+ when ETHERNET
575
862
  return 'switchport_mode_ethernet'
576
- when /port-channel/i
863
+ when PORTCHANNEL
577
864
  return 'switchport_mode_port_channel'
578
865
  else
579
866
  return 'switchport_mode_other_interfaces'
@@ -581,7 +868,8 @@ module Cisco
581
868
  end
582
869
 
583
870
  def switchport_mode
584
- mode = config_get('interface', switchport_mode_lookup_string, @name)
871
+ return nil if platform == :ios_xr
872
+ mode = config_get('interface', switchport_mode_lookup_string, name: @name)
585
873
 
586
874
  return mode.nil? ? :disabled : IF_SWITCHPORT_MODE.key(mode)
587
875
 
@@ -589,6 +877,7 @@ module Cisco
589
877
  # Assume this is an interface that doesn't support switchport.
590
878
  # Do not raise exception since the providers will prefetch this property
591
879
  # regardless of interface type.
880
+ # TODO: this should probably be nil instead
592
881
  return :disabled
593
882
  end
594
883
 
@@ -598,13 +887,10 @@ module Cisco
598
887
  if :fabricpath == mode_set
599
888
  fabricpath_feature_set(:enabled) unless :enabled == fabricpath_feature
600
889
  elsif :fex_fabric == mode_set
601
- fex_feature_set(:enabled) unless :enabled == fex_feature
890
+ Feature.fex_enable
602
891
  end
603
- config_set('interface', switchport_mode_lookup_string, @name, '',
604
- IF_SWITCHPORT_MODE[mode_set])
605
-
606
- rescue RuntimeError
607
- raise "[#{@name}] switchport_mode is not supported on this interface"
892
+ config_set('interface', switchport_mode_lookup_string,
893
+ name: @name, state: '', mode: IF_SWITCHPORT_MODE[mode_set])
608
894
  end
609
895
 
610
896
  def switchport_mode=(mode_set)
@@ -617,13 +903,13 @@ module Cisco
617
903
  when :disabled
618
904
  if switchport
619
905
  # Note: turn off switchport command, not switchport mode
620
- config_set('interface', 'switchport', @name, 'no')
906
+ config_set('interface', 'switchport', name: @name, state: 'no')
621
907
  end
622
908
 
623
909
  when :default
624
910
  if :disabled == default_switchport_mode
625
911
  config_set('interface', switchport_mode_lookup_string,
626
- @name, 'no', '')
912
+ name: @name, state: 'no', mode: '')
627
913
  else
628
914
  switchport_enable_and_mode(mode_set)
629
915
  end
@@ -631,31 +917,27 @@ module Cisco
631
917
  else
632
918
  switchport_enable_and_mode(mode_set)
633
919
  end # case
634
-
635
- rescue Cisco::CliError => e
636
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
637
920
  end
638
921
 
639
922
  def default_switchport_mode
923
+ return nil if platform == :ios_xr
640
924
  return :disabled unless system_default_switchport
641
925
  IF_SWITCHPORT_MODE.key(
642
926
  config_get_default('interface', switchport_mode_lookup_string))
643
927
  end
644
928
 
645
929
  def switchport_trunk_allowed_vlan
646
- config_get('interface', 'switchport_trunk_allowed_vlan', @name)
930
+ config_get('interface', 'switchport_trunk_allowed_vlan', name: @name)
647
931
  end
648
932
 
649
933
  def switchport_trunk_allowed_vlan=(val)
650
934
  if val.nil?
651
- config_set(
652
- 'interface', 'switchport_trunk_allowed_vlan', @name, 'no', '')
935
+ config_set('interface', 'switchport_trunk_allowed_vlan',
936
+ name: @name, state: 'no', vlan: '')
653
937
  else
654
- config_set(
655
- 'interface', 'switchport_trunk_allowed_vlan', @name, '', val)
938
+ config_set('interface', 'switchport_trunk_allowed_vlan',
939
+ name: @name, state: '', vlan: val)
656
940
  end
657
- rescue Cisco::CliError => e
658
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
659
941
  end
660
942
 
661
943
  def default_switchport_trunk_allowed_vlan
@@ -663,19 +945,488 @@ module Cisco
663
945
  end
664
946
 
665
947
  def switchport_trunk_native_vlan
666
- config_get('interface', 'switchport_trunk_native_vlan', @name)
948
+ config_get('interface', 'switchport_trunk_native_vlan', name: @name)
667
949
  end
668
950
 
669
951
  def switchport_trunk_native_vlan=(val)
670
952
  if val.nil?
671
- config_set(
672
- 'interface', 'switchport_trunk_native_vlan', @name, 'no', '')
953
+ config_set('interface', 'switchport_trunk_native_vlan',
954
+ name: @name, state: 'no', vlan: '')
673
955
  else
674
- config_set(
675
- 'interface', 'switchport_trunk_native_vlan', @name, '', val)
956
+ config_set('interface', 'switchport_trunk_native_vlan',
957
+ name: @name, state: '', vlan: val)
676
958
  end
677
- rescue Cisco::CliError => e
678
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
959
+ end
960
+
961
+ # Interface private vlan configuration properties
962
+ # private vlan mode (host, promisc, trunk promisc , trunk sec)
963
+ # private vlan mapping
964
+ # private vlan native vlan
965
+ # private vlan allow vlan
966
+
967
+ def cli_error_check(result)
968
+ # The NXOS interface private vlan cli does not raise an exception
969
+ # in some conditions and instead just displays a STDOUT error message
970
+ # thus NXAPI does not detect the failure.
971
+ # We must catch it by inspecting the "body" hash entry
972
+ # returned by NXAPI. This vlan cli behavior is unlikely to change.
973
+ # Check for messages that can be safely ignored.
974
+
975
+ errors = /(ERROR:|VLAN:|Eth)/
976
+ return unless
977
+ result[1].is_a?(Hash) && errors.match(result[1]['body'].to_s)
978
+ # Split errors into a list, but keep the delimiter as part of the message.
979
+ error_list =
980
+ (result[1]['body'].split(errors) - ['']).each_slice(2).map(&:join)
981
+ error_list.each do |_msg|
982
+ fail result[1]['body']
983
+ end
984
+ end
985
+
986
+ def switchport_enable_and_mode_private_vlan_host(mode_set)
987
+ switchport_enable unless switchport
988
+ if mode_set[/(host|promiscuous)/]
989
+ config_set('interface', 'switchport_mode_private_vlan_host',
990
+ name: @name, state: '', mode: IF_SWITCHPORT_MODE[mode_set])
991
+ else
992
+ config_set('interface', 'switchport_mode_private_vlan_host',
993
+ name: @name, state: 'no', mode: IF_SWITCHPORT_MODE[mode_set])
994
+ end
995
+ end
996
+
997
+ def switchport_mode_private_vlan_host
998
+ mode = config_get('interface',
999
+ 'switchport_mode_private_vlan_host',
1000
+ name: @name)
1001
+ unless mode == default_switchport_mode_private_vlan_host
1002
+ mode = IF_SWITCHPORT_MODE.key(mode)
1003
+ end
1004
+ mode
1005
+ rescue IndexError
1006
+ # Assume this is an interface that doesn't support switchport.
1007
+ # Do not raise exception since the providers will prefetch this property
1008
+ # regardless of interface type.
1009
+ # TODO: this should probably be nil instead
1010
+ return default_switchport_mode_private_vlan_host
1011
+ end
1012
+
1013
+ def switchport_mode_private_vlan_host=(mode_set)
1014
+ fail ArgumentError unless IF_SWITCHPORT_MODE.keys.include? mode_set
1015
+ Feature.private_vlan_enable
1016
+ switchport_enable_and_mode_private_vlan_host(mode_set)
1017
+ end
1018
+
1019
+ def default_switchport_mode_private_vlan_host
1020
+ config_get_default('interface',
1021
+ 'switchport_mode_private_vlan_host')
1022
+ end
1023
+
1024
+ def switchport_mode_private_vlan_host_association
1025
+ result = config_get('interface',
1026
+ 'switchport_mode_private_vlan_host_association',
1027
+ name: @name)
1028
+ unless result == default_switchport_mode_private_vlan_host_association
1029
+ result = result[0].split(' ')
1030
+ end
1031
+ result
1032
+ end
1033
+
1034
+ def switchport_mode_private_vlan_host_association=(vlans)
1035
+ fail TypeError unless vlans.is_a?(Array) || vlans.empty?
1036
+ switchport_enable unless switchport
1037
+ Feature.private_vlan_enable
1038
+ if vlans == default_switchport_mode_private_vlan_host_association
1039
+ result = config_set('interface',
1040
+ 'switchport_mode_private_vlan_host_association',
1041
+ name: @name, state: 'no', vlan_pr: '', vlan_sec: '')
1042
+
1043
+ else
1044
+ result = config_set('interface',
1045
+ 'switchport_mode_private_vlan_host_association',
1046
+ name: @name, state: '',
1047
+ vlan_pr: vlans[0], vlan_sec: vlans[1])
1048
+
1049
+ end
1050
+ cli_error_check(result)
1051
+ end
1052
+
1053
+ def default_switchport_mode_private_vlan_host_association
1054
+ config_get_default('interface',
1055
+ 'switchport_mode_private_vlan_host_association')
1056
+ end
1057
+
1058
+ # This api is used by private vlan to prepare the input to the setter
1059
+ # method. The input can be in the following formats for vlans:
1060
+ # 10-12,14. Prepare_array api is transforming this input into a flat array.
1061
+ # In the example above the returned array will be 10, 11, 12, 13. Prepare
1062
+ # array is first splitting the input on ',' and the than expanding the vlan
1063
+ # range element like 10-12 into a flat array. The final result will
1064
+ # be a flat array.
1065
+ # This way we can later used the lib utility to check the delta from
1066
+ # the input vlan value and the vlan configured to apply the right config.
1067
+ def prepare_array(is_list)
1068
+ new_list = []
1069
+ is_list.each do |item|
1070
+ if item.include?(',')
1071
+ new_list.push(item.split(','))
1072
+ else
1073
+ new_list.push(item)
1074
+ end
1075
+ end
1076
+ new_list.flatten!
1077
+ new_list.sort!
1078
+ new_list.each { |item| item.gsub!('-', '..') }
1079
+ is_list_new = []
1080
+ new_list.each do |elem|
1081
+ if elem.include?('..')
1082
+ elema = elem.split('..').map { |d| Integer(d) }
1083
+ elema.sort!
1084
+ tr = elema[0]..elema[1]
1085
+ tr.to_a.each do |item|
1086
+ is_list_new.push(item.to_s)
1087
+ end
1088
+ else
1089
+ is_list_new.push(elem)
1090
+ end
1091
+ end
1092
+ is_list_new
1093
+ end
1094
+
1095
+ def configure_private_vlan_host_property(property, should_list_new,
1096
+ is_list_new, pr_vlan)
1097
+ delta_hash = Utils.delta_add_remove(should_list_new, is_list_new)
1098
+ [:add, :remove].each do |action|
1099
+ delta_hash[action].each do |vlans|
1100
+ state = (action == :add) ? '' : 'no'
1101
+ oper = (action == :add) ? 'add' : 'remove'
1102
+ if property[/(host_promisc|mapping_trunk)/]
1103
+
1104
+ result = config_set('interface', PVLAN_PROPERTY[property],
1105
+ name: @name, state: state,
1106
+ vlan_pr: pr_vlan, vlans: vlans)
1107
+ @match_found = true
1108
+ end
1109
+ if property[/allow_vlan/]
1110
+ result = config_set('interface',
1111
+ PVLAN_PROPERTY[property],
1112
+ name: @name, state: '',
1113
+ oper: oper, vlans: vlans)
1114
+ end
1115
+ if property[/vlan_mapping/]
1116
+ result = config_set('interface',
1117
+ PVLAN_PROPERTY[property],
1118
+ name: @name, state: state,
1119
+ vlans: vlans)
1120
+ end
1121
+ cli_error_check(result)
1122
+ end
1123
+ end
1124
+ end
1125
+
1126
+ def configure_private_vlan_trunk_property(property, should_list_new,
1127
+ is_list, pr_vlan)
1128
+ case property
1129
+ when :trunk_assoc
1130
+ is_list.each do |vlans|
1131
+ vlans = vlans.split(' ')
1132
+ if vlans[0].eql? should_list_new[0]
1133
+ config_set('interface',
1134
+ 'switchport_private_vlan_association_trunk',
1135
+ name: @name, state: 'no',
1136
+ vlan_pr: pr_vlan, vlan: vlans[1])
1137
+ break
1138
+ else
1139
+ next
1140
+ end
1141
+ end
1142
+ result = config_set('interface', PVLAN_PROPERTY[property], name: @name,
1143
+ state: '', vlan_pr: should_list_new[0],
1144
+ vlan: should_list_new[1])
1145
+ when :mapping_trunk
1146
+ @match_found = false
1147
+ is_list.each do |vlans|
1148
+ vlans = vlans.split(' ')
1149
+ interf_vlan_list_delta(:mapping_trunk, vlans,
1150
+ should_list_new)
1151
+ if @match_found
1152
+ break
1153
+ else
1154
+ next
1155
+ end
1156
+ end
1157
+ result = config_set('interface', PVLAN_PROPERTY[property], name: @name,
1158
+ state: '', vlan_pr: should_list_new[0],
1159
+ vlans: should_list_new[1])
1160
+ end
1161
+ cli_error_check(result)
1162
+ end
1163
+
1164
+ # --------------------------
1165
+ # interf_vlan_list_delta is a helper function for the private_vlan_mapping
1166
+ # property. It walks the delta hash and adds/removes each target private
1167
+ # vlan.
1168
+
1169
+ def interf_vlan_list_delta(property, is_list, should_list)
1170
+ pr_vlan = should_list[0]
1171
+ if is_list[0].eql? should_list[0]
1172
+ should_list = should_list[1].split(',')
1173
+ is_list = is_list[1].split(',')
1174
+
1175
+ should_list_new = prepare_array(should_list)
1176
+ is_list_new = prepare_array(is_list)
1177
+ configure_private_vlan_host_property(property, should_list_new,
1178
+ is_list_new, pr_vlan)
1179
+ else
1180
+ case property
1181
+ when :mapping_trunk
1182
+ return
1183
+ end
1184
+ # If primary vlan are different we can simply replacing the all
1185
+ # config
1186
+ if should_list == default_switchport_mode_private_vlan_host_promisc
1187
+ result = config_set('interface',
1188
+ 'switchport_mode_private_vlan_host_promiscous',
1189
+ name: @name, state: 'no',
1190
+ vlan_pr: '', vlans: '')
1191
+
1192
+ else
1193
+ result = config_set('interface',
1194
+ 'switchport_mode_private_vlan_host_promiscous',
1195
+ name: @name, state: '',
1196
+ vlan_pr: pr_vlan, vlans: should_list[1])
1197
+
1198
+ end
1199
+ cli_error_check(result)
1200
+ end
1201
+ end
1202
+
1203
+ def switchport_mode_private_vlan_host_promisc
1204
+ result = config_get('interface',
1205
+ 'switchport_mode_private_vlan_host_promiscous',
1206
+ name: @name)
1207
+ unless result == default_switchport_mode_private_vlan_host_promisc
1208
+ result = result[0].split(' ')
1209
+ end
1210
+ result
1211
+ end
1212
+
1213
+ def switchport_mode_private_vlan_host_promisc=(vlans)
1214
+ fail TypeError unless vlans.is_a?(Array)
1215
+ fail TypeError unless vlans.empty? || vlans.length == 2
1216
+ switchport_enable unless switchport
1217
+ Feature.private_vlan_enable
1218
+ is_list = switchport_mode_private_vlan_host_promisc
1219
+ interf_vlan_list_delta(:host_promisc, is_list, vlans)
1220
+ end
1221
+
1222
+ def default_switchport_mode_private_vlan_host_promisc
1223
+ config_get_default('interface',
1224
+ 'switchport_mode_private_vlan_host_promiscous')
1225
+ end
1226
+
1227
+ def switchport_mode_private_vlan_trunk_promiscuous
1228
+ config_get('interface',
1229
+ 'switchport_mode_private_vlan_trunk_promiscuous',
1230
+ name: @name)
1231
+ rescue IndexError
1232
+ # Assume this is an interface that doesn't support switchport.
1233
+ # Do not raise exception since the providers will prefetch this property
1234
+ # regardless of interface type.
1235
+ # TODO: this should probably be nil instead
1236
+ return default_switchport_mode_private_vlan_trunk_promiscuous
1237
+ end
1238
+
1239
+ def switchport_mode_private_vlan_trunk_promiscuous=(state)
1240
+ Feature.private_vlan_enable
1241
+ switchport_enable unless switchport
1242
+ if state == default_switchport_mode_private_vlan_trunk_promiscuous
1243
+ config_set('interface',
1244
+ 'switchport_mode_private_vlan_trunk_promiscuous',
1245
+ name: @name, state: 'no')
1246
+ else
1247
+ config_set('interface',
1248
+ 'switchport_mode_private_vlan_trunk_promiscuous',
1249
+ name: @name, state: '')
1250
+ end
1251
+ end
1252
+
1253
+ def default_switchport_mode_private_vlan_trunk_promiscuous
1254
+ config_get_default('interface',
1255
+ 'switchport_mode_private_vlan_trunk_promiscuous')
1256
+ end
1257
+
1258
+ def switchport_mode_private_vlan_trunk_secondary
1259
+ config_get('interface',
1260
+ 'switchport_mode_private_vlan_trunk_secondary',
1261
+ name: @name)
1262
+ rescue IndexError
1263
+ # Assume this is an interface that doesn't support switchport.
1264
+ # Do not raise exception since the providers will prefetch this property
1265
+ # regardless of interface type.
1266
+ # TODO: this should probably be nil instead
1267
+ return default_switchport_mode_private_vlan_trunk_secondary
1268
+ end
1269
+
1270
+ def switchport_mode_private_vlan_trunk_secondary=(state)
1271
+ Feature.private_vlan_enable
1272
+ switchport_enable unless switchport
1273
+ if state == default_switchport_mode_private_vlan_trunk_secondary
1274
+ config_set('interface', 'switchport_mode_private_vlan_trunk_secondary',
1275
+ name: @name, state: 'no')
1276
+ else
1277
+ config_set('interface', 'switchport_mode_private_vlan_trunk_secondary',
1278
+ name: @name, state: '')
1279
+ end
1280
+ end
1281
+
1282
+ def default_switchport_mode_private_vlan_trunk_secondary
1283
+ config_get_default('interface',
1284
+ 'switchport_mode_private_vlan_trunk_secondary')
1285
+ end
1286
+
1287
+ def switchport_private_vlan_trunk_allowed_vlan
1288
+ result = config_get('interface',
1289
+ 'switchport_private_vlan_trunk_allowed_vlan',
1290
+ name: @name)
1291
+
1292
+ unless result == default_switchport_private_vlan_trunk_allowed_vlan
1293
+ if result[0].eql? 'none'
1294
+ result = default_switchport_private_vlan_trunk_allowed_vlan
1295
+ else
1296
+ result = result[0].split(',')
1297
+ end
1298
+ end
1299
+ result
1300
+ end
1301
+
1302
+ def switchport_private_vlan_trunk_allowed_vlan=(vlans)
1303
+ fail TypeError unless vlans.is_a?(Array)
1304
+ Feature.private_vlan_enable
1305
+ switchport_enable unless switchport
1306
+ if vlans == default_switchport_private_vlan_trunk_allowed_vlan
1307
+ vlans = prepare_array(switchport_private_vlan_trunk_allowed_vlan)
1308
+ # If there are no vlan presently configured, we can simply return
1309
+ return if vlans == default_switchport_private_vlan_trunk_allowed_vlan
1310
+ configure_private_vlan_host_property(:allow_vlan, [],
1311
+ vlans, '')
1312
+ else
1313
+ vlans = prepare_array(vlans)
1314
+ is_list = prepare_array(switchport_private_vlan_trunk_allowed_vlan)
1315
+ configure_private_vlan_host_property(:allow_vlan, vlans,
1316
+ is_list, '')
1317
+ end
1318
+ end
1319
+
1320
+ def default_switchport_private_vlan_trunk_allowed_vlan
1321
+ config_get_default('interface',
1322
+ 'switchport_private_vlan_trunk_allowed_vlan')
1323
+ end
1324
+
1325
+ def switchport_private_vlan_trunk_native_vlan
1326
+ config_get('interface',
1327
+ 'switchport_private_vlan_trunk_native_vlan',
1328
+ name: @name)
1329
+ end
1330
+
1331
+ def switchport_private_vlan_trunk_native_vlan=(vlan)
1332
+ Feature.private_vlan_enable
1333
+ switchport_enable unless switchport
1334
+ if vlan == default_switchport_private_vlan_trunk_native_vlan
1335
+ config_set('interface',
1336
+ 'switchport_private_vlan_trunk_native_vlan',
1337
+ name: @name, state: 'no', vlan: '')
1338
+
1339
+ else
1340
+ config_set('interface',
1341
+ 'switchport_private_vlan_trunk_native_vlan',
1342
+ name: @name, state: '', vlan: vlan)
1343
+ end
1344
+ end
1345
+
1346
+ def default_switchport_private_vlan_trunk_native_vlan
1347
+ config_get_default('interface',
1348
+ 'switchport_private_vlan_trunk_native_vlan')
1349
+ end
1350
+
1351
+ def switchport_private_vlan_association_trunk
1352
+ config_get('interface',
1353
+ 'switchport_private_vlan_association_trunk',
1354
+ name: @name)
1355
+ end
1356
+
1357
+ def switchport_private_vlan_association_trunk=(vlans)
1358
+ fail TypeError unless vlans.is_a?(Array) || vlans.empty?
1359
+ Feature.private_vlan_enable
1360
+ switchport_enable unless switchport
1361
+ if vlans == default_switchport_private_vlan_association_trunk
1362
+ config_set('interface', 'switchport_private_vlan_association_trunk',
1363
+ name: @name, state: 'no',
1364
+ vlan_pr: '', vlan: '')
1365
+ else
1366
+ is_list = switchport_private_vlan_association_trunk
1367
+ configure_private_vlan_trunk_property(:trunk_assoc, vlans,
1368
+ is_list, vlans[0])
1369
+ end
1370
+ end
1371
+
1372
+ def default_switchport_private_vlan_association_trunk
1373
+ config_get_default('interface',
1374
+ 'switchport_private_vlan_association_trunk')
1375
+ end
1376
+
1377
+ def switchport_private_vlan_mapping_trunk
1378
+ config_get('interface',
1379
+ 'switchport_private_vlan_mapping_trunk',
1380
+ name: @name)
1381
+ end
1382
+
1383
+ def switchport_private_vlan_mapping_trunk=(vlans)
1384
+ fail TypeError unless vlans.is_a?(Array) || vlans.empty?
1385
+ Feature.private_vlan_enable
1386
+ switchport_enable unless switchport
1387
+ if vlans == default_switchport_private_vlan_mapping_trunk
1388
+ config_set('interface', 'switchport_private_vlan_mapping_trunk',
1389
+ name: @name, state: 'no',
1390
+ vlan_pr: '', vlans: '')
1391
+ else
1392
+ is_list = switchport_private_vlan_mapping_trunk
1393
+ configure_private_vlan_trunk_property(:mapping_trunk, vlans,
1394
+ is_list, vlans[0])
1395
+ end
1396
+ end
1397
+
1398
+ def default_switchport_private_vlan_mapping_trunk
1399
+ config_get_default('interface',
1400
+ 'switchport_private_vlan_mapping_trunk')
1401
+ end
1402
+
1403
+ def private_vlan_mapping
1404
+ match = config_get('interface',
1405
+ 'private_vlan_mapping',
1406
+ name: @name)
1407
+ match[0].delete!(' ') unless match == default_private_vlan_mapping
1408
+ match
1409
+ end
1410
+
1411
+ def private_vlan_mapping=(vlans)
1412
+ fail TypeError unless vlans.is_a?(Array) || vlans.empty?
1413
+ Feature.private_vlan_enable
1414
+ feature_vlan_set(true)
1415
+ if vlans == default_private_vlan_mapping
1416
+ config_set('interface', 'private_vlan_mapping',
1417
+ name: @name, state: 'no', vlans: '')
1418
+ else
1419
+ is_list = private_vlan_mapping
1420
+ new_is_list = prepare_array(is_list)
1421
+ new_vlans = prepare_array(vlans)
1422
+ configure_private_vlan_host_property(:vlan_mapping, new_vlans,
1423
+ new_is_list, '')
1424
+ end
1425
+ end
1426
+
1427
+ def default_private_vlan_mapping
1428
+ config_get_default('interface',
1429
+ 'private_vlan_mapping')
679
1430
  end
680
1431
 
681
1432
  # vlan_mapping & vlan_mapping_enable
@@ -698,13 +1449,13 @@ module Cisco
698
1449
  end
699
1450
 
700
1451
  def vlan_mapping
701
- match = config_get('interface', 'vlan_mapping', @name)
1452
+ match = config_get('interface', 'vlan_mapping', name: @name)
702
1453
  match.each(&:compact!) unless match.nil?
703
1454
  match
704
1455
  end
705
1456
 
706
1457
  def vlan_mapping=(should_list)
707
- Vni.feature_vni_enable unless Vni.feature_vni_enabled
1458
+ Feature.vni_enable
708
1459
 
709
1460
  # Process a hash of vlan_mapping cmds from delta_add_remove().
710
1461
  # The vlan_mapping cli does not allow commands to be updated, they must
@@ -714,16 +1465,14 @@ module Cisco
714
1465
  return if delta_hash.values.flatten.empty?
715
1466
  # Process :remove first to ensure "update" commands will not fail.
716
1467
  [:remove, :add].each do |action|
717
- CiscoLogger.debug("vlan_mapping delta #{@get_args}\n"\
718
- "#{action}: #{delta_hash[action]}")
1468
+ Cisco::Logger.debug("vlan_mapping delta #{@get_args}\n"\
1469
+ "#{action}: #{delta_hash[action]}")
719
1470
  delta_hash[action].each do |original, translated|
720
1471
  state = (action == :add) ? '' : 'no'
721
- config_set('interface', 'vlan_mapping', @name,
722
- state, original, translated)
1472
+ config_set('interface', 'vlan_mapping', name: @name,
1473
+ state: state, original: original, translated: translated)
723
1474
  end
724
1475
  end
725
- rescue Cisco::CliError => e
726
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
727
1476
  end
728
1477
 
729
1478
  # cli: switchport vlan mapping enable
@@ -732,12 +1481,12 @@ module Cisco
732
1481
  end
733
1482
 
734
1483
  def vlan_mapping_enable
735
- config_get('interface', 'vlan_mapping_enable', @name)
1484
+ config_get('interface', 'vlan_mapping_enable', name: @name)
736
1485
  end
737
1486
 
738
1487
  def vlan_mapping_enable=(state)
739
- config_set('interface', 'vlan_mapping_enable', @name,
740
- state ? '' : 'no')
1488
+ config_set('interface', 'vlan_mapping_enable',
1489
+ name: @name, state: state ? '' : 'no')
741
1490
  end
742
1491
 
743
1492
  def default_switchport_trunk_native_vlan
@@ -782,16 +1531,15 @@ module Cisco
782
1531
  end
783
1532
 
784
1533
  def switchport_vtp
785
- return false unless switchport_vtp_mode_capable?
786
- config_get('interface', 'vtp', @name)
1534
+ return nil unless switchport_vtp_mode_capable?
1535
+ config_get('interface', 'vtp', name: @name)
787
1536
  end
788
1537
 
789
1538
  def switchport_vtp=(vtp_set)
1539
+ # TODO: throw UnsupportedError instead of returning false?
790
1540
  return false unless switchport_vtp_mode_capable?
791
1541
  no_cmd = (vtp_set) ? '' : 'no'
792
- config_set('interface', 'vtp', @name, no_cmd)
793
- rescue Cisco::CliError => e
794
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
1542
+ config_set('interface', 'vtp', name: @name, state: no_cmd)
795
1543
  end
796
1544
 
797
1545
  def svi_cmd_allowed?(cmd)
@@ -802,13 +1550,14 @@ module Cisco
802
1550
  # svi_autostate is exclusive to svi interfaces
803
1551
  def svi_autostate
804
1552
  return nil unless @name[/^vlan/i]
805
- config_get('interface', 'svi_autostate', @name)
1553
+ config_get('interface', 'svi_autostate', name: @name)
806
1554
  end
807
1555
 
808
1556
  def svi_autostate=(val)
809
- check_switchport_disabled
1557
+ check_switchport(:disabled)
810
1558
  svi_cmd_allowed?('autostate')
811
- config_set('interface', 'svi_autostate', @name, val ? '' : 'no')
1559
+ config_set('interface', 'svi_autostate',
1560
+ name: @name, state: val ? '' : 'no')
812
1561
  end
813
1562
 
814
1563
  def default_svi_autostate
@@ -821,19 +1570,20 @@ module Cisco
821
1570
 
822
1571
  def feature_vlan_set(val)
823
1572
  return if feature_vlan? == val
824
- config_set('interface', 'feature_vlan', val ? '' : 'no')
1573
+ config_set('interface', 'feature_vlan', state: val ? '' : 'no')
825
1574
  end
826
1575
 
827
1576
  # svi_management is exclusive to svi interfaces
828
1577
  def svi_management
829
1578
  return nil unless @name[/^vlan/i]
830
- config_get('interface', 'svi_management', @name)
1579
+ config_get('interface', 'svi_management', name: @name)
831
1580
  end
832
1581
 
833
1582
  def svi_management=(val)
834
- check_switchport_disabled
1583
+ check_switchport(:disabled)
835
1584
  svi_cmd_allowed?('management')
836
- config_set('interface', 'svi_management', @name, val ? '' : 'no')
1585
+ config_set('interface', 'svi_management',
1586
+ name: @name, state: val ? '' : 'no')
837
1587
  end
838
1588
 
839
1589
  def default_svi_management
@@ -841,6 +1591,7 @@ module Cisco
841
1591
  end
842
1592
 
843
1593
  def default_switchport_vtp
1594
+ return nil unless switchport_vtp_mode_capable?
844
1595
  config_get_default('interface', 'vtp')
845
1596
  end
846
1597
 
@@ -848,22 +1599,36 @@ module Cisco
848
1599
  config_get('vtp', 'feature')
849
1600
  end
850
1601
 
851
- def check_switchport_disabled
852
- fail "#{caller[0][/`.*'/][1..-2]} cannot be set unless switchport mode" \
853
- ' is disabled' unless switchport_mode == :disabled
1602
+ def switchport_status?(status)
1603
+ case status
1604
+ when :disabled
1605
+ return true if switchport_mode == status || switchport_mode.nil?
1606
+ when :access, :trunk
1607
+ return switchport_mode == status
1608
+ when Array
1609
+ return status.include?(switchport_mode)
1610
+ else
1611
+ return false
1612
+ end
1613
+ end
1614
+
1615
+ def check_switchport(status)
1616
+ return if switchport_status?(status)
1617
+ fail("#{caller[0][/`.*'/][1..-2]} cannot be set unless " \
1618
+ "switchport mode is #{status}")
854
1619
  end
855
1620
 
856
1621
  def vpc_id
857
- config_get('interface', 'vpc_id', @name)
1622
+ config_get('interface', 'vpc_id', name: @name)
858
1623
  end
859
1624
 
860
1625
  def vpc_id=(num)
861
1626
  if num
862
- config_set('interface', 'vpc_id', @name, '', num)
1627
+ config_set('interface', 'vpc_id', name: @name, state: '', id: num)
863
1628
  else
864
1629
  # 'no vpc' doesn't work for phy ports, so do a get
865
1630
  num = vpc_id
866
- config_set('interface', 'vpc_id', @name, 'no', num)
1631
+ config_set('interface', 'vpc_id', name: @name, state: 'no', id: num)
867
1632
  end
868
1633
  end
869
1634
 
@@ -872,12 +1637,12 @@ module Cisco
872
1637
  end
873
1638
 
874
1639
  def vpc_peer_link
875
- config_get('interface', 'vpc_peer_link', @name)
1640
+ config_get('interface', 'vpc_peer_link', name: @name)
876
1641
  end
877
1642
 
878
1643
  def vpc_peer_link=(state)
879
1644
  no_cmd = (state ? '' : 'no')
880
- config_set('interface', 'vpc_peer_link', @name, no_cmd)
1645
+ config_set('interface', 'vpc_peer_link', name: @name, state: no_cmd)
881
1646
  end
882
1647
 
883
1648
  def default_vpc_peer_link
@@ -885,22 +1650,48 @@ module Cisco
885
1650
  end
886
1651
 
887
1652
  def vrf
888
- config_get('interface', 'vrf', @name)
889
- end
890
-
891
- def vrf=(vrf)
892
- fail TypeError unless vrf.is_a?(String)
893
- if vrf.empty?
894
- config_set('interface', 'vrf', @name, 'no', '')
1653
+ config_get('interface', 'vrf', name: @name)
1654
+ end
1655
+
1656
+ def vrf=(v)
1657
+ fail TypeError unless v.is_a?(String)
1658
+ return if v == vrf
1659
+ # Changing the VRF can result in loss of IP address, so cache it
1660
+ addr_1 = ipv4_address
1661
+ mask_1 = ipv4_netmask_length
1662
+ addr_2 = ipv4_address_secondary
1663
+ mask_2 = ipv4_netmask_length_secondary
1664
+ # XR actually blocks you from changing the VRF if IP addr is present
1665
+ unless platform == :nexus
1666
+ ipv4_addr_mask_set(nil, nil, false) unless addr_1.nil?
1667
+ ipv4_addr_mask_set(nil, nil, true) unless addr_2.nil?
1668
+ end
1669
+ if v.empty?
1670
+ config_set('interface', 'vrf', name: @name, state: 'no', vrf: '')
895
1671
  else
896
- config_set('interface', 'vrf', @name, '', vrf)
1672
+ config_set('interface', 'vrf', name: @name, state: '', vrf: v)
897
1673
  end
898
- rescue Cisco::CliError => e
899
- raise "[#{@name}] '#{e.command}' : #{e.clierror}"
1674
+ ipv4_addr_mask_set(addr_1, mask_1, false) unless addr_1.nil?
1675
+ ipv4_addr_mask_set(addr_2, mask_2, true) unless addr_2.nil?
900
1676
  end
901
1677
 
902
1678
  def default_vrf
903
1679
  config_get_default('interface', 'vrf')
904
1680
  end
1681
+
1682
+ def set_range_based_params(list, param_name)
1683
+ list.each do |range, property_value|
1684
+ # if a particular range is set to default, use 'no' cmd
1685
+ if property_value == 'default'
1686
+ config_set('interface', param_name,
1687
+ name: @name, state: 'no',
1688
+ range: range, val: '')
1689
+ else
1690
+ config_set('interface', param_name,
1691
+ name: @name, state: '',
1692
+ range: range, val: property_value)
1693
+ end
1694
+ end
1695
+ end
905
1696
  end # Class
906
1697
  end # Module