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
@@ -1,13 +1,15 @@
1
1
  # yum
2
2
  ---
3
+ _exclude: [ios_xr]
4
+
3
5
  install:
4
- config_set: "install add %s %s activate"
6
+ set_value: "install add %s %s activate"
5
7
 
6
8
  query:
7
9
  multiple: true
8
- config_get: "show install packages"
10
+ get_command: "show install packages"
9
11
  # pass in the pkg name, retrieve version
10
- config_get_token: '/^%s\S*\s+(\S+)\s+(?:installed|@\S+)/'
12
+ get_value: '/^%s\S*\s+(\S+)\s+(?:installed|@\S+)/'
11
13
 
12
14
  remove:
13
- config_set: "install deactivate %s"
15
+ set_value: "install deactivate %s"
@@ -1,5 +1,3 @@
1
- # CommandReference module for testing.
2
- #
3
1
  # Copyright (c) 2014-2016 Cisco and/or its affiliates.
4
2
  #
5
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +12,7 @@
14
12
  # See the License for the specific language governing permissions and
15
13
  # limitations under the License.
16
14
 
15
+ require_relative 'exceptions'
17
16
  require 'yaml'
18
17
 
19
18
  module Cisco
@@ -26,16 +25,16 @@ module Cisco
26
25
  alias_method :multiple?, :multiple
27
26
 
28
27
  KEYS = %w(default_value default_only
29
- config_set config_set_append
30
- config_get config_get_token config_get_token_append
31
- auto_default multiple kind
32
- test_config_get test_config_get_regex test_config_result)
28
+ data_format context value
29
+ get_data_format get_command get_context get_value
30
+ set_data_format set_context set_value
31
+ auto_default multiple kind)
33
32
 
34
33
  def self.keys
35
34
  KEYS
36
35
  end
37
36
 
38
- KINDS = %w(boolean int string)
37
+ KINDS = %w(boolean int string symbol)
39
38
 
40
39
  # Construct a CmdRef describing the given (feature, name) pair.
41
40
  # Param "values" is a hash with keys as described in KEYS.
@@ -45,46 +44,113 @@ module Cisco
45
44
 
46
45
  @feature = feature
47
46
  @name = name
48
- @hash = {}
49
47
  @auto_default = true
50
48
  @default_only = false
51
49
  @multiple = false
52
50
  @kind = nil
53
51
 
52
+ values_to_hash(values, file)
53
+
54
+ if @hash['get_value'] || @hash['get_command']
55
+ define_helper('getter',
56
+ data_format: @hash['get_data_format'] || :cli,
57
+ command: @hash['get_command'],
58
+ context: @hash['get_context'] || [],
59
+ value: @hash['get_value'])
60
+ end
61
+ if @hash['set_value'] # rubocop:disable Style/GuardClause
62
+ define_helper('setter',
63
+ data_format: @hash['set_data_format'] || :cli,
64
+ context: @hash['set_context'] || [],
65
+ values: @hash['set_value'])
66
+ end
67
+ end
68
+
69
+ def values_to_hash(values, file)
70
+ @hash = {}
54
71
  values.each do |key, value|
55
72
  unless KEYS.include?(key)
56
73
  fail "Unrecognized key #{key} for #{feature}, #{name} in #{file}"
57
74
  end
58
- if key == 'config_get_token' || key == 'config_set'
59
- # For simplicity, these are ALWAYS arrays
60
- value = [value] unless value.is_a?(Array)
61
- define_getter(key, value)
62
- # We intentionally do this *after* the define_getter() call
63
- @hash[key] = preprocess_value(value)
64
- elsif key == 'auto_default'
75
+ case key
76
+ when 'auto_default'
65
77
  @auto_default = value ? true : false
66
- elsif key == 'default_only'
78
+ when 'data_format', 'get_data_format', 'set_data_format'
79
+ @hash[key] = value.to_sym
80
+ when 'default_only'
67
81
  @default_only = true
68
82
  # default_value overrides default_only
69
- @hash['default_value'] ||= preprocess_value(value)
70
- elsif key == 'multiple'
83
+ @hash['default_value'] ||= value
84
+ when 'multiple'
71
85
  @multiple = boolean_default_true(value)
72
- elsif key == 'kind'
86
+ when 'kind'
73
87
  fail "Unknown 'kind': '#{value}'" unless KINDS.include?(value)
74
88
  @kind = value.to_sym
75
89
  else
76
90
  # default_value overrides default_only
77
91
  @default_only = false if key == 'default_value'
78
- @hash[key] = preprocess_value(value)
92
+ @hash[key] = value
79
93
  end
80
94
  end
81
95
 
82
- if @default_only # rubocop:disable Style/GuardClause
83
- %w(config_get_token config_set).each do |key|
84
- instance_eval "undef #{key}" if @hash.key?(key)
85
- end
86
- @hash.delete_if { |key, _| key != 'default_value' }
96
+ # Inherit general to specific if needed
97
+ if @hash.key?('data_format')
98
+ @hash['get_data_format'] = @hash['data_format'] \
99
+ unless @hash.key?('get_data_format')
100
+ @hash['set_data_format'] = @hash['data_format'] \
101
+ unless @hash.key?('set_data_format')
87
102
  end
103
+ if @hash.key?('context')
104
+ @hash['get_context'] = @hash['context'] unless @hash.key?('get_context')
105
+ @hash['set_context'] = @hash['context'] unless @hash.key?('set_context')
106
+ end
107
+ if @hash.key?('value')
108
+ @hash['get_value'] = @hash['value'] unless @hash.key?('get_value')
109
+ @hash['set_value'] = @hash['value'] unless @hash.key?('set_value')
110
+ end
111
+
112
+ @hash.delete_if { |key, _| key != 'default_value' } if @default_only
113
+ end
114
+
115
+ # Does this instance have a valid getter() function?
116
+ # Will be overridden at initialization if so.
117
+ def getter?
118
+ !@hash['getter'].nil?
119
+ end
120
+
121
+ # Does this instance have a valid setter() function?
122
+ # Will be overridden at initialization if so.
123
+ def setter?
124
+ !@hash['setter'].nil?
125
+ end
126
+
127
+ # Default getter method.
128
+ # Will be overridden at initialization if the relevant parameters are set.
129
+ #
130
+ # A non-trivial implementation of this method will take args *or* kwargs,
131
+ # and will return a hash of the form:
132
+ # {
133
+ # data_format: :cli,
134
+ # command: string or nil,
135
+ # context: array<string> or array<regexp>, perhaps empty
136
+ # value: string or regexp,
137
+ # }
138
+ def getter(*args, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
139
+ fail UnsupportedError.new(@feature, @name, 'getter')
140
+ end
141
+
142
+ # Default setter method.
143
+ # Will be overridden at initialization if the relevant parameters are set.
144
+ #
145
+ # A non-trivial implementation of this method will take args *or* kwargs,
146
+ # and will return a hash of the form:
147
+ # {
148
+ # data_format: :cli,
149
+ # context: array<string>, perhaps empty
150
+ # values: array<string>,
151
+ # }
152
+ def setter(*args, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
153
+ fail UnsupportedError.new(@feature, @name, 'setter')
88
154
  end
89
155
 
90
156
  # Property with an implicit value of 'true' if no value is given
@@ -92,78 +158,119 @@ module Cisco
92
158
  value.nil? || value
93
159
  end
94
160
 
95
- # Create a getter method for the given key.
96
- # This getter method will automatically handle wildcard arguments.
97
- def define_getter(key, value)
98
- return unless value.is_a?(Array)
99
- if value.any? { |item| item.is_a?(String) && /<\S+>/ =~ item }
100
- # Key-value substitution
101
- define_singleton_method(key.to_sym, key_substitutor(key, value))
102
- elsif value.any? { |item| item.is_a?(String) && /%/ =~ item }
103
- # printf-style substitution
104
- define_singleton_method(key.to_sym, printf_substitutor(key, value))
161
+ def key_substitutor(item, kwargs)
162
+ result = item
163
+ kwargs.each do |key, value|
164
+ result = result.sub("<#{key}>", value.to_s)
165
+ end
166
+ unsub = result[/<(\S+)>/, 1]
167
+ fail ArgumentError, \
168
+ "No value specified for '#{unsub}' in '#{result}'" if unsub
169
+ result
170
+ end
171
+
172
+ def printf_substitutor(item, args)
173
+ item = sprintf(item, *args.shift(item.scan(/%/).length))
174
+ [item, args]
175
+ end
176
+
177
+ # Create a helper method for generating the getter/setter values.
178
+ # This method will automatically handle wildcard arguments.
179
+ def define_helper(method_name, base_hash)
180
+ # Which kind of wildcards (if any) do we need to support?
181
+ combined = []
182
+ base_hash.each_value do |v|
183
+ combined += v if v.is_a?(Array)
184
+ combined << v if v.is_a?(String)
185
+ end
186
+ key_value = combined.any? { |i| i.is_a?(String) && /<\S+>/ =~ i }
187
+ printf = combined.any? { |i| i.is_a?(String) && /%/ =~ i }
188
+
189
+ if key_value && printf
190
+ fail 'Invalid mixture of key-value and printf wildcards ' \
191
+ "in #{method_name}: #{combined}"
192
+ elsif key_value
193
+ define_key_value_helper(method_name, base_hash)
194
+ elsif printf
195
+ arg_count = combined.join.scan(/%/).length
196
+ define_printf_helper(method_name, base_hash, arg_count)
105
197
  else
106
198
  # simple static token(s)
107
- value = preprocess_value(value)
108
- define_singleton_method key.to_sym, -> { value }
199
+ define_static_helper(method_name, base_hash)
109
200
  end
201
+ @hash[method_name] = true
110
202
  end
111
203
 
112
- # curried function to define a getter method body that performs key-value
113
- # substitution
114
- def key_substitutor(config_key, value)
115
- lambda do |**args|
116
- result = []
117
- value.each do |line|
118
- replace = line.scan(/<(\S+)>/).flatten.map(&:to_sym)
119
- replace.each do |item|
120
- line = line.sub("<#{item}>", args[item].to_s) if args.key?(item)
121
- end
122
- result.push(line) unless /<\S+>/.match(line)
204
+ def define_key_value_helper(method_name, base_hash)
205
+ # Key-value substitution
206
+ define_singleton_method method_name.to_sym do |*args, **kwargs|
207
+ unless args.empty?
208
+ fail ArgumentError, "#{method_name} requires keyword args, not "\
209
+ 'positional args'
123
210
  end
124
- if result.empty?
125
- fail ArgumentError,
126
- "Arguments given to #{config_key} yield empty result"
211
+ result = {}
212
+ base_hash.each do |k, v|
213
+ if v.is_a?(String)
214
+ v = key_substitutor(v, kwargs)
215
+ elsif v.is_a?(Array)
216
+ output = []
217
+ v.each do |line|
218
+ # Check for (?) flag indicating optional param
219
+ optional_line = line[/^\(\?\)(.*)/, 1]
220
+ if optional_line
221
+ begin
222
+ line = key_substitutor(optional_line, kwargs)
223
+ rescue ArgumentError # Unsubstituted key - OK to skip this line
224
+ next
225
+ end
226
+ else
227
+ line = key_substitutor(line, kwargs)
228
+ end
229
+ output.push(line)
230
+ end
231
+ v = output
232
+ end
233
+ result[k] = v
127
234
  end
128
- preprocess_value(result)
235
+ result
129
236
  end
130
237
  end
131
238
 
132
- # curried function to define a getter method body that performs
133
- # printf-style substitution
134
- def printf_substitutor(config_key, value)
135
- arg_c = value.join.scan(/%/).length
136
- lambda do |*args|
137
- unless args.length == arg_c
138
- fail ArgumentError,
139
- "Given #{args.length} args, but #{config_key} requires #{arg_c}"
239
+ def define_printf_helper(method_name, base_hash, arg_count)
240
+ define_singleton_method method_name.to_sym do |*args, **kwargs|
241
+ unless kwargs.empty?
242
+ fail ArgumentError, "#{method_name} requires positional args, not " \
243
+ 'keyword args'
140
244
  end
141
- # Fill in the parameters
142
- result = value.map do |line|
143
- sprintf(line, *args.shift(line.scan(/%/).length))
245
+ unless args.length == arg_count
246
+ fail ArgumentError, 'wrong number of arguments ' \
247
+ "(#{args.length} for #{arg_count})"
144
248
  end
145
- preprocess_value(result)
249
+
250
+ result = {}
251
+ base_hash.each do |k, v|
252
+ if v.is_a?(String)
253
+ v, args = printf_substitutor(v, args)
254
+ elsif v.is_a?(Array)
255
+ output = []
256
+ v.each do |line|
257
+ line, args = printf_substitutor(line, args)
258
+ output.push(line)
259
+ end
260
+ v = output
261
+ end
262
+ result[k] = v
263
+ end
264
+ result
146
265
  end
147
266
  end
148
267
 
149
- # Helper method.
150
- # Converts a regexp-like string (or array thereof) into a proper
151
- # Regexp object (or array thereof)
152
- def preprocess_value(value)
153
- if value.is_a?(Array)
154
- # Recurse!
155
- return value.map { |item| preprocess_value(item) }
156
- elsif value.is_a?(String)
157
- # Some 'Strings' in YAML are actually intended to be regexps
158
- if value[0] == '/' && value[-1] == '/'
159
- # '/foo/' => %r{foo}
160
- return Regexp.new(value[1..-2])
161
- elsif value[0] == '/' && value[-2..-1] == '/i'
162
- # '/foo/i' => %r{foo}i
163
- return Regexp.new(value[1..-3], Regexp::IGNORECASE)
164
- end
268
+ def define_static_helper(method_name, base_hash)
269
+ # rubocop:disable Lint/UnusedBlockArgument
270
+ define_singleton_method method_name.to_sym do |*args, **kwargs|
271
+ base_hash
165
272
  end
166
- value
273
+ # rubocop:enable Lint/UnusedBlockArgument
167
274
  end
168
275
 
169
276
  def convert_to_constant(value)
@@ -187,11 +294,6 @@ module Cisco
187
294
  value
188
295
  end
189
296
 
190
- def test_config_result(value)
191
- result = @hash['test_config_result'][value]
192
- convert_to_constant(result)
193
- end
194
-
195
297
  def method_missing(method_name, *args, &block)
196
298
  if KEYS.include?(method_name.to_s)
197
299
  # ref.foo -> return @hash[foo] or fail IndexError
@@ -223,22 +325,6 @@ module Cisco
223
325
  end
224
326
  end
225
327
 
226
- # Exception class raised when a particular feature/attribute
227
- # is explicitly excluded on the given node.
228
- class UnsupportedError < RuntimeError
229
- def initialize(feature, name, oper=nil, msg=nil)
230
- @feature = feature
231
- @name = name
232
- @oper = oper
233
- message = "Feature '#{feature}'"
234
- message += ", attribute '#{name}'" unless name.nil?
235
- message += ", operation '#{oper}'" unless oper.nil?
236
- message += ' is unsupported on this node'
237
- message += ": #{msg}" unless msg.nil?
238
- super(message)
239
- end
240
- end
241
-
242
328
  # Placeholder for known but explicitly excluded entry
243
329
  # For these, we have an implied default_only value of nil.
244
330
  class UnsupportedCmdRef < CmdRef
@@ -257,21 +343,21 @@ module Cisco
257
343
  @@debug = value # rubocop:disable Style/ClassVars
258
344
  end
259
345
 
260
- attr_reader :cli, :files, :platform, :product_id
346
+ attr_reader :data_formats, :files, :platform, :product_id
261
347
 
262
348
  # Constructor.
263
- # Normal usage is to pass product, platform, cli, in which case usual YAML
264
- # files will be located then the list will be filtered down to only those
265
- # matching the given settings.
349
+ # Normal usage is to pass product, platform, data_formats,
350
+ # in which case usual YAML files will be located then the list
351
+ # will be filtered down to only those matching the given settings.
266
352
  # For testing purposes (only!) you can pass an explicit list of files to
267
353
  # load instead. This list will NOT be filtered further by product_id.
268
- def initialize(product: nil,
269
- platform: nil,
270
- cli: false,
271
- files: nil)
354
+ def initialize(product: nil,
355
+ platform: nil,
356
+ data_formats: [],
357
+ files: nil)
272
358
  @product_id = product
273
359
  @platform = platform
274
- @cli = cli
360
+ @data_formats = data_formats
275
361
  @hash = {}
276
362
  if files
277
363
  @files = files
@@ -285,7 +371,6 @@ module Cisco
285
371
  # Build complete reference hash.
286
372
  def build_cmd_ref
287
373
  # Example id's: N3K-C3048TP-1GE, N3K-C3064PQ-10GE, N7K-C7009, N7K-C7009
288
-
289
374
  debug "Product: #{@product_id}"
290
375
  debug "Files being used: #{@files.join(', ')}"
291
376
 
@@ -297,7 +382,11 @@ module Cisco
297
382
  debug "Feature #{feature} is empty"
298
383
  next
299
384
  end
300
- feature_hash = filter_hash(feature_hash)
385
+ begin
386
+ feature_hash = filter_hash(feature_hash)
387
+ rescue RuntimeError => e
388
+ raise "#{file}: #{e}"
389
+ end
301
390
  if feature_hash.empty?
302
391
  debug "Feature #{feature} is excluded"
303
392
  @hash[feature] = UnsupportedCmdRef.new(feature, nil, file)
@@ -322,6 +411,12 @@ module Cisco
322
411
  end
323
412
  end
324
413
 
414
+ def supports?(feature, property=nil)
415
+ value = @hash[feature]
416
+ value = value[property] if value.is_a?(Hash) && property
417
+ !(value.is_a?(UnsupportedCmdRef) || value.nil?)
418
+ end
419
+
325
420
  # Get the command reference
326
421
  def lookup(feature, name)
327
422
  value = @hash[feature]
@@ -339,15 +434,30 @@ module Cisco
339
434
  puts "DEBUG: #{text}" if @@debug
340
435
  end
341
436
 
342
- KNOWN_FILTERS = %w(cli_nexus)
437
+ KNOWN_PLATFORMS = %w(C3064 C3132 C3172 N3k N5k N6k N7k N8k N9k XRv9k)
438
+
439
+ def self.platform_to_filter(platform)
440
+ if KNOWN_PLATFORMS.include?(platform)
441
+ case platform
442
+ when 'XRv9k'
443
+ /XRV9/
444
+ else
445
+ Regexp.new platform.tr('k', '')
446
+ end
447
+ else
448
+ fail IndexError, "Unknown platform key '#{platform}'"
449
+ end
450
+ end
451
+
452
+ KNOWN_FILTERS = %w(nexus ios_xr cli nxapi_structured)
343
453
 
344
- def self.key_match(key, platform, product_id, cli)
345
- if key[0] == '/' && key[-1] == '/'
346
- # It's a product-id regexp. Does it match our given product_id?
347
- return Regexp.new(key[1..-2]) =~ product_id ? true : false
454
+ def self.key_match(key, platform, product_id, data_formats)
455
+ if KNOWN_PLATFORMS.include?(key)
456
+ return platform_to_filter(key) =~ product_id ? true : false
348
457
  elsif KNOWN_FILTERS.include?(key)
349
- return false if key.match(/cli/) && !cli
350
- return Regexp.new(platform.to_s) =~ key ? true : false
458
+ return true if data_formats && data_formats.include?(key.to_sym)
459
+ return true if key == platform.to_s
460
+ return false
351
461
  else
352
462
  return :unknown
353
463
  end
@@ -355,20 +465,22 @@ module Cisco
355
465
 
356
466
  # Helper method
357
467
  # Given a Hash of command reference data as read from YAML, does:
358
- # - Filter out any API-specific data not applicable to this API
359
- # - Filter any platform-specific data not applicable to this product_id
468
+ # - Delete any platform-specific data not applicable to this platform
469
+ # - Delete any product-specific data not applicable to this product_id
470
+ # - Delete any data-model-specific data not supported by this node
360
471
  # Returns the filtered hash (possibly empty)
361
472
  def self.filter_hash(hash,
362
473
  platform: nil,
363
474
  product_id: nil,
364
- cli: false,
475
+ data_formats: nil,
365
476
  allow_unknown_keys: true)
366
477
  result = {}
367
478
 
368
- exclude = hash.delete('_exclude') || []
479
+ exclude = hash['_exclude'] || []
369
480
  exclude.each do |value|
370
- if key_match(value, platform, product_id, cli) == true
371
- debug 'Exclude this product (#{product_id}, #{value})'
481
+ # We don't allow exclusion by data_format - just platform/product
482
+ if key_match(value, platform, product_id, nil) == true
483
+ debug "Exclude this product (#{product_id}, #{value})"
372
484
  return result
373
485
  end
374
486
  end
@@ -379,10 +491,11 @@ module Cisco
379
491
  regexp_match = false
380
492
 
381
493
  hash.each do |key, value|
494
+ next if key == '_exclude'
382
495
  if CmdRef.keys.include?(key)
383
496
  result[key] = value
384
497
  elsif key != 'else'
385
- match = key_match(key, platform, product_id, cli)
498
+ match = key_match(key, platform, product_id, data_formats)
386
499
  next if match == false
387
500
  if match == :unknown
388
501
  fail "Unrecognized key '#{key}'" unless allow_unknown_keys
@@ -404,9 +517,10 @@ module Cisco
404
517
  result[key] = filter_hash(hash[key],
405
518
  platform: platform,
406
519
  product_id: product_id,
407
- cli: cli,
520
+ data_formats: data_formats,
408
521
  allow_unknown_keys: false)
409
522
  rescue RuntimeError => e
523
+ # Recursively wrap the error as needed to provide context
410
524
  raise "[#{key}]: #{e}"
411
525
  end
412
526
  end
@@ -415,18 +529,17 @@ module Cisco
415
529
 
416
530
  def filter_hash(input_hash)
417
531
  CommandReference.filter_hash(input_hash,
418
- platform: platform,
419
- product_id: product_id,
420
- cli: cli)
532
+ platform: platform,
533
+ product_id: product_id,
534
+ data_formats: data_formats)
421
535
  end
422
536
 
423
537
  # Helper method
424
538
  # Given a suitably filtered Hash of command reference data, does:
425
539
  # - Inherit data from the given base_hash (if any) and extend/override it
426
540
  # with the given input data.
427
- # - Append 'config_set_append' data to any existing 'config_set' data
428
- # - Append 'config_get_token_append' data to 'config_get_token', ditto
429
541
  def self.hash_merge(input_hash, base_hash=nil)
542
+ return base_hash if input_hash.nil?
430
543
  result = base_hash
431
544
  result ||= {}
432
545
  # to_inspect: sub-hashes we want to recurse into
@@ -434,16 +547,11 @@ module Cisco
434
547
 
435
548
  input_hash.each do |key, value|
436
549
  if CmdRef.keys.include?(key)
437
- if key == 'config_set_append'
438
- result['config_set'] = value_append(result['config_set'], value)
439
- elsif key == 'config_get_token_append'
440
- result['config_get_token'] = value_append(
441
- result['config_get_token'], value)
442
- else
443
- result[key] = value
444
- end
550
+ result[key] = value
445
551
  elsif value.is_a?(Hash)
446
552
  to_inspect << value
553
+ elsif value.nil?
554
+ next
447
555
  else
448
556
  fail "Unexpected non-hash data: #{value}"
449
557
  end
@@ -579,7 +687,18 @@ module Cisco
579
687
  end
580
688
 
581
689
  def to_s
582
- @hash.each_value { |names| names.each_value(&:to_s) }
690
+ @num_features ||= @hash.values.length
691
+ @num_attributes ||= @hash.values.inject(0) do |sum, n|
692
+ sum + (n.is_a?(Hash) ? n.values.length : 1)
693
+ end
694
+ "CommandReference describing #{@num_features} features " \
695
+ "with #{@num_attributes} attributes in total"
696
+ end
697
+
698
+ def inspect
699
+ "CommandReference for '#{product_id}' " \
700
+ "(platform:'#{platform}', data formats:#{data_formats}) " \
701
+ "based on #{files.length} files"
583
702
  end
584
703
  end
585
704
  end