cisco_node_utils_mgx 2.1.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +96 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +676 -0
- data/CONTRIBUTING.md +43 -0
- data/Gemfile +10 -0
- data/LICENSE +201 -0
- data/README.md +246 -0
- data/Rakefile +44 -0
- data/SUPPORT.md +3 -0
- data/bin/.rubocop.yml +18 -0
- data/bin/check_metric_limits.rb +109 -0
- data/bin/git/hooks/commit-msg/enforce_style +89 -0
- data/bin/git/hooks/hook_lib +115 -0
- data/bin/git/hooks/hooks-wrapper +38 -0
- data/bin/git/hooks/post-flow-hotfix-start/update-version +24 -0
- data/bin/git/hooks/post-flow-release-finish/update-version +29 -0
- data/bin/git/hooks/post-flow-release-start/update-version +19 -0
- data/bin/git/hooks/post-merge/update-hooks +6 -0
- data/bin/git/hooks/post-rewrite/update-hooks +6 -0
- data/bin/git/hooks/pre-commit/check_unstaged_changes +18 -0
- data/bin/git/hooks/pre-commit/rubocop +25 -0
- data/bin/git/hooks/pre-commit/validate-diffs +45 -0
- data/bin/git/hooks/pre-commit/validate-yaml +18 -0
- data/bin/git/hooks/pre-push/check-changelog +24 -0
- data/bin/git/hooks/pre-push/rubocop +7 -0
- data/bin/git/update-hooks +123 -0
- data/bin/show_running_yang.rb +233 -0
- data/cisco_node_utils.gemspec +41 -0
- data/docs/README-develop-best-practices.md +521 -0
- data/docs/README-develop-node-utils-APIs.md +570 -0
- data/docs/README-maintainers.md +77 -0
- data/docs/README-test-execution.md +57 -0
- data/docs/README-utilities.md +14 -0
- data/docs/agent_files.png +0 -0
- data/docs/cisco_node_utils.yaml.example +36 -0
- data/docs/template-router.rb +123 -0
- data/docs/template-test_router.rb +104 -0
- data/ext/mkrf_conf.rb +63 -0
- data/lib/.rubocop.yml +18 -0
- data/lib/cisco_node_utils/aaa_authentication_login.rb +95 -0
- data/lib/cisco_node_utils/aaa_authentication_login_service.rb +138 -0
- data/lib/cisco_node_utils/aaa_authorization_service.rb +156 -0
- data/lib/cisco_node_utils/ace.rb +467 -0
- data/lib/cisco_node_utils/acl.rb +101 -0
- data/lib/cisco_node_utils/banner.rb +63 -0
- data/lib/cisco_node_utils/bfd_global.rb +305 -0
- data/lib/cisco_node_utils/bgp.rb +988 -0
- data/lib/cisco_node_utils/bgp_af.rb +545 -0
- data/lib/cisco_node_utils/bgp_af_aggr_addr.rb +207 -0
- data/lib/cisco_node_utils/bgp_neighbor.rb +527 -0
- data/lib/cisco_node_utils/bgp_neighbor_af.rb +780 -0
- data/lib/cisco_node_utils/bridge_domain.rb +178 -0
- data/lib/cisco_node_utils/bridge_domain_vni.rb +206 -0
- data/lib/cisco_node_utils/cisco_cmn_utils.rb +444 -0
- data/lib/cisco_node_utils/client/client.rb +238 -0
- data/lib/cisco_node_utils/client/grpc/client.rb +395 -0
- data/lib/cisco_node_utils/client/grpc/ems.proto +148 -0
- data/lib/cisco_node_utils/client/grpc/ems.rb +111 -0
- data/lib/cisco_node_utils/client/grpc/ems_services.rb +49 -0
- data/lib/cisco_node_utils/client/grpc.rb +33 -0
- data/lib/cisco_node_utils/client/nxapi/client.rb +368 -0
- data/lib/cisco_node_utils/client/nxapi.rb +31 -0
- data/lib/cisco_node_utils/client/utils.rb +180 -0
- data/lib/cisco_node_utils/client.rb +35 -0
- data/lib/cisco_node_utils/cmd_ref/README_YAML.md +590 -0
- data/lib/cisco_node_utils/cmd_ref/aaa_auth_login_service.yaml +25 -0
- data/lib/cisco_node_utils/cmd_ref/aaa_authentication_login.yaml +38 -0
- data/lib/cisco_node_utils/cmd_ref/aaa_authorization_service.yaml +40 -0
- data/lib/cisco_node_utils/cmd_ref/acl.yaml +48 -0
- data/lib/cisco_node_utils/cmd_ref/banner.yaml +11 -0
- data/lib/cisco_node_utils/cmd_ref/bfd_global.yaml +117 -0
- data/lib/cisco_node_utils/cmd_ref/bgp.yaml +383 -0
- data/lib/cisco_node_utils/cmd_ref/bgp_af.yaml +223 -0
- data/lib/cisco_node_utils/cmd_ref/bgp_af_aa.yaml +38 -0
- data/lib/cisco_node_utils/cmd_ref/bgp_neighbor.yaml +174 -0
- data/lib/cisco_node_utils/cmd_ref/bgp_neighbor_af.yaml +236 -0
- data/lib/cisco_node_utils/cmd_ref/bridge_domain.yaml +49 -0
- data/lib/cisco_node_utils/cmd_ref/bridge_domain_vni.yaml +33 -0
- data/lib/cisco_node_utils/cmd_ref/dhcp_relay_global.yaml +128 -0
- data/lib/cisco_node_utils/cmd_ref/dnsclient.yaml +55 -0
- data/lib/cisco_node_utils/cmd_ref/encapsulation.yaml +25 -0
- data/lib/cisco_node_utils/cmd_ref/evpn_multicast.yaml +12 -0
- data/lib/cisco_node_utils/cmd_ref/evpn_multisite.yaml +18 -0
- data/lib/cisco_node_utils/cmd_ref/evpn_stormcontrol.yaml +18 -0
- data/lib/cisco_node_utils/cmd_ref/evpn_vni.yaml +48 -0
- data/lib/cisco_node_utils/cmd_ref/fabricpath.yaml +183 -0
- data/lib/cisco_node_utils/cmd_ref/fabricpath_topology.yaml +40 -0
- data/lib/cisco_node_utils/cmd_ref/feature.yaml +126 -0
- data/lib/cisco_node_utils/cmd_ref/hostname.yaml +8 -0
- data/lib/cisco_node_utils/cmd_ref/hsrp_global.yaml +25 -0
- data/lib/cisco_node_utils/cmd_ref/images.yaml +8 -0
- data/lib/cisco_node_utils/cmd_ref/interface.yaml +781 -0
- data/lib/cisco_node_utils/cmd_ref/interface_channel_group.yaml +45 -0
- data/lib/cisco_node_utils/cmd_ref/interface_evpn_multisite.yaml +17 -0
- data/lib/cisco_node_utils/cmd_ref/interface_hsrp_group.yaml +120 -0
- data/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml +112 -0
- data/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml +87 -0
- data/lib/cisco_node_utils/cmd_ref/interface_service_vni.yaml +42 -0
- data/lib/cisco_node_utils/cmd_ref/inventory.yaml +45 -0
- data/lib/cisco_node_utils/cmd_ref/ip_multicast.yaml +22 -0
- data/lib/cisco_node_utils/cmd_ref/itd_device_group.yaml +83 -0
- data/lib/cisco_node_utils/cmd_ref/itd_service.yaml +119 -0
- data/lib/cisco_node_utils/cmd_ref/memory.yaml +24 -0
- data/lib/cisco_node_utils/cmd_ref/ntp_auth_key.yaml +10 -0
- data/lib/cisco_node_utils/cmd_ref/ntp_config.yaml +27 -0
- data/lib/cisco_node_utils/cmd_ref/ntp_server.yaml +34 -0
- data/lib/cisco_node_utils/cmd_ref/object_group.yaml +32 -0
- data/lib/cisco_node_utils/cmd_ref/ospf.yaml +91 -0
- data/lib/cisco_node_utils/cmd_ref/ospf_area.yaml +91 -0
- data/lib/cisco_node_utils/cmd_ref/ospf_area_vlink.yaml +88 -0
- data/lib/cisco_node_utils/cmd_ref/overlay_global.yaml +37 -0
- data/lib/cisco_node_utils/cmd_ref/pim.yaml +43 -0
- data/lib/cisco_node_utils/cmd_ref/portchannel_global.yaml +86 -0
- data/lib/cisco_node_utils/cmd_ref/radius_global.yaml +37 -0
- data/lib/cisco_node_utils/cmd_ref/radius_server.yaml +100 -0
- data/lib/cisco_node_utils/cmd_ref/radius_server_group.yaml +19 -0
- data/lib/cisco_node_utils/cmd_ref/route_map.yaml +601 -0
- data/lib/cisco_node_utils/cmd_ref/show_system.yaml +9 -0
- data/lib/cisco_node_utils/cmd_ref/show_version.yaml +84 -0
- data/lib/cisco_node_utils/cmd_ref/snmp_community.yaml +81 -0
- data/lib/cisco_node_utils/cmd_ref/snmp_group.yaml +9 -0
- data/lib/cisco_node_utils/cmd_ref/snmp_notification_receiver.yaml +74 -0
- data/lib/cisco_node_utils/cmd_ref/snmp_server.yaml +91 -0
- data/lib/cisco_node_utils/cmd_ref/snmp_user.yaml +57 -0
- data/lib/cisco_node_utils/cmd_ref/snmpnotification.yaml +23 -0
- data/lib/cisco_node_utils/cmd_ref/span_session.yaml +65 -0
- data/lib/cisco_node_utils/cmd_ref/stp_global.yaml +235 -0
- data/lib/cisco_node_utils/cmd_ref/syslog_facility.yaml +10 -0
- data/lib/cisco_node_utils/cmd_ref/syslog_server.yaml +34 -0
- data/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml +45 -0
- data/lib/cisco_node_utils/cmd_ref/system.yaml +7 -0
- data/lib/cisco_node_utils/cmd_ref/tacacs_global.yaml +37 -0
- data/lib/cisco_node_utils/cmd_ref/tacacs_server.yaml +63 -0
- data/lib/cisco_node_utils/cmd_ref/tacacs_server_group.yaml +45 -0
- data/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml +64 -0
- data/lib/cisco_node_utils/cmd_ref/upgrade.yaml +38 -0
- data/lib/cisco_node_utils/cmd_ref/vdc.yaml +52 -0
- data/lib/cisco_node_utils/cmd_ref/virtual_service.yaml +8 -0
- data/lib/cisco_node_utils/cmd_ref/vlan.yaml +106 -0
- data/lib/cisco_node_utils/cmd_ref/vpc.yaml +233 -0
- data/lib/cisco_node_utils/cmd_ref/vrf.yaml +86 -0
- data/lib/cisco_node_utils/cmd_ref/vrf_af.yaml +139 -0
- data/lib/cisco_node_utils/cmd_ref/vtp.yaml +32 -0
- data/lib/cisco_node_utils/cmd_ref/vxlan_vtep.yaml +114 -0
- data/lib/cisco_node_utils/cmd_ref/vxlan_vtep_vni.yaml +71 -0
- data/lib/cisco_node_utils/cmd_ref/yang.yaml +7 -0
- data/lib/cisco_node_utils/cmd_ref/yum.yaml +68 -0
- data/lib/cisco_node_utils/command_reference.rb +724 -0
- data/lib/cisco_node_utils/configparser_lib.rb +195 -0
- data/lib/cisco_node_utils/constants.rb +40 -0
- data/lib/cisco_node_utils/dhcp_relay_global.rb +302 -0
- data/lib/cisco_node_utils/dns_domain.rb +93 -0
- data/lib/cisco_node_utils/domain_name.rb +82 -0
- data/lib/cisco_node_utils/encapsulation.rb +112 -0
- data/lib/cisco_node_utils/environment.rb +110 -0
- data/lib/cisco_node_utils/evpn_multicast.rb +66 -0
- data/lib/cisco_node_utils/evpn_multisite.rb +96 -0
- data/lib/cisco_node_utils/evpn_stormcontrol.rb +84 -0
- data/lib/cisco_node_utils/evpn_vni.rb +159 -0
- data/lib/cisco_node_utils/exceptions.rb +140 -0
- data/lib/cisco_node_utils/fabricpath_global.rb +405 -0
- data/lib/cisco_node_utils/fabricpath_topology.rb +137 -0
- data/lib/cisco_node_utils/feature.rb +377 -0
- data/lib/cisco_node_utils/hostname.rb +62 -0
- data/lib/cisco_node_utils/hsrp_global.rb +97 -0
- data/lib/cisco_node_utils/interface.rb +2128 -0
- data/lib/cisco_node_utils/interface_channel_group.rb +142 -0
- data/lib/cisco_node_utils/interface_evpn_multisite.rb +72 -0
- data/lib/cisco_node_utils/interface_hsrp_group.rb +557 -0
- data/lib/cisco_node_utils/interface_ospf.rb +378 -0
- data/lib/cisco_node_utils/interface_portchannel.rb +180 -0
- data/lib/cisco_node_utils/interface_service_vni.rb +132 -0
- data/lib/cisco_node_utils/ip_multicast.rb +90 -0
- data/lib/cisco_node_utils/itd_device_group.rb +228 -0
- data/lib/cisco_node_utils/itd_device_group_node.rb +144 -0
- data/lib/cisco_node_utils/itd_service.rb +511 -0
- data/lib/cisco_node_utils/logger.rb +78 -0
- data/lib/cisco_node_utils/name_server.rb +64 -0
- data/lib/cisco_node_utils/node.rb +443 -0
- data/lib/cisco_node_utils/node_util.rb +111 -0
- data/lib/cisco_node_utils/ntp_auth_key.rb +67 -0
- data/lib/cisco_node_utils/ntp_config.rb +83 -0
- data/lib/cisco_node_utils/ntp_server.rb +86 -0
- data/lib/cisco_node_utils/object_group.rb +75 -0
- data/lib/cisco_node_utils/object_group_entry.rb +143 -0
- data/lib/cisco_node_utils/overlay_global.rb +142 -0
- data/lib/cisco_node_utils/pim.rb +131 -0
- data/lib/cisco_node_utils/pim_group_list.rb +109 -0
- data/lib/cisco_node_utils/pim_rp_address.rb +103 -0
- data/lib/cisco_node_utils/platform.rb +217 -0
- data/lib/cisco_node_utils/portchannel_global.rb +347 -0
- data/lib/cisco_node_utils/radius_global.rb +165 -0
- data/lib/cisco_node_utils/radius_server.rb +421 -0
- data/lib/cisco_node_utils/radius_server_group.rb +117 -0
- data/lib/cisco_node_utils/route_map.rb +2540 -0
- data/lib/cisco_node_utils/router_ospf.rb +77 -0
- data/lib/cisco_node_utils/router_ospf_area.rb +416 -0
- data/lib/cisco_node_utils/router_ospf_area_vlink.rb +313 -0
- data/lib/cisco_node_utils/router_ospf_vrf.rb +342 -0
- data/lib/cisco_node_utils/snmp_notification_receiver.rb +176 -0
- data/lib/cisco_node_utils/snmpcommunity.rb +109 -0
- data/lib/cisco_node_utils/snmpgroup.rb +54 -0
- data/lib/cisco_node_utils/snmpnotification.rb +57 -0
- data/lib/cisco_node_utils/snmpserver.rb +132 -0
- data/lib/cisco_node_utils/snmpuser.rb +403 -0
- data/lib/cisco_node_utils/span_session.rb +149 -0
- data/lib/cisco_node_utils/stp_global.rb +676 -0
- data/lib/cisco_node_utils/syslog_facility.rb +64 -0
- data/lib/cisco_node_utils/syslog_server.rb +146 -0
- data/lib/cisco_node_utils/syslog_settings.rb +174 -0
- data/lib/cisco_node_utils/tacacs_global.rb +137 -0
- data/lib/cisco_node_utils/tacacs_server.rb +173 -0
- data/lib/cisco_node_utils/tacacs_server_group.rb +149 -0
- data/lib/cisco_node_utils/tacacs_server_host.rb +216 -0
- data/lib/cisco_node_utils/upgrade.rb +122 -0
- data/lib/cisco_node_utils/vdc.rb +118 -0
- data/lib/cisco_node_utils/version.rb +21 -0
- data/lib/cisco_node_utils/vlan.rb +301 -0
- data/lib/cisco_node_utils/vpc.rb +466 -0
- data/lib/cisco_node_utils/vrf.rb +192 -0
- data/lib/cisco_node_utils/vrf_af.rb +327 -0
- data/lib/cisco_node_utils/vtp.rb +125 -0
- data/lib/cisco_node_utils/vxlan_vtep.rb +286 -0
- data/lib/cisco_node_utils/vxlan_vtep_vni.rb +331 -0
- data/lib/cisco_node_utils/yang.rb +160 -0
- data/lib/cisco_node_utils/yum.rb +213 -0
- data/lib/cisco_node_utils.rb +21 -0
- data/lib/minitest/environment_plugin.rb +31 -0
- data/lib/minitest/log_level_plugin.rb +41 -0
- data/spec/client_spec.rb +7 -0
- data/spec/environment_spec.rb +384 -0
- data/spec/grpc_client_spec.rb +23 -0
- data/spec/isolate/all_clients_spec.rb +9 -0
- data/spec/isolate/grpc_only_spec.rb +16 -0
- data/spec/isolate/no_clients_spec.rb +26 -0
- data/spec/isolate/nxapi_only_spec.rb +16 -0
- data/spec/nxapi_client_spec.rb +42 -0
- data/spec/schema.yaml +82 -0
- data/spec/shared_examples_for_clients.rb +14 -0
- data/spec/spec_helper.rb +91 -0
- data/spec/whitespace_spec.rb +10 -0
- data/spec/yaml_spec.rb +42 -0
- data/tests/.rubocop.yml +18 -0
- data/tests/CSCuxdublin-1.0.0-7.0.3.I3.1.lib32_n9000.rpm +0 -0
- data/tests/basetest.rb +243 -0
- data/tests/ciscotest.rb +577 -0
- data/tests/cmd_config.yaml +75 -0
- data/tests/cmd_config_invalid.yaml +16 -0
- data/tests/n9000_sample-1.0.0-7.0.3.x86_64.rpm +0 -0
- data/tests/noop.rb +7 -0
- data/tests/platform_info.rb +63 -0
- data/tests/tacacs_server.yaml.example +6 -0
- data/tests/test_aaa_authentication_login.rb +243 -0
- data/tests/test_aaa_authentication_login_service.rb +761 -0
- data/tests/test_aaa_authorization_service.rb +874 -0
- data/tests/test_ace.rb +304 -0
- data/tests/test_acl.rb +185 -0
- data/tests/test_banner.rb +85 -0
- data/tests/test_bfd_global.rb +272 -0
- data/tests/test_bgp_af.rb +875 -0
- data/tests/test_bgp_af_aa.rb +108 -0
- data/tests/test_bgp_neighbor.rb +596 -0
- data/tests/test_bgp_neighbor_af.rb +781 -0
- data/tests/test_bridge_domain.rb +198 -0
- data/tests/test_bridge_domain_vni.rb +109 -0
- data/tests/test_client_utils.rb +111 -0
- data/tests/test_cmn_utils.rb +76 -0
- data/tests/test_command_config.rb +206 -0
- data/tests/test_command_reference.rb +669 -0
- data/tests/test_dhcp_relay_global.rb +286 -0
- data/tests/test_dns_domain.rb +123 -0
- data/tests/test_domain_name.rb +96 -0
- data/tests/test_encapsulation.rb +75 -0
- data/tests/test_evpn_multicast.rb +65 -0
- data/tests/test_evpn_multisite.rb +70 -0
- data/tests/test_evpn_stormcontrol.rb +56 -0
- data/tests/test_evpn_vni.rb +131 -0
- data/tests/test_fabricpath_global.rb +246 -0
- data/tests/test_fabricpath_topology.rb +77 -0
- data/tests/test_feature.rb +272 -0
- data/tests/test_grpc.rb +166 -0
- data/tests/test_hostname.rb +64 -0
- data/tests/test_hsrp_global.rb +79 -0
- data/tests/test_interface.rb +1958 -0
- data/tests/test_interface_bdi.rb +80 -0
- data/tests/test_interface_channel_group.rb +131 -0
- data/tests/test_interface_evpn_multisite.rb +94 -0
- data/tests/test_interface_hsrp.rb +134 -0
- data/tests/test_interface_hsrp_group.rb +570 -0
- data/tests/test_interface_ospf.rb +820 -0
- data/tests/test_interface_portchannel.rb +135 -0
- data/tests/test_interface_private_vlan.rb +365 -0
- data/tests/test_interface_service_vni.rb +203 -0
- data/tests/test_interface_svi.rb +210 -0
- data/tests/test_interface_switchport.rb +468 -0
- data/tests/test_ip_multicast.rb +80 -0
- data/tests/test_itd_device_group.rb +145 -0
- data/tests/test_itd_device_group_node.rb +199 -0
- data/tests/test_itd_service.rb +314 -0
- data/tests/test_logger.rb +43 -0
- data/tests/test_name_server.rb +94 -0
- data/tests/test_node.rb +50 -0
- data/tests/test_node_ext.rb +406 -0
- data/tests/test_node_util.rb +119 -0
- data/tests/test_ntp_auth_key.rb +77 -0
- data/tests/test_ntp_config.rb +100 -0
- data/tests/test_ntp_server.rb +146 -0
- data/tests/test_nxapi.rb +236 -0
- data/tests/test_object_group.rb +122 -0
- data/tests/test_overlay_global.rb +108 -0
- data/tests/test_pim.rb +203 -0
- data/tests/test_pim_group_list.rb +147 -0
- data/tests/test_pim_rp_address.rb +155 -0
- data/tests/test_platform.rb +254 -0
- data/tests/test_portchannel_global.rb +322 -0
- data/tests/test_radius_global.rb +108 -0
- data/tests/test_radius_server.rb +377 -0
- data/tests/test_radius_server_group.rb +151 -0
- data/tests/test_route_map.rb +1479 -0
- data/tests/test_router_bgp.rb +1325 -0
- data/tests/test_router_ospf.rb +56 -0
- data/tests/test_router_ospf_area.rb +433 -0
- data/tests/test_router_ospf_area_vlink.rb +298 -0
- data/tests/test_router_ospf_vrf.rb +690 -0
- data/tests/test_snmp_notification_receiver.rb +169 -0
- data/tests/test_snmpcommunity.rb +422 -0
- data/tests/test_snmpgroup.rb +71 -0
- data/tests/test_snmpnotification.rb +91 -0
- data/tests/test_snmpserver.rb +251 -0
- data/tests/test_snmpuser.rb +666 -0
- data/tests/test_span_session.rb +155 -0
- data/tests/test_stp_global.rb +575 -0
- data/tests/test_syslog_facility.rb +80 -0
- data/tests/test_syslog_server.rb +119 -0
- data/tests/test_syslog_settings.rb +123 -0
- data/tests/test_tacacs_global.rb +109 -0
- data/tests/test_tacacs_server.rb +436 -0
- data/tests/test_tacacs_server_group.rb +434 -0
- data/tests/test_tacacs_server_host.rb +427 -0
- data/tests/test_upgrade.rb +105 -0
- data/tests/test_vdc.rb +64 -0
- data/tests/test_vlan.rb +386 -0
- data/tests/test_vlan_private.rb +656 -0
- data/tests/test_vpc.rb +548 -0
- data/tests/test_vrf.rb +248 -0
- data/tests/test_vrf_af.rb +288 -0
- data/tests/test_vtp.rb +278 -0
- data/tests/test_vxlan_vtep.rb +327 -0
- data/tests/test_vxlan_vtep_vni.rb +326 -0
- data/tests/test_yang.rb +369 -0
- data/tests/test_yum.rb +109 -0
- data/tests/upgrade_info.yaml.example +3 -0
- data/tests/yum_package.yaml +94 -0
- metadata +534 -0
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
# Copyright (c) 2014-2016 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 'exceptions'
|
|
16
|
+
require 'yaml'
|
|
17
|
+
|
|
18
|
+
module Cisco
|
|
19
|
+
# Control a reference for an attribute.
|
|
20
|
+
class CmdRef
|
|
21
|
+
attr_reader :feature, :name, :hash
|
|
22
|
+
attr_reader :auto_default, :multiple, :kind, :default_only, :os_version
|
|
23
|
+
alias_method :auto_default?, :auto_default
|
|
24
|
+
alias_method :default_only?, :default_only
|
|
25
|
+
alias_method :multiple?, :multiple
|
|
26
|
+
|
|
27
|
+
KEYS = %w(default_value default_only
|
|
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 os_version)
|
|
32
|
+
|
|
33
|
+
def self.keys
|
|
34
|
+
KEYS
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
KINDS = %w(boolean int string symbol)
|
|
38
|
+
|
|
39
|
+
# Construct a CmdRef describing the given (feature, name) pair.
|
|
40
|
+
# Param "values" is a hash with keys as described in KEYS.
|
|
41
|
+
# Param "file" is for debugging purposes only.
|
|
42
|
+
def initialize(feature, name, values, file)
|
|
43
|
+
fail ArgumentError, "'#{values}' is not a hash." unless values.is_a? Hash
|
|
44
|
+
|
|
45
|
+
@feature = feature
|
|
46
|
+
@name = name
|
|
47
|
+
@auto_default = true
|
|
48
|
+
@default_only = false
|
|
49
|
+
@multiple = false
|
|
50
|
+
@kind = nil
|
|
51
|
+
@os_version = nil
|
|
52
|
+
|
|
53
|
+
values_to_hash(values, file)
|
|
54
|
+
|
|
55
|
+
if @hash['get_value'] || @hash['get_command']
|
|
56
|
+
define_helper('getter',
|
|
57
|
+
data_format: @hash['get_data_format'] || :cli,
|
|
58
|
+
command: @hash['get_command'],
|
|
59
|
+
context: @hash['get_context'] || [],
|
|
60
|
+
value: @hash['get_value'])
|
|
61
|
+
end
|
|
62
|
+
if @hash['set_value'] # rubocop:disable Style/GuardClause
|
|
63
|
+
define_helper('setter',
|
|
64
|
+
data_format: @hash['set_data_format'] || :cli,
|
|
65
|
+
context: @hash['set_context'] || [],
|
|
66
|
+
values: @hash['set_value'])
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def values_to_hash(values, file)
|
|
71
|
+
@hash = {}
|
|
72
|
+
values.each do |key, value|
|
|
73
|
+
unless KEYS.include?(key)
|
|
74
|
+
fail "Unrecognized key #{key} for #{feature}, #{name} in #{file}"
|
|
75
|
+
end
|
|
76
|
+
case key
|
|
77
|
+
when 'auto_default'
|
|
78
|
+
@auto_default = value ? true : false
|
|
79
|
+
when 'data_format', 'get_data_format', 'set_data_format'
|
|
80
|
+
@hash[key] = value.to_sym
|
|
81
|
+
when 'default_only'
|
|
82
|
+
@default_only = true
|
|
83
|
+
# default_value overrides default_only
|
|
84
|
+
@hash['default_value'] ||= value
|
|
85
|
+
when 'multiple'
|
|
86
|
+
@multiple = boolean_default_true(value)
|
|
87
|
+
when 'kind'
|
|
88
|
+
fail "Unknown 'kind': '#{value}'" unless KINDS.include?(value)
|
|
89
|
+
@kind = value.to_sym
|
|
90
|
+
when 'os_version'
|
|
91
|
+
@os_version = value
|
|
92
|
+
else
|
|
93
|
+
# default_value overrides default_only
|
|
94
|
+
@default_only = false if key == 'default_value'
|
|
95
|
+
@hash[key] = value
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Inherit general to specific if needed
|
|
100
|
+
if @hash.key?('data_format')
|
|
101
|
+
@hash['get_data_format'] = @hash['data_format'] \
|
|
102
|
+
unless @hash.key?('get_data_format')
|
|
103
|
+
@hash['set_data_format'] = @hash['data_format'] \
|
|
104
|
+
unless @hash.key?('set_data_format')
|
|
105
|
+
end
|
|
106
|
+
if @hash.key?('context')
|
|
107
|
+
@hash['get_context'] = @hash['context'] unless @hash.key?('get_context')
|
|
108
|
+
@hash['set_context'] = @hash['context'] unless @hash.key?('set_context')
|
|
109
|
+
end
|
|
110
|
+
if @hash.key?('value')
|
|
111
|
+
@hash['get_value'] = @hash['value'] unless @hash.key?('get_value')
|
|
112
|
+
@hash['set_value'] = @hash['value'] unless @hash.key?('set_value')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
@hash.delete_if { |key, _| key != 'default_value' } if @default_only
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Does this instance have a valid getter() function?
|
|
119
|
+
# Will be overridden at initialization if so.
|
|
120
|
+
def getter?
|
|
121
|
+
!@hash['getter'].nil?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Does this instance have a valid setter() function?
|
|
125
|
+
# Will be overridden at initialization if so.
|
|
126
|
+
def setter?
|
|
127
|
+
!@hash['setter'].nil?
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Default getter method.
|
|
131
|
+
# Will be overridden at initialization if the relevant parameters are set.
|
|
132
|
+
#
|
|
133
|
+
# A non-trivial implementation of this method will take args *or* kwargs,
|
|
134
|
+
# and will return a hash of the form:
|
|
135
|
+
# {
|
|
136
|
+
# data_format: :cli,
|
|
137
|
+
# command: string or nil,
|
|
138
|
+
# context: array<string> or array<regexp>, perhaps empty
|
|
139
|
+
# value: string or regexp,
|
|
140
|
+
# }
|
|
141
|
+
def getter(*args, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
|
|
142
|
+
fail UnsupportedError.new(@feature, @name, 'getter')
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Default setter method.
|
|
146
|
+
# Will be overridden at initialization if the relevant parameters are set.
|
|
147
|
+
#
|
|
148
|
+
# A non-trivial implementation of this method will take args *or* kwargs,
|
|
149
|
+
# and will return a hash of the form:
|
|
150
|
+
# {
|
|
151
|
+
# data_format: :cli,
|
|
152
|
+
# context: array<string>, perhaps empty
|
|
153
|
+
# values: array<string>,
|
|
154
|
+
# }
|
|
155
|
+
def setter(*args, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
|
|
156
|
+
fail UnsupportedError.new(@feature, @name, 'setter')
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Property with an implicit value of 'true' if no value is given
|
|
160
|
+
def boolean_default_true(value)
|
|
161
|
+
value.nil? || value
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def key_substitutor(item, kwargs)
|
|
165
|
+
result = item
|
|
166
|
+
kwargs.each do |key, value|
|
|
167
|
+
result = result.sub("<#{key}>", value.to_s)
|
|
168
|
+
end
|
|
169
|
+
unsub = result[/<(\S+)>/, 1]
|
|
170
|
+
fail ArgumentError, \
|
|
171
|
+
"No value specified for '#{unsub}' in '#{result}'" if unsub
|
|
172
|
+
result
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def printf_substitutor(item, args)
|
|
176
|
+
item = sprintf(item, *args.shift(item.scan(/%/).length))
|
|
177
|
+
[item, args]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Create a helper method for generating the getter/setter values.
|
|
181
|
+
# This method will automatically handle wildcard arguments.
|
|
182
|
+
def define_helper(method_name, base_hash)
|
|
183
|
+
# Which kind of wildcards (if any) do we need to support?
|
|
184
|
+
combined = []
|
|
185
|
+
base_hash.each_value do |v|
|
|
186
|
+
combined += v if v.is_a?(Array)
|
|
187
|
+
combined << v if v.is_a?(String)
|
|
188
|
+
end
|
|
189
|
+
key_value = combined.any? { |i| i.is_a?(String) && /<\S+>/ =~ i }
|
|
190
|
+
printf = combined.any? { |i| i.is_a?(String) && /%/ =~ i }
|
|
191
|
+
|
|
192
|
+
if key_value && printf
|
|
193
|
+
fail 'Invalid mixture of key-value and printf wildcards ' \
|
|
194
|
+
"in #{method_name}: #{combined}"
|
|
195
|
+
elsif key_value
|
|
196
|
+
define_key_value_helper(method_name, base_hash)
|
|
197
|
+
elsif printf
|
|
198
|
+
arg_count = combined.join.scan(/%/).length
|
|
199
|
+
define_printf_helper(method_name, base_hash, arg_count)
|
|
200
|
+
else
|
|
201
|
+
# simple static token(s)
|
|
202
|
+
define_static_helper(method_name, base_hash)
|
|
203
|
+
end
|
|
204
|
+
@hash[method_name] = true
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def define_key_value_helper(method_name, base_hash)
|
|
208
|
+
# Key-value substitution
|
|
209
|
+
define_singleton_method method_name.to_sym do |*args, **kwargs|
|
|
210
|
+
unless args.empty?
|
|
211
|
+
fail ArgumentError, "#{method_name} requires keyword args, not "\
|
|
212
|
+
'positional args'
|
|
213
|
+
end
|
|
214
|
+
result = {}
|
|
215
|
+
base_hash.each do |k, v|
|
|
216
|
+
if v.is_a?(String)
|
|
217
|
+
v = key_substitutor(v, kwargs)
|
|
218
|
+
elsif v.is_a?(Array)
|
|
219
|
+
output = []
|
|
220
|
+
v.each do |line|
|
|
221
|
+
# Check for (?) flag indicating optional param
|
|
222
|
+
optional_line = line[/^\(\?\)(.*)/, 1]
|
|
223
|
+
if optional_line
|
|
224
|
+
begin
|
|
225
|
+
line = key_substitutor(optional_line, kwargs)
|
|
226
|
+
rescue ArgumentError # Unsubstituted key - OK to skip this line
|
|
227
|
+
next
|
|
228
|
+
end
|
|
229
|
+
else
|
|
230
|
+
line = key_substitutor(line, kwargs)
|
|
231
|
+
end
|
|
232
|
+
output.push(line)
|
|
233
|
+
end
|
|
234
|
+
v = output
|
|
235
|
+
end
|
|
236
|
+
result[k] = v
|
|
237
|
+
end
|
|
238
|
+
result
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def define_printf_helper(method_name, base_hash, arg_count)
|
|
243
|
+
define_singleton_method method_name.to_sym do |*args, **kwargs|
|
|
244
|
+
unless kwargs.empty?
|
|
245
|
+
fail ArgumentError, "#{method_name} requires positional args, not " \
|
|
246
|
+
'keyword args'
|
|
247
|
+
end
|
|
248
|
+
unless args.length == arg_count
|
|
249
|
+
fail ArgumentError, 'wrong number of arguments ' \
|
|
250
|
+
"(#{args.length} for #{arg_count})"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
result = {}
|
|
254
|
+
base_hash.each do |k, v|
|
|
255
|
+
if v.is_a?(String)
|
|
256
|
+
v, args = printf_substitutor(v, args)
|
|
257
|
+
elsif v.is_a?(Array)
|
|
258
|
+
output = []
|
|
259
|
+
v.each do |line|
|
|
260
|
+
line, args = printf_substitutor(line, args)
|
|
261
|
+
output.push(line)
|
|
262
|
+
end
|
|
263
|
+
v = output
|
|
264
|
+
end
|
|
265
|
+
result[k] = v
|
|
266
|
+
end
|
|
267
|
+
result
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def define_static_helper(method_name, base_hash)
|
|
272
|
+
# rubocop:disable Lint/UnusedBlockArgument
|
|
273
|
+
define_singleton_method method_name.to_sym do |*args, **kwargs|
|
|
274
|
+
base_hash
|
|
275
|
+
end
|
|
276
|
+
# rubocop:enable Lint/UnusedBlockArgument
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def convert_to_constant(value)
|
|
280
|
+
# NOTE: This method is now deprecated and should not be used for future
|
|
281
|
+
# development.
|
|
282
|
+
#
|
|
283
|
+
# If value is a string and it is empty OR the first letter is lower case
|
|
284
|
+
# then leave value untouched.
|
|
285
|
+
# If value is a string and the first letter is uppercase this indicates
|
|
286
|
+
# that it could be a constant in Ruby, so attempt to convert it
|
|
287
|
+
# to a Constant.
|
|
288
|
+
if value.is_a?(String) && !value.empty?
|
|
289
|
+
if value[0].chr == value[0].chr.upcase
|
|
290
|
+
begin
|
|
291
|
+
value = Object.const_get(value) if Object.const_defined?(value)
|
|
292
|
+
rescue NameError
|
|
293
|
+
debug("'#{value}' is not a constant")
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
value
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def method_missing(method_name, *args, &block)
|
|
301
|
+
if KEYS.include?(method_name.to_s)
|
|
302
|
+
# ref.foo -> return @hash[foo] or fail IndexError
|
|
303
|
+
method_name = method_name.to_s
|
|
304
|
+
unless @hash.include?(method_name)
|
|
305
|
+
if @default_only
|
|
306
|
+
fail UnsupportedError.new(@feature, @name, method_name)
|
|
307
|
+
end
|
|
308
|
+
fail IndexError, "No #{method_name} defined for #{@feature}, #{@name}"
|
|
309
|
+
end
|
|
310
|
+
# puts("get #{method_name}: '#{@hash[method_name]}'")
|
|
311
|
+
@hash[method_name]
|
|
312
|
+
elsif method_name.to_s[-1] == '?' && \
|
|
313
|
+
KEYS.include?(method_name.to_s[0..-2])
|
|
314
|
+
# ref.foo? -> return true if @hash[foo], else false
|
|
315
|
+
method_name = method_name.to_s[0..-2]
|
|
316
|
+
@hash.include?(method_name)
|
|
317
|
+
else
|
|
318
|
+
super(method_name, *args, &block)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Print useful debugging information about the object.
|
|
323
|
+
def to_s
|
|
324
|
+
str = ''
|
|
325
|
+
str << "Command: #{@feature} #{@name}\n"
|
|
326
|
+
@hash.each { |key, value| str << " #{key}: #{value}\n" }
|
|
327
|
+
str
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Placeholder for known but explicitly excluded entry
|
|
332
|
+
# For these, we have an implied default_only value of nil.
|
|
333
|
+
class UnsupportedCmdRef < CmdRef
|
|
334
|
+
def initialize(feature, name, file)
|
|
335
|
+
super(feature, name, { 'default_only' => nil }, file)
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Builds reference hash for the platform specified in the product id.
|
|
340
|
+
class CommandReference
|
|
341
|
+
@@debug = false # rubocop:disable Style/ClassVars
|
|
342
|
+
|
|
343
|
+
def self.debug=(value)
|
|
344
|
+
fail ArgumentError, 'Debug must be boolean' unless value == true ||
|
|
345
|
+
value == false
|
|
346
|
+
@@debug = value # rubocop:disable Style/ClassVars
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
attr_reader :data_formats, :files, :platform, :product_id
|
|
350
|
+
|
|
351
|
+
# Constructor.
|
|
352
|
+
# Normal usage is to pass product, platform, data_formats,
|
|
353
|
+
# in which case usual YAML files will be located then the list
|
|
354
|
+
# will be filtered down to only those matching the given settings.
|
|
355
|
+
# For testing purposes (only!) you can pass an explicit list of files to
|
|
356
|
+
# load instead. This list will NOT be filtered further by product_id.
|
|
357
|
+
def initialize(product: nil,
|
|
358
|
+
platform: nil,
|
|
359
|
+
data_formats: [],
|
|
360
|
+
files: nil)
|
|
361
|
+
@product_id = product
|
|
362
|
+
@platform = platform
|
|
363
|
+
@data_formats = data_formats
|
|
364
|
+
@hash = {}
|
|
365
|
+
if files
|
|
366
|
+
@files = files
|
|
367
|
+
else
|
|
368
|
+
@files = Dir.glob(__dir__ + '/cmd_ref/*.yaml')
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
build_cmd_ref
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Build complete reference hash.
|
|
375
|
+
def build_cmd_ref
|
|
376
|
+
# Example id's: N3K-C3048TP-1GE, N3K-C3064PQ-10GE, N7K-C7009, N7K-C7009
|
|
377
|
+
debug "Product: #{@product_id}"
|
|
378
|
+
debug "Files being used: #{@files.join(', ')}"
|
|
379
|
+
|
|
380
|
+
@files.each do |file|
|
|
381
|
+
feature = File.basename(file).split('.')[0]
|
|
382
|
+
debug "Processing file '#{file}' as feature '#{feature}'"
|
|
383
|
+
feature_hash = load_yaml(file)
|
|
384
|
+
if feature_hash.empty?
|
|
385
|
+
debug "Feature #{feature} is empty"
|
|
386
|
+
next
|
|
387
|
+
end
|
|
388
|
+
begin
|
|
389
|
+
feature_hash = filter_hash(feature_hash)
|
|
390
|
+
rescue RuntimeError => e
|
|
391
|
+
raise "#{file}: #{e}"
|
|
392
|
+
end
|
|
393
|
+
if feature_hash.empty?
|
|
394
|
+
debug "Feature #{feature} is excluded"
|
|
395
|
+
@hash[feature] = UnsupportedCmdRef.new(feature, nil, file)
|
|
396
|
+
next
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
base_hash = {}
|
|
400
|
+
if feature_hash.key?('_template')
|
|
401
|
+
base_hash = CommandReference.hash_merge(feature_hash['_template'])
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
feature_hash.each do |name, value|
|
|
405
|
+
fail "No entries under '#{name}' in '#{file}'" if value.nil?
|
|
406
|
+
@hash[feature] ||= {}
|
|
407
|
+
if value.empty?
|
|
408
|
+
@hash[feature][name] = UnsupportedCmdRef.new(feature, name, file)
|
|
409
|
+
else
|
|
410
|
+
values = CommandReference.hash_merge(value, base_hash.clone)
|
|
411
|
+
@hash[feature][name] = CmdRef.new(feature, name, values, file)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def supports?(feature, property=nil)
|
|
418
|
+
value = @hash[feature]
|
|
419
|
+
value = value[property] if value.is_a?(Hash) && property
|
|
420
|
+
!(value.is_a?(UnsupportedCmdRef) || value.nil?)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Get the command reference
|
|
424
|
+
def lookup(feature, name)
|
|
425
|
+
value = @hash[feature]
|
|
426
|
+
value = value[name] if value.is_a? Hash
|
|
427
|
+
fail IndexError, "No CmdRef defined for #{feature}, #{name}" if value.nil?
|
|
428
|
+
value
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def empty?
|
|
432
|
+
@hash.empty?
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# Print debug statements
|
|
436
|
+
def debug(text)
|
|
437
|
+
puts "DEBUG: #{text}" if @@debug
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
KNOWN_PLATFORMS = %w(C3048 C3064 C3132 C3172 N35 N3k N3k-F N5k N6k N7k N9k
|
|
441
|
+
N9k-F XRv9k)
|
|
442
|
+
|
|
443
|
+
def self.platform_to_filter(platform)
|
|
444
|
+
if KNOWN_PLATFORMS.include?(platform)
|
|
445
|
+
case platform
|
|
446
|
+
when 'XRv9k'
|
|
447
|
+
/XRV9/
|
|
448
|
+
when 'N35'
|
|
449
|
+
/N3K-C35/
|
|
450
|
+
when 'N9k'
|
|
451
|
+
# For non-fretta n9k platforms we need to
|
|
452
|
+
# match everything except the trailing -F
|
|
453
|
+
/^N9...(?!.*-F)/
|
|
454
|
+
when 'N9k-F'
|
|
455
|
+
# For fretta n9k we need to include the trailing -F
|
|
456
|
+
/^N9.*-F$/
|
|
457
|
+
when 'N3k'
|
|
458
|
+
# For non-fretta n3k platforms we need to
|
|
459
|
+
# match everything except the trailing -F
|
|
460
|
+
/^N3...(?!.*-F)/
|
|
461
|
+
when 'N3k-F'
|
|
462
|
+
# For fretta n3k we need to include the trailing -F
|
|
463
|
+
/^N3.*-F$/
|
|
464
|
+
else
|
|
465
|
+
Regexp.new platform.tr('k', '')
|
|
466
|
+
end
|
|
467
|
+
else
|
|
468
|
+
fail IndexError, "Unknown platform key '#{platform}'"
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
KNOWN_FILTERS = %w(nexus ios_xr cli nxapi_structured)
|
|
473
|
+
|
|
474
|
+
def self.key_match(key, platform, product_id, data_formats)
|
|
475
|
+
if KNOWN_PLATFORMS.include?(key)
|
|
476
|
+
return platform_to_filter(key) =~ product_id ? true : false
|
|
477
|
+
elsif KNOWN_FILTERS.include?(key)
|
|
478
|
+
return true if data_formats && data_formats.include?(key.to_sym)
|
|
479
|
+
return true if key == platform.to_s
|
|
480
|
+
return false
|
|
481
|
+
else
|
|
482
|
+
return :unknown
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Helper method
|
|
487
|
+
# Given a Hash of command reference data as read from YAML, does:
|
|
488
|
+
# - Delete any platform-specific data not applicable to this platform
|
|
489
|
+
# - Delete any product-specific data not applicable to this product_id
|
|
490
|
+
# - Delete any data-model-specific data not supported by this node
|
|
491
|
+
# Returns the filtered hash (possibly empty)
|
|
492
|
+
def self.filter_hash(hash,
|
|
493
|
+
platform: nil,
|
|
494
|
+
product_id: nil,
|
|
495
|
+
data_formats: nil,
|
|
496
|
+
allow_unknown_keys: true)
|
|
497
|
+
result = {}
|
|
498
|
+
|
|
499
|
+
exclude = hash['_exclude'] || []
|
|
500
|
+
exclude.each do |value|
|
|
501
|
+
# We don't allow exclusion by data_format - just platform/product
|
|
502
|
+
if key_match(value, platform, product_id, nil) == true
|
|
503
|
+
debug "Exclude this product (#{product_id}, #{value})"
|
|
504
|
+
return result
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# to_inspect: sub-keys we want to recurse into
|
|
509
|
+
to_inspect = []
|
|
510
|
+
# regexp_match: did we find a product_id regexp that matches?
|
|
511
|
+
regexp_match = false
|
|
512
|
+
|
|
513
|
+
hash.each do |key, value|
|
|
514
|
+
next if key == '_exclude'
|
|
515
|
+
if CmdRef.keys.include?(key)
|
|
516
|
+
result[key] = value
|
|
517
|
+
elsif key != 'else'
|
|
518
|
+
match = key_match(key, platform, product_id, data_formats)
|
|
519
|
+
next if match == false
|
|
520
|
+
if match == :unknown
|
|
521
|
+
fail "Unrecognized key '#{key}'" unless allow_unknown_keys
|
|
522
|
+
end
|
|
523
|
+
regexp_match = true if match == true
|
|
524
|
+
to_inspect << key
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
# If we didn't find any platform regexp match,
|
|
528
|
+
# and an 'else' sub-hash is provided, descend into 'else'
|
|
529
|
+
to_inspect << 'else' if hash.key?('else') && !regexp_match
|
|
530
|
+
# Recurse! Sub-hashes can override the base hash
|
|
531
|
+
to_inspect.each do |key|
|
|
532
|
+
unless hash[key].is_a?(Hash)
|
|
533
|
+
result[key] = hash[key]
|
|
534
|
+
next
|
|
535
|
+
end
|
|
536
|
+
begin
|
|
537
|
+
result[key] = filter_hash(hash[key],
|
|
538
|
+
platform: platform,
|
|
539
|
+
product_id: product_id,
|
|
540
|
+
data_formats: data_formats,
|
|
541
|
+
allow_unknown_keys: false)
|
|
542
|
+
rescue RuntimeError => e
|
|
543
|
+
# Recursively wrap the error as needed to provide context
|
|
544
|
+
raise "[#{key}]: #{e}"
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
result
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def filter_hash(input_hash)
|
|
551
|
+
CommandReference.filter_hash(input_hash,
|
|
552
|
+
platform: platform,
|
|
553
|
+
product_id: product_id,
|
|
554
|
+
data_formats: data_formats)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# Helper method
|
|
558
|
+
# Given a suitably filtered Hash of command reference data, does:
|
|
559
|
+
# - Inherit data from the given base_hash (if any) and extend/override it
|
|
560
|
+
# with the given input data.
|
|
561
|
+
def self.hash_merge(input_hash, base_hash=nil)
|
|
562
|
+
return base_hash if input_hash.nil?
|
|
563
|
+
result = base_hash
|
|
564
|
+
result ||= {}
|
|
565
|
+
# to_inspect: sub-hashes we want to recurse into
|
|
566
|
+
to_inspect = []
|
|
567
|
+
|
|
568
|
+
input_hash.each do |key, value|
|
|
569
|
+
if CmdRef.keys.include?(key)
|
|
570
|
+
result[key] = value
|
|
571
|
+
elsif value.is_a?(Hash)
|
|
572
|
+
to_inspect << value
|
|
573
|
+
elsif value.nil?
|
|
574
|
+
next
|
|
575
|
+
else
|
|
576
|
+
fail "Unexpected non-hash data: #{value}"
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
# Recurse! Sub-hashes can override the base hash
|
|
580
|
+
to_inspect.each do |hash|
|
|
581
|
+
result = hash_merge(hash, result)
|
|
582
|
+
end
|
|
583
|
+
result
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
# Helper method.
|
|
587
|
+
# Combines the two given values (either or both of which may be arrays)
|
|
588
|
+
# into a single combined array
|
|
589
|
+
# value_append('foo', 'bar') ==> ['foo', 'bar']
|
|
590
|
+
# value_append('foo', ['bar', 'baz']) ==> ['foo', 'bar', 'baz']
|
|
591
|
+
def self.value_append(base_value, new_value)
|
|
592
|
+
base_value = [base_value] unless base_value.is_a?(Array)
|
|
593
|
+
new_value = [new_value] unless new_value.is_a?(Array)
|
|
594
|
+
base_value + new_value
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def mapping?(node)
|
|
598
|
+
node.class.ancestors.any? { |name| /Map/ =~ name.to_s }
|
|
599
|
+
end
|
|
600
|
+
private :mapping?
|
|
601
|
+
|
|
602
|
+
def get_keys_values_from_map(node)
|
|
603
|
+
# A Psych::Node::Mapping instance has an Array of children in
|
|
604
|
+
# the format [key1, val1, key2, val2]
|
|
605
|
+
key_children = node.children.select.each_with_index { |_, i| i.even? }
|
|
606
|
+
val_children = node.children.select.each_with_index { |_, i| i.odd? }
|
|
607
|
+
debug "children of #{node} mapping: #{key_children}, #{val_children}"
|
|
608
|
+
[key_children, val_children]
|
|
609
|
+
end
|
|
610
|
+
private :get_keys_values_from_map
|
|
611
|
+
|
|
612
|
+
# Validate the YAML node tree before converting it into Ruby
|
|
613
|
+
# data structures.
|
|
614
|
+
#
|
|
615
|
+
# @raise RuntimeError if the node tree is not valid by our constraints.
|
|
616
|
+
#
|
|
617
|
+
# @param node Node to be validated, then recurse to its children.
|
|
618
|
+
# @param filename File that YAML was parsed from, for messages
|
|
619
|
+
# @param depth Depth into the node tree
|
|
620
|
+
# @param parents String describing parents of this node, for messages
|
|
621
|
+
def validate_yaml(node, filename, depth=0, parents=nil)
|
|
622
|
+
return unless node && (mapping?(node) || node.children)
|
|
623
|
+
# Psych wraps everything in a Document instance, which we ignore.
|
|
624
|
+
unless node.class.ancestors.any? { |name| /Document/ =~ name.to_s }
|
|
625
|
+
depth += 1
|
|
626
|
+
end
|
|
627
|
+
debug "Validating #{node.class} at depth #{depth}"
|
|
628
|
+
|
|
629
|
+
# No special validation for non-mapping nodes - just recurse
|
|
630
|
+
unless mapping?(node)
|
|
631
|
+
node.children.each do |child|
|
|
632
|
+
validate_yaml(child, filename, depth, parents)
|
|
633
|
+
end
|
|
634
|
+
return
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
# For Mappings, we validate more extensively:
|
|
638
|
+
# 1. no duplicate keys are allowed (Psych doesn't catch this)
|
|
639
|
+
# 2. Features must be listed in alphabetical order for maintainability
|
|
640
|
+
|
|
641
|
+
# Take advantage of our known YAML structure to assign labels by depth
|
|
642
|
+
label = %w(feature name param).fetch(depth, 'key')
|
|
643
|
+
|
|
644
|
+
# Get the key nodes and value nodes under this mapping
|
|
645
|
+
key_children, val_children = get_keys_values_from_map(node)
|
|
646
|
+
# Get an array of key names
|
|
647
|
+
key_arr = key_children.map(&:value)
|
|
648
|
+
|
|
649
|
+
# Make sure no duplicate key names.
|
|
650
|
+
# If searching from the start of the array finds a name at one index,
|
|
651
|
+
# but searching from the end of the array finds it at a different one,
|
|
652
|
+
# then we have a duplicate.
|
|
653
|
+
dup = key_arr.detect { |e| key_arr.index(e) != key_arr.rindex(e) }
|
|
654
|
+
if dup
|
|
655
|
+
msg = "Duplicate #{label} '#{dup}'#{parents} in #{filename}!"
|
|
656
|
+
fail msg
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
# Enforce alphabetical ordering of features (only).
|
|
660
|
+
# We can extend this later to enforce ordering of names if desired
|
|
661
|
+
# by checking at depth 2 as well.
|
|
662
|
+
if depth == 1
|
|
663
|
+
last_key = nil
|
|
664
|
+
key_arr.each do |key|
|
|
665
|
+
if last_key && key < last_key
|
|
666
|
+
fail "features out of order in #{filename}: (#{last_key} > #{key})"
|
|
667
|
+
end
|
|
668
|
+
last_key = key
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# Recurse to the children. We get a little fancy here so as to be able
|
|
673
|
+
# to provide more meaningful debug/error messages, such as:
|
|
674
|
+
# Duplicate param 'default_value' under feature 'foo', name 'bar'
|
|
675
|
+
key_children.zip(val_children).each do |key_node, val_node|
|
|
676
|
+
if parents
|
|
677
|
+
new_parents = parents + ", #{label} '#{key_node.value}'"
|
|
678
|
+
else
|
|
679
|
+
new_parents = " under #{label} '#{key_node.value}'"
|
|
680
|
+
end
|
|
681
|
+
validate_yaml(key_node, filename, depth, new_parents) # unnecessary?
|
|
682
|
+
validate_yaml(val_node, filename, depth, new_parents)
|
|
683
|
+
end
|
|
684
|
+
end
|
|
685
|
+
private :validate_yaml
|
|
686
|
+
|
|
687
|
+
# Read in yaml file.
|
|
688
|
+
# The expectation is that a file corresponds to a feature
|
|
689
|
+
def load_yaml(yaml_file)
|
|
690
|
+
fail "File #{yaml_file} doesn't exist." unless File.exist?(yaml_file)
|
|
691
|
+
# Parse YAML file into a tree of nodes
|
|
692
|
+
# Psych::SyntaxError doesn't inherit from StandardError in some versions,
|
|
693
|
+
# so we want to explicitly catch it if using Psych.
|
|
694
|
+
rescue_errors = [::StandardError, ::Psych::SyntaxError]
|
|
695
|
+
yaml_parsed = File.open(yaml_file, 'r') do |f|
|
|
696
|
+
begin
|
|
697
|
+
YAML.parse(f)
|
|
698
|
+
rescue *rescue_errors => e
|
|
699
|
+
raise "unable to parse #{yaml_file}: #{e}"
|
|
700
|
+
end
|
|
701
|
+
end
|
|
702
|
+
return {} unless yaml_parsed
|
|
703
|
+
# Validate the node tree
|
|
704
|
+
validate_yaml(yaml_parsed, yaml_file)
|
|
705
|
+
# If validation passed, convert the node tree to a Ruby Hash.
|
|
706
|
+
yaml_parsed.transform
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def to_s
|
|
710
|
+
@num_features ||= @hash.values.length
|
|
711
|
+
@num_attributes ||= @hash.values.inject(0) do |sum, n|
|
|
712
|
+
sum + (n.is_a?(Hash) ? n.values.length : 1)
|
|
713
|
+
end
|
|
714
|
+
"CommandReference describing #{@num_features} features " \
|
|
715
|
+
"with #{@num_attributes} attributes in total"
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def inspect
|
|
719
|
+
"CommandReference for '#{product_id}' " \
|
|
720
|
+
"(platform:'#{platform}', data formats:#{data_formats}) " \
|
|
721
|
+
"based on #{files.length} files"
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
end
|