cisco_node_utils 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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