diff --git a/ansible/inventory/group_vars/all/network b/ansible/inventory/group_vars/all/network index dbb263712..49acbd3fb 100644 --- a/ansible/inventory/group_vars/all/network +++ b/ansible/inventory/group_vars/all/network @@ -80,6 +80,17 @@ network_patch_suffix_phy: '-phy' # OVS bridge. network_patch_suffix_ovs: '-ovs' +############################################################################### +# OpenStack Network configuration + +# Whether to register networks in OpenStack. The following networks are +# registered: +# - Ironic provisioning network (defined by provision_wl_net_name) +# - Ironic inspection network (defined by inspection_net_name) +# - Ironic cleaning network (defined by cleaning_net_name) +# Default is true. +openstack_network_registration_enabled: true + ############################################################################### # Network routing table configuration. diff --git a/ansible/network-connectivity.yml b/ansible/network-connectivity.yml index 8259f898a..327522c67 100644 --- a/ansible/network-connectivity.yml +++ b/ansible/network-connectivity.yml @@ -7,7 +7,7 @@ default(100) }} vars: # Skip external connectivity check when behind a proxy. - nc_skip_external_net: "{{ http_proxy | truthy }}" + nc_skip_external_net: "{{ http_proxy is truthy }}" # Set this to an external IP address to check. nc_external_ip: 8.8.8.8 # Set this to an external hostname to check. diff --git a/ansible/provision-net.yml b/ansible/provision-net.yml index 94a0d1f60..f6c8b8ccb 100644 --- a/ansible/provision-net.yml +++ b/ansible/provision-net.yml @@ -68,29 +68,31 @@ - cleaning-net - inspection-net tasks: - - name: Validate OpenStack password authentication parameters - fail: - msg: > - Required OpenStack authentication parameter {{ item }} is - {% if item in openstack_auth %}empty{% else %}not present{% endif %} - in openstack_auth. Have you sourced the environment file? - when: - - openstack_auth_type == 'password' - - item not in openstack_auth or not openstack_auth[item] - with_items: "{{ openstack_auth_password_required_params }}" - tags: - - config-validation + - block: + - name: Validate OpenStack password authentication parameters + fail: + msg: > + Required OpenStack authentication parameter {{ item }} is + {% if item in openstack_auth %}empty{% else %}not present{% endif %} + in openstack_auth. Have you sourced the environment file? + when: + - openstack_auth_type == 'password' + - item not in openstack_auth or not openstack_auth[item] + with_items: "{{ openstack_auth_password_required_params }}" + tags: + - config-validation - - import_role: - name: stackhpc.openstack.os_networks - vars: - os_openstacksdk_install_epel: "{{ dnf_install_epel }}" - os_openstacksdk_state: "latest" - os_networks_upper_constraints_file: "{{ openstacksdk_upper_constraints_file }}" - os_networks_venv: "{{ venv }}" - os_networks_auth_type: "{{ openstack_auth_type }}" - os_networks_auth: "{{ openstack_auth }}" - os_networks_cacert: "{{ openstack_cacert | default(omit, true) }}" - os_networks_interface: "{{ openstack_interface | default(omit, true) }}" - # Network configuration. - os_networks: "{{ network_registrations }}" + - import_role: + name: stackhpc.openstack.os_networks + vars: + os_openstacksdk_install_epel: "{{ dnf_install_epel }}" + os_openstacksdk_state: "latest" + os_networks_upper_constraints_file: "{{ openstacksdk_upper_constraints_file }}" + os_networks_venv: "{{ venv }}" + os_networks_auth_type: "{{ openstack_auth_type }}" + os_networks_auth: "{{ openstack_auth }}" + os_networks_cacert: "{{ openstack_cacert | default(omit, true) }}" + os_networks_interface: "{{ openstack_interface | default(omit, true) }}" + # Network configuration. + os_networks: "{{ network_registrations }}" + when: openstack_network_registration_enabled | bool diff --git a/doc/source/configuration/reference/network.rst b/doc/source/configuration/reference/network.rst index 00dac7e54..00301da8b 100644 --- a/doc/source/configuration/reference/network.rst +++ b/doc/source/configuration/reference/network.rst @@ -616,6 +616,26 @@ The following attributes are supported: bond and bridge interfaces, settings apply to underlying interfaces. This should be a string of arguments passed to the ``ethtool`` utility, for example ``"-G ${DEVICE} rx 8192 tx 8192"``. +``ingress_qos_map`` + .. note:: + + ``ingress_qos_map`` is only supported with + ``network_engine: nmstate`` on VLAN interfaces. + + VLAN ingress QoS map configuration. This maps VLAN header Priority Code + Point (PCP) to Linux internal packet priority for incoming packets. + + - Structured list: ``[{from: 7, to: 254}, {from: 3, to: 12}]`` +``egress_qos_map`` + .. note:: + + ``egress_qos_map`` is only supported with + ``network_engine: nmstate`` on VLAN interfaces. + + VLAN egress QoS map configuration. This maps Linux internal packet + priority to VLAN header Priority Code Point (PCP) for outgoing packets. + + - Structured list: ``[{from: 129, to: 7}, {from: 130, to: 6}]`` ``zone`` .. note:: ``zone`` is not currently supported on Ubuntu. @@ -765,6 +785,29 @@ this case, a ``parent`` attribute must specify the underlying interface: Ethernet interfaces, bridges, and bond master interfaces may all be parents to a VLAN interface. +VLAN QoS Mapping (nmstate) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using ``network_engine: nmstate``, VLAN interfaces can define native +nmstate QoS maps via ``ingress_qos_map`` and ``egress_qos_map``. These map +between Linux internal packet priority and VLAN header Priority Code Point +(PCP). Use the structured list-of-maps form. + +.. code-block:: yaml + :caption: ``inventory/group_vars//network-interfaces`` + + example_interface: "eth2.{{ example_vlan }}" + example_ingress_qos_map: + - from: 7 + to: 254 + example_egress_qos_map: + - from: 129 + to: 7 + +Undefined QoS map values do not render an nmstate QoS map key. Use an +explicit empty list (``[]``) to render an empty map through nmstate, for +example when clearing an existing map. + Bridges and VLANs ^^^^^^^^^^^^^^^^^ @@ -926,6 +969,27 @@ consideration: lookup the interface for the cloud-init network configuration that occurs during bifrost provisioning of the overcloud. + +Registration of networks in OpenStack +-------------------------------------- + +By default, Kayobe will register the following networks: + +* :ref:`workload-cleaning-network` +* :ref:`workload-provisioning-network` +* :ref:`workload-inspection-network` + +as part of ``kayobe overcloud post configure``. You can change this behaviour +for all networks with the ``openstack_network_registration_enabled`` variable: + +.. code-block:: yaml + :caption: ``networks.yml`` + + # Disabling registration of OpenStack networks + openstack_network_registration_enabled: false + +This can be useful if you define you networks by some other means e.g OpenTofu. + Overcloud Provisioning Network ------------------------------ @@ -951,6 +1015,8 @@ To configure a network called ``example`` with an inspection allocation pool: This pool should not overlap with a kayobe allocation pool on the same network. +.. _workload-cleaning-network: + Workload Cleaning Network ------------------------- @@ -980,6 +1046,8 @@ allocation pool: This pool should not overlap with a kayobe or inspection allocation pool on the same network. +.. _workload-provisioning-network: + Workload Provisioning Network ----------------------------- @@ -1007,6 +1075,8 @@ allocation pool: This pool should not overlap with a kayobe or inspection allocation pool on the same network. +.. _workload-inspection-network: + Workload Inspection Network --------------------------- diff --git a/etc/kayobe/networks.yml b/etc/kayobe/networks.yml index 2132fd179..12ee2917d 100644 --- a/etc/kayobe/networks.yml +++ b/etc/kayobe/networks.yml @@ -96,6 +96,17 @@ # OVS bridge. #network_patch_suffix_ovs: +############################################################################### +# OpenStack Network configuration + +# Whether to register networks in OpenStack. The following networks are +# registered: +# - Ironic provisioning network (defined by provision_wl_net_name) +# - Ironic inspection network (defined by inspection_net_name) +# - Ironic cleaning network (defined by cleaning_net_name) +# Default is true. +#openstack_network_registration_enabled: + ############################################################################### # Network routing table configuration. diff --git a/kayobe/cli/commands.py b/kayobe/cli/commands.py index ec9275e1a..ee97c5d3b 100644 --- a/kayobe/cli/commands.py +++ b/kayobe/cli/commands.py @@ -462,6 +462,20 @@ def take_action(self, parsed_args): ignore_limit=True, check=False) +class ControlHostServiceDeploy(KayobeAnsibleMixin, VaultMixin, Command): + """Deploy the Ansible control host services.""" + + def take_action(self, parsed_args): + self.app.LOG.debug("Running no-op control host service deploy") + + +class ControlHostServiceDestroy(KayobeAnsibleMixin, VaultMixin, Command): + """Destroy the Ansible control host services.""" + + def take_action(self, parsed_args): + self.app.LOG.debug("Running no-op control host service destroy") + + class ConfigurationDump(KayobeAnsibleMixin, VaultMixin, Command): """Dump Kayobe configuration. @@ -1202,12 +1216,24 @@ def take_action(self, parsed_args): class InfraVMServiceDeploy(KayobeAnsibleMixin, VaultMixin, Command): - """Run hooks for infra structure services.""" + """Run hooks for infra VM services.""" def take_action(self, parsed_args): self.app.LOG.debug("Running no-op Infra VM service deploy") +class InfraVMServiceDestroy(KayobeAnsibleMixin, VaultMixin, + Command): + """Destroy the infra VM services. + + Permanently destroy the infra VM containers, container images, and + container volumes. + """ + + def take_action(self, parsed_args): + self.app.LOG.debug("Running no-op Infra VM service destroy") + + class OvercloudInventoryDiscover(KayobeAnsibleMixin, VaultMixin, Command): """Discover the overcloud inventory from the seed's Ironic service. diff --git a/kayobe/plugins/filter/nmstate.py b/kayobe/plugins/filter/nmstate.py index 54e4c8155..7fae395f1 100644 --- a/kayobe/plugins/filter/nmstate.py +++ b/kayobe/plugins/filter/nmstate.py @@ -65,6 +65,9 @@ def _get_ip_config(context, name, inventory_hostname, defroute=None): 'rx', 'tx', 'rx-max', 'tx-max', 'rx-jumbo', 'rx-mini' } +MAX_U32 = 2**32 - 1 +MAX_VLAN_PRIORITY = 7 + def _resolve_ethtool_feature_aliases(features): """Convert ethtool feature aliases to canonical names. @@ -290,6 +293,79 @@ def _get_bond_options(context, name, inventory_hostname): return bond_options +def _validate_vlan_qos_map_int(value, network_name, direction, field): + if isinstance(value, bool) or not isinstance(value, int): + raise ValueError( + f"Network '{network_name}' has invalid {direction} QoS map " + f"'{field}' value '{value}'. Expected an integer.") + + if value < 0 or value > MAX_U32: + raise ValueError( + f"Network '{network_name}' has invalid {direction} QoS map " + f"'{field}' value '{value}'. Expected a value in range " + f"0..{MAX_U32}.") + + if direction == "ingress" and field == "from" \ + and value > MAX_VLAN_PRIORITY: + raise ValueError( + f"Network '{network_name}' has invalid ingress QoS map " + f"'from' value '{value}'. Maximum VLAN priority is " + f"{MAX_VLAN_PRIORITY}.") + + if direction == "egress" and field == "to" \ + and value > MAX_VLAN_PRIORITY: + raise ValueError( + f"Network '{network_name}' has invalid egress QoS map " + f"'to' value '{value}'. Maximum VLAN priority is " + f"{MAX_VLAN_PRIORITY}.") + + return value + + +def _validate_vlan_qos_map_entry(entry, network_name, direction): + if not isinstance(entry, dict): + raise ValueError( + f"Network '{network_name}' has invalid {direction} QoS map " + "entry format. Expected a dict with keys 'from' and 'to'.") + + if "from" not in entry or "to" not in entry: + raise ValueError( + f"Network '{network_name}' has invalid {direction} QoS map " + "entry. Required keys are 'from' and 'to'.") + + return { + "from": _validate_vlan_qos_map_int( + entry["from"], network_name, direction, "from"), + "to": _validate_vlan_qos_map_int( + entry["to"], network_name, direction, "to") + } + + +def _validate_and_sort_vlan_qos_map(raw_map, network_name, direction): + if raw_map is None: + return None + + if not isinstance(raw_map, list): + raise ValueError( + f"Network '{network_name}' has invalid {direction} QoS map " + f"format '{type(raw_map).__name__}'. Expected a list of dicts " + "with keys 'from' and 'to'.") + + normalized = [] + for entry in raw_map: + normalized.append(_validate_vlan_qos_map_entry( + entry, network_name, direction)) + + normalized.sort(key=lambda item: (item["from"], item["to"])) + return normalized + + +def _get_vlan_qos_map(context, name, inventory_hostname, direction): + qos_map = networks.net_attr( + context, name, f"{direction}_qos_map", inventory_hostname) + return _validate_and_sort_vlan_qos_map(qos_map, name, direction) + + @jinja2.pass_context def nmstate_config(context, names, inventory_hostname=None): interfaces = {} @@ -514,6 +590,17 @@ def get_iface(name): "base-iface": parent, "id": int(vlan_id) } + + ingress_qos_map = _get_vlan_qos_map( + context, name, inventory_hostname, "ingress") + if ingress_qos_map is not None: + iface["vlan"]["ingress-qos-map"] = ingress_qos_map + + egress_qos_map = _get_vlan_qos_map( + context, name, inventory_hostname, "egress") + if egress_qos_map is not None: + iface["vlan"]["egress-qos-map"] = egress_qos_map + # Ensure parent is initialized get_iface(parent) diff --git a/kayobe/tests/unit/plugins/filter/test_nmstate.py b/kayobe/tests/unit/plugins/filter/test_nmstate.py index 720200ec8..91c5966c8 100644 --- a/kayobe/tests/unit/plugins/filter/test_nmstate.py +++ b/kayobe/tests/unit/plugins/filter/test_nmstate.py @@ -494,6 +494,61 @@ def test_vlan_interface_parent_derivation(self): if i["name"] == "bond0") self.assertEqual(bond_iface["state"], "up") + def test_vlan_interface_qos_map_structured(self): + context = self._make_context( + { + "inventory_hostname": "test-host", + "ansible_facts": {"os_family": "RedHat"}, + "vlan_interface": "eth0.123", + "vlan_ingress_qos_map": [ + {"from": 7, "to": 254}, + {"from": 3, "to": 12}, + ], + "vlan_egress_qos_map": [ + {"from": 130, "to": 6}, + {"from": 129, "to": 7}, + ], + } + ) + + result = nmstate.nmstate_config(context, ["vlan"]) + vlan_iface = next( + iface for iface in result["interfaces"] + if iface["name"] == "eth0.123" + ) + self.assertEqual( + vlan_iface["vlan"]["ingress-qos-map"], + [{"from": 3, "to": 12}, {"from": 7, "to": 254}], + ) + self.assertEqual( + vlan_iface["vlan"]["egress-qos-map"], + [{"from": 129, "to": 7}, {"from": 130, "to": 6}], + ) + + def test_vlan_interface_qos_map_invalid_input(self): + test_cases = [ + ("non-list input", {"vlan_ingress_qos_map": ""}), + ("missing required key", {"vlan_ingress_qos_map": [{"from": 1}]}), + ("wrong entry type", {"vlan_ingress_qos_map": ["1:2"]}), + ( + "invalid numeric bound", + {"vlan_egress_qos_map": [{"from": 129, "to": 8}]}, + ), + ] + + for test_case, qos_map in test_cases: + variables = { + "inventory_hostname": "test-host", + "ansible_facts": {"os_family": "RedHat"}, + "vlan_interface": "eth0.123", + } + variables.update(qos_map) + context = self._make_context(variables) + + with self.subTest(test_case=test_case): + with self.assertRaises(ValueError): + nmstate.nmstate_config(context, ["vlan"]) + def test_bridge_stp_unset(self): """Test bridge with unset bridge_stp does not configure STP.""" variables = { diff --git a/lint-requirements.txt b/lint-requirements.txt new file mode 100644 index 000000000..222ad5319 --- /dev/null +++ b/lint-requirements.txt @@ -0,0 +1,6 @@ +ansible-lint>=26.0.0,<27.0.0 # MIT +bandit>=1.1.0 # Apache-2.0 +bashate>=0.2 # Apache-2.0 +doc8 # Apache-2.0 +hacking>=7.0.0,<7.1.0 # Apache-2.0 +yamllint # GPLv3 diff --git a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 index b3fd5fcd8..d2bdd1def 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 +++ b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 @@ -67,6 +67,18 @@ test_net_eth_vlan_rules: table: kayobe-test-route-table {% endif %} test_net_eth_vlan_zone: test-zone1 +{% if host_configure_network_engine == "nmstate" %} +test_net_eth_vlan_ingress_qos_map: + - from: 3 + to: 12 + - from: 7 + to: 254 +test_net_eth_vlan_egress_qos_map: + - from: 129 + to: 7 + - from: 130 + to: 6 +{% endif %} # br0: bridge with ports dummy3, dummy4. test_net_bridge_cidr: 192.168.36.0/24 diff --git a/playbooks/kayobe-overcloud-host-configure-base/run.yml b/playbooks/kayobe-overcloud-host-configure-base/run.yml index 688166c32..1863cc753 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/run.yml +++ b/playbooks/kayobe-overcloud-host-configure-base/run.yml @@ -35,6 +35,7 @@ environment: SITE_MIRROR_FQDN: "{{ zuul_site_mirror_fqdn }}" FAIL2BAN_ENABLED: "{{ fail2ban_enabled | default(false) }}" + CI_NETWORK_ENGINE: "{{ ci_network_engine | default('default') }}" - name: Test bouncing interfaces shell: diff --git a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py index 1a7a083d5..0f620472f 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py +++ b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py @@ -4,6 +4,7 @@ # Uses py.test and TestInfra. import ipaddress +import json import os import distro @@ -37,6 +38,24 @@ def _is_fail2ban_enabled(): return os.environ.get('FAIL2BAN_ENABLED', 'false').lower() == 'true' +def _is_nmstate_enabled(): + return os.environ.get('CI_NETWORK_ENGINE', 'default').lower() == 'nmstate' + + +def _get_vlan_info_data(host, interface_name): + output = host.check_output( + '/sbin/ip -d -j link show dev %s' % interface_name) + link_data = json.loads(output) + assert len(link_data) == 1 + linkinfo = link_data[0].get('linkinfo', {}) + assert linkinfo.get('info_kind') == 'vlan' + return linkinfo.get('info_data', {}) + + +def _normalize_qos_map(qos_map): + return {(entry['from'], entry['to']) for entry in qos_map} + + def test_network_ethernet(host): interface = host.interface('dummy2') assert interface.exists @@ -59,6 +78,15 @@ def test_network_ethernet_vlan(host): expected_to = 'to 192.168.35.0/24 lookup kayobe-test-route-table' assert expected_from in rules assert expected_to in rules + vlan_info_data = _get_vlan_info_data(host, 'dummy2.42') + assert vlan_info_data['id'] == 42 + if _is_nmstate_enabled(): + ingress_qos = _normalize_qos_map(vlan_info_data['ingress_qos']) + egress_qos = _normalize_qos_map(vlan_info_data['egress_qos']) + assert (3, 12) in ingress_qos + assert (7, 254) in ingress_qos + assert (129, 7) in egress_qos + assert (130, 6) in egress_qos def test_network_bridge(host): diff --git a/releasenotes/notes/add-control-host-service-destroy-deploy-skeleton-1297979c046e298e.yaml b/releasenotes/notes/add-control-host-service-destroy-deploy-skeleton-1297979c046e298e.yaml new file mode 100644 index 000000000..023be8180 --- /dev/null +++ b/releasenotes/notes/add-control-host-service-destroy-deploy-skeleton-1297979c046e298e.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds two new commands, ``kayobe control host service deploy`` and + ``kayobe control host service destroy``. This is currently a + no-op, but can be used for hooks. diff --git a/releasenotes/notes/add-infra-vm-service-destroy-skeleton-6e49240e720e2593.yaml b/releasenotes/notes/add-infra-vm-service-destroy-skeleton-6e49240e720e2593.yaml new file mode 100644 index 000000000..d57ea8dc2 --- /dev/null +++ b/releasenotes/notes/add-infra-vm-service-destroy-skeleton-6e49240e720e2593.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds new command ``kayobe infra vm service destroy``. This is currently a + no-op, but can be used for hooks. diff --git a/releasenotes/notes/adds-openstack-network-registration-enabled-2450171db1d2b63a.yaml b/releasenotes/notes/adds-openstack-network-registration-enabled-2450171db1d2b63a.yaml new file mode 100644 index 000000000..5497423d3 --- /dev/null +++ b/releasenotes/notes/adds-openstack-network-registration-enabled-2450171db1d2b63a.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds a new feature flag, ``openstack_network_registration_enabled``, + to control whether or not networks defined in Kayobe are registered + as Neutron networks in OpenStack. diff --git a/releasenotes/notes/nmstate-vlan-qos-map-7c4f0f7f57128e7a.yaml b/releasenotes/notes/nmstate-vlan-qos-map-7c4f0f7f57128e7a.yaml new file mode 100644 index 000000000..c09326b9f --- /dev/null +++ b/releasenotes/notes/nmstate-vlan-qos-map-7c4f0f7f57128e7a.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds native nmstate support for structured VLAN QoS mapping in Kayobe via + ``_ingress_qos_map`` and ``_egress_qos_map``. + These are rendered to nmstate VLAN keys + ``ingress-qos-map`` and ``egress-qos-map``. diff --git a/setup.cfg b/setup.cfg index 61c698637..7f385d1e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,8 @@ kayobe.cli= control_host_configure = kayobe.cli.commands:ControlHostConfigure control_host_package_update = kayobe.cli.commands:ControlHostPackageUpdate control_host_upgrade = kayobe.cli.commands:ControlHostUpgrade + control_host_service_deploy = kayobe.cli.commands:ControlHostServiceDeploy + control_host_service_destroy = kayobe.cli.commands:ControlHostServiceDestroy configuration_dump = kayobe.cli.commands:ConfigurationDump environment_create = kayobe.cli.commands:EnvironmentCreate inventory= kayobe.cli.commands:Inventory @@ -110,6 +112,7 @@ kayobe.cli= infra_vm_host_command_run = kayobe.cli.commands:InfraVMHostCommandRun infra_vm_host_package_update = kayobe.cli.commands:InfraVMHostPackageUpdate infra_vm_service_deploy = kayobe.cli.commands:InfraVMServiceDeploy + infra_vm_service_destroy = kayobe.cli.commands:InfraVMServiceDestroy kayobe.cli.baremetal_compute_register = hooks = kayobe.cli.commands:HookDispatcher @@ -139,6 +142,10 @@ kayobe.cli.control_host_package_update = hooks = kayobe.cli.commands:HookDispatcher kayobe.cli.control_host_upgrade = hooks = kayobe.cli.commands:HookDispatcher +kayobe.cli.control_host_service_deploy = + hooks = kayobe.cli.commands:HookDispatcher +kayobe.cli.control_host_service_destroy = + hooks = kayobe.cli.commands:HookDispatcher kayobe.cli.configuration_dump = hooks = kayobe.cli.commands:HookDispatcher kayobe.cli.environment_create = @@ -255,3 +262,5 @@ kayobe.cli.infra_vm_host_package_update = hooks = kayobe.cli.commands:HookDispatcher kayobe.cli.infra_vm_service_deploy = hooks = kayobe.cli.commands:HookDispatcher +kayobe.cli.infra_vm_service_destroy = + hooks = kayobe.cli.commands:HookDispatcher diff --git a/test-requirements.txt b/test-requirements.txt index 0f57abc8e..41948fe06 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,13 +1,3 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -ansible-lint>=26.0.0,<27.0.0 # MIT -bandit>=1.1.0 # Apache-2.0 -bashate>=0.2 # Apache-2.0 coverage>=4.0 # Apache-2.0 -doc8 # Apache-2.0 -hacking>=7.0.0,<7.1.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 stestr # Apache-2.0 -yamllint # GPLv3 diff --git a/tox.ini b/tox.ini index 71ce94a0d..d284e89ae 100644 --- a/tox.ini +++ b/tox.ini @@ -27,10 +27,9 @@ commands = stestr run {posargs} [testenv:pep8] # sphinx8 needs the sphinx package which is required via doc/requirements.txt deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt - -r{toxinidir}/test-requirements.txt + -r{toxinidir}/lint-requirements.txt commands = bash {toxinidir}/tools/run-bashate.sh flake8 {posargs} kayobe @@ -79,9 +78,8 @@ setenv = ANSIBLE_ROLES_PATH = {toxinidir}/ansible/roles deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt + -r{toxinidir}/lint-requirements.txt commands = {[testenv:ansible-lint]commands}