cisco_node_utils 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +4 -1
  6. data/CHANGELOG.md +81 -2
  7. data/CONTRIBUTING.md +2 -17
  8. data/Gemfile +5 -0
  9. data/README.md +92 -47
  10. data/Rakefile +23 -1
  11. data/bin/git/hooks/hook_lib +7 -0
  12. data/bin/git/hooks/pre-commit/check_unstaged_changes +18 -0
  13. data/bin/git/hooks/pre-commit/rubocop +7 -2
  14. data/bin/git/hooks/pre-commit/validate-diffs +18 -4
  15. data/bin/git/hooks/pre-commit/validate-yaml +18 -0
  16. data/bin/git/update-hooks +64 -6
  17. data/cisco_node_utils.gemspec +9 -6
  18. data/docs/README-develop-best-practices.md +149 -50
  19. data/docs/README-develop-node-utils-APIs.md +92 -42
  20. data/docs/README-maintainers.md +7 -4
  21. data/docs/README-test-execution.md +57 -0
  22. data/docs/cisco_node_utils.yaml.example +30 -0
  23. data/docs/template-router.rb +4 -0
  24. data/ext/mkrf_conf.rb +63 -0
  25. data/lib/.rubocop.yml +2 -2
  26. data/lib/cisco_node_utils.rb +5 -0
  27. data/lib/cisco_node_utils/aaa_authentication_login.rb +5 -6
  28. data/lib/cisco_node_utils/aaa_authorization_service.rb +1 -1
  29. data/lib/cisco_node_utils/ace.rb +165 -12
  30. data/lib/cisco_node_utils/acl.rb +2 -1
  31. data/lib/cisco_node_utils/bgp.rb +184 -21
  32. data/lib/cisco_node_utils/bgp_af.rb +94 -249
  33. data/lib/cisco_node_utils/bgp_neighbor.rb +94 -14
  34. data/lib/cisco_node_utils/bgp_neighbor_af.rb +75 -8
  35. data/lib/cisco_node_utils/bridge_domain.rb +183 -0
  36. data/lib/cisco_node_utils/bridge_domain_vni.rb +206 -0
  37. data/lib/cisco_node_utils/cisco_cmn_utils.rb +85 -2
  38. data/lib/cisco_node_utils/client.rb +35 -0
  39. data/lib/cisco_node_utils/client/client.rb +234 -0
  40. data/lib/cisco_node_utils/client/grpc.rb +33 -0
  41. data/lib/cisco_node_utils/client/grpc/client.rb +311 -0
  42. data/lib/cisco_node_utils/client/grpc/ems.proto +148 -0
  43. data/lib/cisco_node_utils/client/grpc/ems.rb +111 -0
  44. data/lib/cisco_node_utils/client/grpc/ems_services.rb +49 -0
  45. data/lib/cisco_node_utils/client/nxapi.rb +31 -0
  46. data/lib/cisco_node_utils/client/nxapi/client.rb +305 -0
  47. data/lib/cisco_node_utils/client/utils.rb +164 -0
  48. data/lib/cisco_node_utils/cmd_ref/README_YAML.md +222 -254
  49. data/lib/cisco_node_utils/cmd_ref/aaa_auth_login_service.yaml +11 -8
  50. data/lib/cisco_node_utils/cmd_ref/aaa_authentication_login.yaml +22 -15
  51. data/lib/cisco_node_utils/cmd_ref/aaa_authorization_service.yaml +11 -8
  52. data/lib/cisco_node_utils/cmd_ref/acl.yaml +21 -16
  53. data/lib/cisco_node_utils/cmd_ref/bgp.yaml +239 -109
  54. data/lib/cisco_node_utils/cmd_ref/bgp_af.yaml +114 -55
  55. data/lib/cisco_node_utils/cmd_ref/bgp_neighbor.yaml +76 -52
  56. data/lib/cisco_node_utils/cmd_ref/bgp_neighbor_af.yaml +106 -62
  57. data/lib/cisco_node_utils/cmd_ref/bridge_domain.yaml +71 -0
  58. data/lib/cisco_node_utils/cmd_ref/bridge_domain_vni.yaml +33 -0
  59. data/lib/cisco_node_utils/cmd_ref/dnsclient.yaml +35 -14
  60. data/lib/cisco_node_utils/cmd_ref/encapsulation.yaml +25 -0
  61. data/lib/cisco_node_utils/cmd_ref/evpn_vni.yaml +23 -17
  62. data/lib/cisco_node_utils/cmd_ref/fabricpath.yaml +94 -83
  63. data/lib/cisco_node_utils/cmd_ref/fabricpath_topology.yaml +22 -17
  64. data/lib/cisco_node_utils/cmd_ref/feature.yaml +76 -26
  65. data/lib/cisco_node_utils/cmd_ref/images.yaml +3 -2
  66. data/lib/cisco_node_utils/cmd_ref/interface.yaml +381 -153
  67. data/lib/cisco_node_utils/cmd_ref/interface_channel_group.yaml +21 -11
  68. data/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml +21 -21
  69. data/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml +30 -21
  70. data/lib/cisco_node_utils/cmd_ref/interface_service_vni.yaml +18 -13
  71. data/lib/cisco_node_utils/cmd_ref/inventory.yaml +26 -31
  72. data/lib/cisco_node_utils/cmd_ref/itd_device_group.yaml +83 -0
  73. data/lib/cisco_node_utils/cmd_ref/itd_service.yaml +119 -0
  74. data/lib/cisco_node_utils/cmd_ref/memory.yaml +17 -6
  75. data/lib/cisco_node_utils/cmd_ref/ntp_config.yaml +10 -3
  76. data/lib/cisco_node_utils/cmd_ref/ntp_server.yaml +17 -5
  77. data/lib/cisco_node_utils/cmd_ref/ospf.yaml +33 -29
  78. data/lib/cisco_node_utils/cmd_ref/overlay_global.yaml +12 -10
  79. data/lib/cisco_node_utils/cmd_ref/pim.yaml +16 -19
  80. data/lib/cisco_node_utils/cmd_ref/portchannel_global.yaml +40 -25
  81. data/lib/cisco_node_utils/cmd_ref/radius_global.yaml +17 -12
  82. data/lib/cisco_node_utils/cmd_ref/radius_server.yaml +71 -35
  83. data/lib/cisco_node_utils/cmd_ref/radius_server_group.yaml +10 -5
  84. data/lib/cisco_node_utils/cmd_ref/show_system.yaml +6 -2
  85. data/lib/cisco_node_utils/cmd_ref/show_version.yaml +47 -43
  86. data/lib/cisco_node_utils/cmd_ref/snmp_community.yaml +13 -11
  87. data/lib/cisco_node_utils/cmd_ref/snmp_group.yaml +4 -2
  88. data/lib/cisco_node_utils/cmd_ref/snmp_notification_receiver.yaml +23 -21
  89. data/lib/cisco_node_utils/cmd_ref/snmp_server.yaml +26 -22
  90. data/lib/cisco_node_utils/cmd_ref/snmp_user.yaml +19 -17
  91. data/lib/cisco_node_utils/cmd_ref/snmpnotification.yaml +18 -6
  92. data/lib/cisco_node_utils/cmd_ref/stp_global.yaml +234 -0
  93. data/lib/cisco_node_utils/cmd_ref/syslog_server.yaml +24 -9
  94. data/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml +5 -3
  95. data/lib/cisco_node_utils/cmd_ref/system.yaml +4 -3
  96. data/lib/cisco_node_utils/cmd_ref/tacacs_server.yaml +22 -20
  97. data/lib/cisco_node_utils/cmd_ref/tacacs_server_group.yaml +27 -15
  98. data/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml +45 -16
  99. data/lib/cisco_node_utils/cmd_ref/vdc.yaml +21 -11
  100. data/lib/cisco_node_utils/cmd_ref/virtual_service.yaml +3 -2
  101. data/lib/cisco_node_utils/cmd_ref/vlan.yaml +60 -32
  102. data/lib/cisco_node_utils/cmd_ref/vpc.yaml +118 -101
  103. data/lib/cisco_node_utils/cmd_ref/vrf.yaml +54 -58
  104. data/lib/cisco_node_utils/cmd_ref/vrf_af.yaml +118 -0
  105. data/lib/cisco_node_utils/cmd_ref/vtp.yaml +19 -25
  106. data/lib/cisco_node_utils/cmd_ref/vxlan_vtep.yaml +28 -18
  107. data/lib/cisco_node_utils/cmd_ref/vxlan_vtep_vni.yaml +34 -17
  108. data/lib/cisco_node_utils/cmd_ref/yum.yaml +6 -4
  109. data/lib/cisco_node_utils/command_reference.rb +261 -142
  110. data/lib/cisco_node_utils/constants.rb +33 -0
  111. data/lib/cisco_node_utils/encapsulation.rb +112 -0
  112. data/lib/cisco_node_utils/environment.rb +102 -0
  113. data/lib/cisco_node_utils/evpn_vni.rb +5 -3
  114. data/lib/cisco_node_utils/exceptions.rb +111 -0
  115. data/lib/cisco_node_utils/fabricpath_global.rb +52 -35
  116. data/lib/cisco_node_utils/fabricpath_topology.rb +44 -57
  117. data/lib/cisco_node_utils/feature.rb +165 -3
  118. data/lib/cisco_node_utils/interface.rb +1051 -260
  119. data/lib/cisco_node_utils/interface_channel_group.rb +11 -10
  120. data/lib/cisco_node_utils/interface_ospf.rb +1 -2
  121. data/lib/cisco_node_utils/interface_portchannel.rb +4 -12
  122. data/lib/cisco_node_utils/interface_service_vni.rb +7 -7
  123. data/lib/cisco_node_utils/itd_device_group.rb +248 -0
  124. data/lib/cisco_node_utils/itd_device_group_node.rb +144 -0
  125. data/lib/cisco_node_utils/itd_service.rb +523 -0
  126. data/lib/cisco_node_utils/logger.rb +75 -0
  127. data/lib/cisco_node_utils/node.rb +62 -192
  128. data/lib/cisco_node_utils/node_util.rb +56 -10
  129. data/lib/cisco_node_utils/overlay_global.rb +2 -2
  130. data/lib/cisco_node_utils/pim.rb +2 -13
  131. data/lib/cisco_node_utils/pim_group_list.rb +1 -1
  132. data/lib/cisco_node_utils/pim_rp_address.rb +1 -1
  133. data/lib/cisco_node_utils/platform.rb +52 -21
  134. data/lib/cisco_node_utils/portchannel_global.rb +89 -19
  135. data/lib/cisco_node_utils/radius_server.rb +168 -37
  136. data/lib/cisco_node_utils/router_ospf.rb +20 -35
  137. data/lib/cisco_node_utils/router_ospf_vrf.rb +4 -4
  138. data/lib/cisco_node_utils/snmpserver.rb +1 -6
  139. data/lib/cisco_node_utils/snmpuser.rb +6 -4
  140. data/lib/cisco_node_utils/stp_global.rb +676 -0
  141. data/lib/cisco_node_utils/syslog_server.rb +77 -18
  142. data/lib/cisco_node_utils/syslog_settings.rb +1 -1
  143. data/lib/cisco_node_utils/tacacs_server_group.rb +8 -4
  144. data/lib/cisco_node_utils/tacacs_server_host.rb +115 -25
  145. data/lib/cisco_node_utils/vdc.rb +12 -0
  146. data/lib/cisco_node_utils/version.rb +1 -1
  147. data/lib/cisco_node_utils/vlan.rb +147 -29
  148. data/lib/cisco_node_utils/vpc.rb +55 -3
  149. data/lib/cisco_node_utils/vrf.rb +72 -11
  150. data/lib/cisco_node_utils/vrf_af.rb +114 -29
  151. data/lib/cisco_node_utils/vtp.rb +34 -52
  152. data/lib/cisco_node_utils/vxlan_vtep.rb +34 -8
  153. data/lib/cisco_node_utils/vxlan_vtep_vni.rb +36 -4
  154. data/lib/minitest/environment_plugin.rb +31 -0
  155. data/lib/minitest/log_level_plugin.rb +41 -0
  156. data/spec/client_spec.rb +7 -0
  157. data/spec/environment_spec.rb +263 -0
  158. data/spec/grpc_client_spec.rb +23 -0
  159. data/spec/isolate/all_clients_spec.rb +9 -0
  160. data/spec/isolate/grpc_only_spec.rb +16 -0
  161. data/spec/isolate/no_clients_spec.rb +26 -0
  162. data/spec/isolate/nxapi_only_spec.rb +16 -0
  163. data/spec/nxapi_client_spec.rb +42 -0
  164. data/spec/schema.yaml +75 -0
  165. data/spec/shared_examples_for_clients.rb +14 -0
  166. data/spec/spec_helper.rb +91 -0
  167. data/spec/whitespace_spec.rb +10 -0
  168. data/spec/yaml_spec.rb +42 -0
  169. data/tests/.rubocop.yml +2 -2
  170. data/tests/CSCuxdublin-1.0.0-7.0.3.I3.1.lib32_n9000.rpm +0 -0
  171. data/tests/basetest.rb +96 -36
  172. data/tests/ciscotest.rb +220 -12
  173. data/tests/cmd_config.yaml +71 -49
  174. data/tests/cmd_config_invalid.yaml +1 -1
  175. data/tests/test_aaa_authentication_login.rb +1 -0
  176. data/tests/test_aaa_authentication_login_service.rb +9 -0
  177. data/tests/test_aaa_authorization_service.rb +173 -367
  178. data/tests/test_ace.rb +171 -100
  179. data/tests/test_acl.rb +10 -1
  180. data/tests/test_bgp_af.rb +395 -728
  181. data/tests/test_bgp_neighbor.rb +274 -115
  182. data/tests/test_bgp_neighbor_af.rb +178 -77
  183. data/tests/test_bridge_domain.rb +191 -0
  184. data/tests/test_bridge_domain_vni.rb +116 -0
  185. data/tests/test_client_utils.rb +111 -0
  186. data/tests/test_command_config.rb +9 -5
  187. data/tests/test_command_reference.rb +380 -102
  188. data/tests/test_dns_domain.rb +13 -3
  189. data/tests/test_domain_name.rb +13 -3
  190. data/tests/test_encapsulation.rb +77 -0
  191. data/tests/test_evpn_vni.rb +25 -7
  192. data/tests/test_fabricpath_global.rb +167 -163
  193. data/tests/test_fabricpath_topology.rb +12 -33
  194. data/tests/test_feature.rb +215 -0
  195. data/tests/test_grpc.rb +166 -0
  196. data/tests/test_interface.rb +585 -344
  197. data/tests/test_interface_bdi.rb +80 -0
  198. data/tests/test_interface_channel_group.rb +6 -3
  199. data/tests/test_interface_ospf.rb +26 -24
  200. data/tests/test_interface_portchannel.rb +1 -0
  201. data/tests/test_interface_private_vlan.rb +724 -0
  202. data/tests/test_interface_service_vni.rb +37 -66
  203. data/tests/test_interface_svi.rb +98 -101
  204. data/tests/test_interface_switchport.rb +419 -549
  205. data/tests/test_itd_device_group.rb +145 -0
  206. data/tests/test_itd_device_group_node.rb +199 -0
  207. data/tests/test_itd_service.rb +298 -0
  208. data/tests/test_logger.rb +43 -0
  209. data/tests/test_name_server.rb +11 -2
  210. data/tests/test_node.rb +16 -75
  211. data/tests/test_node_ext.rb +174 -163
  212. data/tests/test_node_util.rb +119 -0
  213. data/tests/test_ntp_config.rb +5 -1
  214. data/tests/test_ntp_server.rb +2 -2
  215. data/tests/test_nxapi.rb +221 -0
  216. data/tests/test_overlay_global.rb +47 -38
  217. data/tests/test_pim.rb +2 -0
  218. data/tests/test_pim_group_list.rb +2 -0
  219. data/tests/test_pim_rp_address.rb +2 -0
  220. data/tests/test_platform.rb +86 -39
  221. data/tests/test_portchannel_global.rb +211 -135
  222. data/tests/test_radius_global.rb +13 -5
  223. data/tests/test_radius_server.rb +256 -104
  224. data/tests/test_radius_server_group.rb +2 -0
  225. data/tests/test_router_bgp.rb +781 -485
  226. data/tests/test_router_ospf.rb +26 -103
  227. data/tests/test_router_ospf_vrf.rb +52 -57
  228. data/tests/test_snmp_notification_receiver.rb +2 -0
  229. data/tests/test_snmpcommunity.rb +2 -0
  230. data/tests/test_snmpgroup.rb +2 -0
  231. data/tests/test_snmpnotification.rb +40 -21
  232. data/tests/test_snmpserver.rb +2 -0
  233. data/tests/test_snmpuser.rb +2 -0
  234. data/tests/test_stp_global.rb +563 -0
  235. data/tests/test_syslog_server.rb +32 -8
  236. data/tests/test_syslog_settings.rb +22 -9
  237. data/tests/test_tacacs_server.rb +32 -27
  238. data/tests/test_tacacs_server_group.rb +100 -45
  239. data/tests/test_tacacs_server_host.rb +135 -43
  240. data/tests/test_vdc.rb +2 -16
  241. data/tests/test_vlan.rb +106 -54
  242. data/tests/test_vlan_mt_full.rb +11 -21
  243. data/tests/test_vlan_private.rb +669 -0
  244. data/tests/test_vpc.rb +312 -159
  245. data/tests/test_vrf.rb +122 -113
  246. data/tests/test_vrf_af.rb +238 -0
  247. data/tests/test_vtp.rb +58 -102
  248. data/tests/test_vxlan_vtep.rb +38 -17
  249. data/tests/test_vxlan_vtep_vni.rb +61 -9
  250. data/tests/test_yum.rb +49 -25
  251. metadata +122 -36
  252. data/lib/cisco_node_utils/cmd_ref/fex.yaml +0 -9
  253. data/lib/cisco_node_utils/cmd_ref/vni.yaml +0 -76
  254. data/lib/cisco_node_utils/vni.rb +0 -227
  255. data/tests/test_vni.rb +0 -106
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2015 Cisco and/or its affiliates.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative 'client'
16
+
17
+ # Fail gracefully if submodule dependencies are not met
18
+ begin
19
+ Cisco::Client.silence_warnings do
20
+ require 'grpc'
21
+ end
22
+ rescue LoadError => e
23
+ raise unless e.message =~ /-- grpc/
24
+ # If grpc is not installed, raise an error that client understands.
25
+ raise LoadError, "Unable to load client/grpc -- #{e}"
26
+ end
27
+
28
+ # Namespace for Cisco EMS gRPC-specific code
29
+ class Cisco::Client::GRPC < Cisco::Client
30
+ end
31
+
32
+ # Auto-load all Ruby files in the subdirectory
33
+ Dir.glob(__dir__ + '/grpc/*.rb') { |file| require file }
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # October 2015, Glenn F. Matthews
4
+ #
5
+ # Copyright (c) 2015-2016 Cisco and/or its affiliates.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative '../client'
20
+ Cisco::Client.silence_warnings do
21
+ require 'grpc'
22
+ end
23
+ require 'json'
24
+ require_relative 'ems_services'
25
+
26
+ include IOSXRExtensibleManagabilityService
27
+ include Cisco::Logger
28
+
29
+ # Client implementation using gRPC API for IOS XR
30
+ class Cisco::Client::GRPC < Cisco::Client
31
+ register_client(self)
32
+
33
+ attr_accessor :timeout
34
+
35
+ def initialize(**kwargs)
36
+ # Defaults for gRPC:
37
+ kwargs[:host] ||= '127.0.0.1'
38
+ kwargs[:port] ||= 57_400
39
+ # rubocop:disable Style/HashSyntax
40
+ super(data_formats: [:cli],
41
+ platform: :ios_xr,
42
+ **kwargs)
43
+ # rubocop:enable Style/HashSyntax
44
+ @config = GRPCConfigOper::Stub.new(@address, :this_channel_is_insecure)
45
+ @exec = GRPCExec::Stub.new(@address, :this_channel_is_insecure)
46
+
47
+ # Make sure we can actually connect
48
+ @timeout = 10
49
+ begin
50
+ base_msg = 'gRPC client creation failure: '
51
+ get(command: 'show clock')
52
+ rescue Cisco::ClientError => e
53
+ error 'initial connect failed: ' + e.to_s
54
+ if e.message[/deadline exceeded/i]
55
+ raise Cisco::ConnectionRefused, \
56
+ base_msg + 'timed out during initial connection: ' + e.message
57
+ end
58
+ raise e.class, base_msg + e.message
59
+ end
60
+
61
+ # Let commands in general take up to 2 minutes
62
+ @timeout = 120
63
+ end
64
+
65
+ def self.validate_args(**kwargs)
66
+ super
67
+ base_msg = 'gRPC client creation failure: '
68
+ # Connection to remote system - username and password are required
69
+ fail TypeError, base_msg + 'username must be specified' \
70
+ if kwargs[:username].nil?
71
+ fail TypeError, base_msg + 'password must be specified' \
72
+ if kwargs[:password].nil?
73
+ end
74
+
75
+ def cache_flush
76
+ @cache_hash = {
77
+ 'cli_config' => {},
78
+ 'show_cmd_text_output' => {},
79
+ 'show_cmd_json_output' => {},
80
+ }
81
+ end
82
+
83
+ # Configure the given CLI command(s) on the device.
84
+ #
85
+ # @param data_format one of Cisco::DATA_FORMATS. Default is :cli
86
+ # @param context [Array<String>] Zero or more configuration commands used
87
+ # to enter the desired CLI sub-mode
88
+ # @param values [Array<String>] One or more commands to enter within the
89
+ # CLI sub-mode.
90
+ def set(data_format: :cli,
91
+ context: nil,
92
+ values: nil)
93
+ context = munge_to_array(context)
94
+ values = munge_to_array(values)
95
+ super
96
+ # IOS XR lets us concatenate submode commands together.
97
+ # This makes it possible to guarantee we are in the correct context:
98
+ # context: ['foo', 'bar'], values: ['baz', 'bat']
99
+ # ---> values: ['foo bar baz', 'foo bar bat']
100
+ # However, there's a special case for 'no' commands:
101
+ # context: ['foo', 'bar'], values: ['no baz']
102
+ # ---> values: ['no foo bar baz'] ---- the 'no' goes at the start
103
+ context = context.join(' ')
104
+ unless context.empty?
105
+ values.map! do |cmd|
106
+ match = cmd[/^\s*no\s+(.*)/, 1]
107
+ if match
108
+ cmd = "no #{context} #{match}"
109
+ else
110
+ cmd = "#{context} #{cmd}"
111
+ end
112
+ cmd
113
+ end
114
+ end
115
+ # CliConfigArgs wants a newline-separated string of commands
116
+ args = CliConfigArgs.new(cli: values.join("\n"))
117
+ req(@config, 'cli_config', args)
118
+ end
119
+
120
+ def get(data_format: :cli,
121
+ command: nil,
122
+ context: nil,
123
+ value: nil)
124
+ super
125
+ fail ArgumentError if command.nil?
126
+ args = ShowCmdArgs.new(cli: command)
127
+ output = req(@exec, 'show_cmd_text_output', args)
128
+ self.class.filter_cli(cli_output: output, context: context, value: value)
129
+ end
130
+
131
+ def req(stub, type, args)
132
+ if cache_enable? && @cache_hash[type] && @cache_hash[type][args.cli]
133
+ return @cache_hash[type][args.cli]
134
+ end
135
+
136
+ debug "Sending '#{type}' request:"
137
+ if args.is_a?(ShowCmdArgs) || args.is_a?(CliConfigArgs)
138
+ debug " with cli: '#{args.cli}'"
139
+ end
140
+ output = Cisco::Client.silence_warnings do
141
+ response = stub.send(type, args,
142
+ timeout: @timeout,
143
+ username: @username,
144
+ password: @password)
145
+ # gRPC server may split the response into multiples
146
+ response = response.is_a?(Enumerator) ? response.to_a : [response]
147
+ debug "Got responses: #{response.map(&:class).join(', ')}"
148
+ # Check for errors first
149
+ handle_errors(args, response.select { |r| !r.errors.empty? })
150
+
151
+ # If we got here, no errors occurred
152
+ handle_response(args, response)
153
+ end
154
+
155
+ @cache_hash[type][args.cli] = output if cache_enable? && !output.empty?
156
+ return output
157
+ rescue ::GRPC::BadStatus => e
158
+ warn "gRPC error '#{e.code}' during '#{type}' request: "
159
+ if args.is_a?(ShowCmdArgs) || args.is_a?(CliConfigArgs)
160
+ warn " with cli: '#{args.cli}'"
161
+ end
162
+ warn " '#{e.details}'"
163
+ case e.code
164
+ when ::GRPC::Core::StatusCodes::UNAVAILABLE
165
+ raise Cisco::ConnectionRefused, "Connection refused: #{e.details}"
166
+ when ::GRPC::Core::StatusCodes::UNAUTHENTICATED
167
+ raise Cisco::AuthenticationFailed, e.details
168
+ else
169
+ raise Cisco::ClientError, e.details
170
+ end
171
+ end
172
+
173
+ def handle_response(args, replies)
174
+ klass = replies[0].class
175
+ unless replies.all? { |r| r.class == klass }
176
+ fail Cisco::ClientError, 'reply class inconsistent: ' +
177
+ replies.map(&:class).join(', ')
178
+ end
179
+ debug "Handling #{replies.length} '#{klass}' reply(s):"
180
+ case klass.to_s
181
+ when /ShowCmdTextReply/
182
+ replies.each { |r| debug " output:\n#{r.output}" }
183
+ output = replies.map(&:output).join('')
184
+ output = handle_text_output(args, output)
185
+ when /ShowCmdJSONReply/
186
+ # TODO: not yet supported by server to test against
187
+ replies.each { |r| debug " jsonoutput:\n#{r.jsonoutput}" }
188
+ output = replies.map(&:jsonoutput).join("\n---\n")
189
+ when /CliConfigReply/
190
+ # nothing to process
191
+ output = ''
192
+ else
193
+ fail Cisco::ClientError, "unsupported reply class #{klass}"
194
+ end
195
+ debug "Success with output:\n#{output}"
196
+ output
197
+ end
198
+
199
+ def handle_text_output(args, output)
200
+ # For a successful show command, gRPC presents the output as:
201
+ # \n--------- <cmd> ----------
202
+ # \n<output of command>
203
+ # \n\n
204
+
205
+ # For an invalid CLI, gRPC presents the output as:
206
+ # \n--------- <cmd> --------
207
+ # \n<cmd>
208
+ # \n<error output>
209
+ # \n\n
210
+
211
+ # Discard the leading whitespace, header, and trailing whitespace
212
+ output = output.split("\n").drop(2)
213
+ return '' if output.nil? || output.empty?
214
+
215
+ # Now we have either [<output_line_1>, <output_line_2>, ...] or
216
+ # [<cmd>, <error_line_1>, <error_line_2>, ...]
217
+ if output[0].strip == args.cli.strip
218
+ fail Cisco::CliError.new( # rubocop:disable Style/RaiseArgs
219
+ rejected_input: args.cli,
220
+ clierror: output.join("\n"),
221
+ )
222
+ end
223
+ output.join("\n")
224
+ end
225
+
226
+ def handle_errors(args, error_responses)
227
+ return if error_responses.empty?
228
+ debug "#{error_responses.length} response(s) had errors:"
229
+ error_responses.each { |r| debug " error:\n#{r.errors}" }
230
+ first_error = error_responses.first.errors
231
+ # Conveniently for us, all *Reply protobufs in EMS have an errors field
232
+ # Less conveniently, some are JSON and some are not.
233
+ begin
234
+ msg = JSON.parse(first_error)
235
+ handle_json_error(msg)
236
+ rescue JSON::ParserError
237
+ handle_text_error(args, first_error)
238
+ end
239
+ end
240
+
241
+ # Generate an error from a failed request
242
+ def handle_text_error(args, msg)
243
+ if /^Disallowed commands:/ =~ msg
244
+ fail Cisco::RequestNotSupported, msg
245
+ else
246
+ fail Cisco::CliError.new( # rubocop:disable Style/RaiseArgs
247
+ rejected_input: args.cli,
248
+ clierror: msg,
249
+ )
250
+ end
251
+ end
252
+
253
+ # Generate a CliError from a failed CliConfigReply
254
+ def handle_json_error(msg)
255
+ # {
256
+ # "cisco-grpc:errors": {
257
+ # "error": [
258
+ # {
259
+ # "error-type": "application",
260
+ # "error-tag": "operation-failed",
261
+ # "error-severity": "error",
262
+ # "error-message": "....",
263
+ # },
264
+ # {
265
+ # ...
266
+
267
+ # {
268
+ # "cisco-grpc:errors": [
269
+ # {
270
+ # "error-type": "protocol",
271
+ # "error-message": "Failed authentication"
272
+ # }
273
+ # ]
274
+ # }
275
+
276
+ msg = msg['cisco-grpc:errors']
277
+ msg = msg['error'] unless msg.is_a?(Array)
278
+ msg.each do |m|
279
+ type = m['error-type']
280
+ message = m['error-message']
281
+ if type == 'protocol' && message == 'Failed authentication'
282
+ fail Cisco::AuthenticationFailed, message
283
+ elsif type == 'application'
284
+ # Example message:
285
+ # !! SYNTAX/AUTHORIZATION ERRORS: This configuration failed due to
286
+ # !! one or more of the following reasons:
287
+ # !! - the entered commands do not exist,
288
+ # !! - the entered commands have errors in their syntax,
289
+ # !! - the software packages containing the commands are not active,
290
+ # !! - the current user is not a member of a task-group that has
291
+ # !! permissions to use the commands.
292
+ #
293
+ # foo
294
+ # bar
295
+ #
296
+ match = /\n\n(.*)\n\n\Z/m.match(message)
297
+ if match.nil?
298
+ rejected = '(unknown, see error message)'
299
+ else
300
+ rejected = match[1].split("\n")
301
+ end
302
+ fail Cisco::CliError.new( # rubocop:disable Style/RaiseArgs
303
+ rejected_input: rejected,
304
+ clierror: message,
305
+ )
306
+ else
307
+ fail Cisco::ClientError, message
308
+ end
309
+ end
310
+ end
311
+ end
@@ -0,0 +1,148 @@
1
+ syntax = "proto3";
2
+
3
+ package IOSXRExtensibleManagabilityService;
4
+
5
+ service gRPCConfigOper {
6
+
7
+ // Configuration related commands
8
+
9
+ rpc GetConfig(ConfigGetArgs) returns(stream ConfigGetReply) {};
10
+ // 6.0 Config commands do implicit commits
11
+ rpc MergeConfig(ConfigArgs) returns(ConfigReply) {};
12
+
13
+ rpc DeleteConfig(ConfigArgs) returns(ConfigReply) {};
14
+
15
+ rpc ReplaceConfig(ConfigArgs) returns(ConfigReply) {};
16
+
17
+ rpc CliConfig(CliConfigArgs) returns(CliConfigReply) {};
18
+
19
+ // not implemented for 6.0
20
+ rpc CommitReplace(CommitReplaceArgs) returns (CommitReplaceReply) {};
21
+ // not implemented for 6.0
22
+ rpc CommitConfig(CommitArgs) returns(CommitReply) {};
23
+ // not implemented for 6.0
24
+ rpc ConfigDiscardChanges(DiscardChangesArgs) returns(DiscardChangesReply) {};
25
+
26
+
27
+ // Get only returns oper data
28
+ //
29
+ rpc GetOper(GetOperArgs) returns(stream GetOperReply) {};
30
+ // Do we need "Get" also to give combined oper and config?
31
+ }
32
+
33
+ //
34
+ // Should we seperate Exec from Config/Oper?
35
+ //
36
+
37
+ service gRPCExec {
38
+ // Exec commands
39
+ rpc ShowCmdTextOutput(ShowCmdArgs) returns(stream ShowCmdTextReply) {};
40
+ rpc ShowCmdJSONOutput(ShowCmdArgs) returns(stream ShowCmdJSONReply) {};
41
+
42
+
43
+ }
44
+
45
+ message ConfigGetArgs {
46
+ int64 ReqId = 1;
47
+ string yangpathjson = 2;
48
+ }
49
+
50
+ message ConfigGetReply {
51
+ int64 ResReqId = 1;
52
+ string yangjson = 2;
53
+ string errors = 3;
54
+ }
55
+
56
+ message GetOperArgs {
57
+ int64 ReqId = 1;
58
+ string yangpathjson = 2;
59
+ }
60
+
61
+ message GetOperReply {
62
+ int64 ResReqId = 1;
63
+ string yangjson = 2;
64
+ string errors = 3;
65
+ }
66
+
67
+
68
+ message ConfigArgs {
69
+ int64 ReqId = 1;
70
+ string yangjson = 2;
71
+
72
+ }
73
+
74
+ message ConfigReply {
75
+ int64 ResReqId = 1;
76
+ string errors = 2;
77
+ }
78
+
79
+ message CliConfigArgs {
80
+ int64 ReqId = 1;
81
+ string cli = 2;
82
+ }
83
+
84
+ message CliConfigReply {
85
+ int64 ResReqId = 1;
86
+ string errors = 2;
87
+ }
88
+
89
+
90
+ message CommitReplaceArgs {
91
+ int64 ReqId = 1;
92
+ string cli = 2;
93
+ string yangjson = 3;
94
+ }
95
+
96
+ message CommitReplaceReply {
97
+ int64 ResReqId = 1;
98
+ string errors = 2;
99
+ }
100
+
101
+ message CommitMsg {
102
+ string label = 1;
103
+ string comment = 2;
104
+ }
105
+
106
+ enum CommitResult {
107
+ CHANGE = 0;
108
+ NO_CHANGE = 1;
109
+ FAIL = 2;
110
+ }
111
+
112
+ message CommitArgs {
113
+ CommitMsg msg = 1;
114
+ int64 ReqId = 2;
115
+ }
116
+
117
+ message CommitReply {
118
+ CommitResult result = 1;
119
+ int64 ResReqId = 2;
120
+ string errors = 3;
121
+ }
122
+
123
+
124
+ message DiscardChangesArgs {
125
+ int64 ReqId = 1;
126
+ }
127
+
128
+ message DiscardChangesReply {
129
+ int64 ResReqId = 1;
130
+ string errors = 2;
131
+ }
132
+
133
+
134
+ message ShowCmdArgs {
135
+ int64 ReqId = 1;
136
+ string cli = 2;
137
+ }
138
+
139
+ message ShowCmdTextReply {
140
+ int64 ResReqId =1;
141
+ string output = 2;
142
+ string errors = 3;
143
+ }
144
+ message ShowCmdJSONReply {
145
+ int64 ResReqId =1;
146
+ string jsonoutput = 2;
147
+ string errors = 3;
148
+ }