Merge "Add test "attach_previously_used_port_to_new_instance""
diff --git a/.zuul.yaml b/.zuul.yaml
index 7a3c106..2e99198 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -80,6 +80,7 @@
- subnet_allocation
- subnet-dns-publish-fixed-ip
- subnetpool-prefix-ops
+ - tag-ports-during-bulk-creation
- trunk
- trunk-details
- uplink-status-propagation
@@ -104,13 +105,14 @@
neutron-network-segment-range: true
neutron-port-forwarding: true
neutron-conntrack-helper: true
+ neutron-tag-ports-during-bulk-creation: true
devstack_local_conf:
post-config:
$NEUTRON_CONF:
QUOTAS:
quota_router: 100
quota_floatingip: 500
- quota_security_group: 100
+ quota_security_group: 150
quota_security_group_rule: 1000
# NOTE(slaweq): We can get rid of this hardcoded absolute path when
# devstack-tempest job will be switched to use lib/neutron instead of
@@ -248,8 +250,16 @@
name: neutron-tempest-plugin-api-rocky
nodeset: openstack-single-node-xenial
parent: neutron-tempest-plugin-api
+ description: |
+ This job run on py2 for stable/rocky gate.
override-checkout: stable/rocky
- vars:
+ required-projects: &required-projects-rocky
+ - openstack/devstack-gate
+ - openstack/neutron
+ - name: openstack/neutron-tempest-plugin
+ override-checkout: 0.9.0
+ - openstack/tempest
+ vars: &api_vars_rocky
branch_override: stable/rocky
# TODO(slaweq): find a way to put this list of extensions in
# neutron repository and keep it different per branch,
@@ -316,6 +326,25 @@
USE_PYTHON3: false
NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_tempest) | join(',') }}"
TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+ # NOTE(gmann): This job run on py2 for stable/rocky gate.
+ branches:
+ - stable/rocky
+
+
+- job:
+ name: neutron-tempest-plugin-api-rocky
+ nodeset: openstack-single-node-xenial
+ parent: neutron-tempest-plugin-api
+ description: |
+ This job run on py3 for other than stable/rocky gate
+ which is nothing but neutron-tempest-pluign master gate.
+ override-checkout: stable/rocky
+ required-projects: *required-projects-rocky
+ vars:
+ <<: *api_vars_rocky
+ devstack_localrc:
+ USE_PYTHON3: True
+ branches: ^(?!stable/rocky).*$
- job:
name: neutron-tempest-plugin-api-stein
@@ -486,9 +515,7 @@
tempest_test_regex: ^neutron_tempest_plugin\.scenario
devstack_localrc:
PHYSICAL_NETWORK: default
- DOWNLOAD_DEFAULT_IMAGES: false
- IMAGE_URLS: "http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img"
- DEFAULT_IMAGE_NAME: cirros-0.3.4-i386-disk
+ IMAGE_URLS: https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
ADVANCED_IMAGE_NAME: ubuntu-16.04-server-cloudimg-amd64-disk1
ADVANCED_INSTANCE_TYPE: ds512M
ADVANCED_INSTANCE_USER: ubuntu
@@ -553,15 +580,39 @@
- job:
name: neutron-tempest-plugin-scenario-openvswitch-rocky
parent: neutron-tempest-plugin-scenario-openvswitch
+ description: |
+ This job run on py2 for stable/rocky gate.
nodeset: openstack-single-node-xenial
override-checkout: stable/rocky
- vars:
+ required-projects: *required-projects-rocky
+ vars: &scenario_vars_rocky
branch_override: stable/rocky
network_api_extensions: *api_extensions_rocky
devstack_localrc:
USE_PYTHON3: false
NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+ # NOTE(bcafarel): newer tests, unstable on rocky branch
+ tempest_black_regex: "\
+ (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
+ (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+ branches:
+ - stable/rocky
+
+- job:
+ name: neutron-tempest-plugin-scenario-openvswitch-rocky
+ parent: neutron-tempest-plugin-scenario-openvswitch
+ nodeset: openstack-single-node-xenial
+ description: |
+ This job run on py3 for other than stable/rocky gate
+ which is nothing but neutron-tempest-pluign master gate.
+ override-checkout: stable/rocky
+ required-projects: *required-projects-rocky
+ vars:
+ <<: *scenario_vars_rocky
+ devstack_localrc:
+ USE_PYTHON3: True
+ branches: ^(?!stable/rocky).*$
- job:
name: neutron-tempest-plugin-scenario-openvswitch-stein
@@ -620,14 +671,41 @@
name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-rocky
parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
nodeset: openstack-single-node-xenial
+ description: |
+ This job run on py2 for stable/rocky gate.
override-checkout: stable/rocky
- vars:
+ required-projects: *required-projects-rocky
+ vars: &openvswitch_vars_rocky
branch_override: stable/rocky
network_api_extensions: *api_extensions_rocky
devstack_localrc:
USE_PYTHON3: false
NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+ # TODO(bcafarel): remove trunks subport_connectivity test from blacklist
+ # when bug https://bugs.launchpad.net/neutron/+bug/1838760 will be fixed
+ # NOTE(bcafarel): other are newer tests, unstable on rocky branch
+ tempest_black_regex: "\
+ (^neutron_tempest_plugin.scenario.test_trunk.TrunkTest.test_subport_connectivity)|\
+ (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
+ (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+ branches:
+ - stable/rocky
+
+- job:
+ name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-rocky
+ parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
+ nodeset: openstack-single-node-xenial
+ description: |
+ This job run on py3 for other than stable/rocky gate
+ which is nothing but neutron-tempest-pluign master gate.
+ override-checkout: stable/rocky
+ required-projects: *required-projects-rocky
+ vars:
+ <<: *openvswitch_vars_rocky
+ devstack_localrc:
+ USE_PYTHON3: True
+ branches: ^(?!stable/rocky).*$
- job:
name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-stein
@@ -711,9 +789,12 @@
- job:
name: neutron-tempest-plugin-scenario-linuxbridge-rocky
parent: neutron-tempest-plugin-scenario-linuxbridge
+ description: |
+ This job run on py2 for stable/rocky gate.
nodeset: openstack-single-node-xenial
override-checkout: stable/rocky
- vars:
+ required-projects: *required-projects-rocky
+ vars: &linuxbridge_vars_rocky
branch_override: stable/rocky
network_api_extensions: *api_extensions_rocky
devstack_localrc:
@@ -728,6 +809,27 @@
$TEMPEST_CONFIG:
neutron_plugin_options:
q_agent: None
+ # NOTE(bcafarel): newer tests, unstable on rocky branch
+ tempest_black_regex: "\
+ (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
+ (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+ branches:
+ - stable/rocky
+
+- job:
+ name: neutron-tempest-plugin-scenario-linuxbridge-rocky
+ parent: neutron-tempest-plugin-scenario-linuxbridge
+ nodeset: openstack-single-node-xenial
+ description: |
+ This job run on py3 for other than stable/rocky gate
+ which is nothing but neutron-tempest-pluign master gate.
+ override-checkout: stable/rocky
+ required-projects: *required-projects-rocky
+ vars:
+ <<: *linuxbridge_vars_rocky
+ devstack_localrc:
+ USE_PYTHON3: True
+ branches: ^(?!stable/rocky).*$
- job:
name: neutron-tempest-plugin-scenario-linuxbridge-stein
@@ -774,9 +876,7 @@
USE_PYTHON3: true
NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_dvr) | join(',') }}"
PHYSICAL_NETWORK: default
- DOWNLOAD_DEFAULT_IMAGES: false
- IMAGE_URLS: "http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img"
- DEFAULT_IMAGE_NAME: cirros-0.3.4-i386-disk
+ IMAGE_URLS: https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
ADVANCED_IMAGE_NAME: ubuntu-16.04-server-cloudimg-amd64-disk1
ADVANCED_INSTANCE_TYPE: ds512M
ADVANCED_INSTANCE_USER: ubuntu
@@ -911,14 +1011,42 @@
- job:
name: neutron-tempest-plugin-dvr-multinode-scenario-rocky
parent: neutron-tempest-plugin-dvr-multinode-scenario
+ description: |
+ This job run on py2 for stable/rocky gate.
nodeset: openstack-two-node-xenial
override-checkout: stable/rocky
- vars:
+ required-projects: *required-projects-rocky
+ vars: &multinode_scenario_vars_rocky
branch_override: stable/rocky
network_api_extensions_common: *api_extensions_rocky
devstack_localrc:
USE_PYTHON3: false
TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+ # NOTE(bcafarel): newer tests, unstable on rocky branch
+ tempest_black_regex: "\
+ (^neutron_tempest_plugin.scenario.test_port_forwardings.PortForwardingTestJSON.test_port_forwarding_to_2_servers)|\
+ (^neutron_tempest_plugin.scenario.test_security_groups.NetworkSecGroupTest.test_multiple_ports_portrange_remote)"
+ branches:
+ - stable/rocky
+
+- job:
+ name: neutron-tempest-plugin-dvr-multinode-scenario-rocky
+ parent: neutron-tempest-plugin-dvr-multinode-scenario
+ nodeset: openstack-two-node-xenial
+ description: |
+ This job run on py3 for other than stable/rocky gate
+ which is nothing but neutron-tempest-pluign master gate.
+ override-checkout: stable/rocky
+ vars:
+ <<: *multinode_scenario_vars_rocky
+ devstack_localrc:
+ USE_PYTHON3: True
+ required-projects: *required-projects-rocky
+ group-vars:
+ subnode:
+ devstack_localrc:
+ USE_PYTHON3: True
+ branches: ^(?!stable/rocky).*$
- job:
name: neutron-tempest-plugin-dvr-multinode-scenario-stein
@@ -948,8 +1076,6 @@
vars:
devstack_localrc:
DESIGNATE_BACKEND_DRIVER: bind9
- DOWNLOAD_DEFAULT_IMAGES: false
- IMAGE_URLS: http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,
Q_AGENT: openvswitch
# In this job advanced image is not needed, so it's name should be
# empty
@@ -989,6 +1115,8 @@
- openstack/neutron
- name: openstack/neutron-tempest-plugin
override-checkout: 0.3.0
+ - name: openstack/designate-tempest-plugin
+ override-checkout: 0.7.0
- openstack/tempest
vars:
branch_override: stable/queens
@@ -1004,19 +1132,53 @@
- job:
name: neutron-tempest-plugin-designate-scenario-rocky
parent: neutron-tempest-plugin-designate-scenario
+ description: |
+ This job run on py2 for stable/rocky gate.
nodeset: openstack-single-node-xenial
override-checkout: stable/rocky
- vars:
+ required-projects:
+ - openstack/devstack-gate
+ - openstack/neutron
+ - name: openstack/neutron-tempest-plugin
+ override-checkout: 0.9.0
+ - name: openstack/designate-tempest-plugin
+ override-checkout: 0.7.0
+ - openstack/tempest
+ vars: &designate_scenario_vars_rocky
branch_override: stable/rocky
network_api_extensions_common: *api_extensions_rocky
devstack_localrc:
USE_PYTHON3: false
TEMPEST_PLUGINS: '"/opt/stack/designate-tempest-plugin /opt/stack/neutron-tempest-plugin"'
+ branches:
+ - stable/rocky
+
+- job:
+ name: neutron-tempest-plugin-designate-scenario-rocky
+ parent: neutron-tempest-plugin-designate-scenario
+ nodeset: openstack-single-node-xenial
+ description: |
+ This job run on py3 for other than stable/rocky gate
+ which is nothing but neutron-tempest-plugin master gate.
+ override-checkout: stable/rocky
+ required-projects: *required-projects-rocky
+ vars:
+ <<: *designate_scenario_vars_rocky
+ devstack_localrc:
+ USE_PYTHON3: True
+ branches: ^(?!stable/rocky).*$
- job:
name: neutron-tempest-plugin-designate-scenario-stein
parent: neutron-tempest-plugin-designate-scenario
override-checkout: stable/stein
+ required-projects:
+ - openstack/devstack-gate
+ - openstack/neutron
+ - openstack/neutron-tempest-plugin
+ - name: openstack/designate-tempest-plugin
+ override-checkout: 0.7.0
+ - openstack/tempest
vars:
branch_override: stable/stein
network_api_extensions_common: *api_extensions_stein
@@ -1255,9 +1417,6 @@
templates:
- build-openstack-docs-pti
- neutron-tempest-plugin-jobs
- # TODO(slaweq): bring rocky jobs back when dropping py27
- # drama will be finally over
- # - neutron-tempest-plugin-jobs-rocky
- neutron-tempest-plugin-jobs-stein
- neutron-tempest-plugin-jobs-train
- check-requirements
diff --git a/neutron_tempest_plugin/api/test_ports.py b/neutron_tempest_plugin/api/test_ports.py
index 52783b9..8867eee 100644
--- a/neutron_tempest_plugin/api/test_ports.py
+++ b/neutron_tempest_plugin/api/test_ports.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
from tempest.common import utils
from tempest.lib import decorators
@@ -203,3 +205,62 @@
@decorators.idempotent_id('74293e59-d794-4a93-be09-38667199ef68')
def test_list_pagination_page_reverse_with_href_links(self):
self._test_list_pagination_page_reverse_with_href_links()
+
+
+class PortsTaggingOnCreationTestJSON(base.BaseNetworkTest):
+
+ _tags = [
+ ['tag-1', 'tag-2', 'tag-3'],
+ ['tag-1', 'tag-2'],
+ ['tag-1', 'tag-3'],
+ []
+ ]
+
+ @classmethod
+ def resource_setup(cls):
+ super(PortsTaggingOnCreationTestJSON, cls).resource_setup()
+ cls.network = cls.create_network()
+
+ def _create_ports_in_bulk(self, ports):
+ body = self.client.create_bulk_port(ports)
+ for port in body['ports']:
+ self.ports.append(port)
+ return body
+
+ def _create_ports_list(self):
+ num_ports = len(self._tags)
+ net_id = self.network['id']
+ port = {'port': {'network_id': net_id,
+ 'admin_state_up': True}}
+ return [copy.deepcopy(port) for x in range(num_ports)]
+
+ @decorators.idempotent_id('5cf26014-fdd3-4a6d-b94d-a05f0c55da89')
+ @utils.requires_ext(extension="tag-ports-during-bulk-creation",
+ service="network")
+ def test_tagging_ports_during_bulk_creation(self):
+ ports = self._create_ports_list()
+ ports_tags_map = {}
+ for port, tags in zip(ports, self._tags):
+ port['port']['tags'] = tags
+ port['port']['name'] = '-'.join(tags)
+ ports_tags_map[port['port']['name']] = tags
+ body = self._create_ports_in_bulk(ports)
+ for port in body['ports']:
+ self.assertEqual(ports_tags_map[port['name']], port['tags'])
+
+ @decorators.idempotent_id('33eda785-a08a-44a0-1bbb-fb50a2f1cd78')
+ @utils.requires_ext(extension="tag-ports-during-bulk-creation",
+ service="network")
+ def test_tagging_ports_during_bulk_creation_no_tags(self):
+ ports = self._create_ports_list()
+ body = self._create_ports_in_bulk(ports)
+ for port in body['ports']:
+ self.assertFalse(port['tags'])
+
+ @decorators.idempotent_id('6baa43bf-88fb-8bca-6051-97ea1a5e8f4f')
+ @utils.requires_ext(extension="tag-ports-during-bulk-creation",
+ service="network")
+ def test_tagging_ports_during_creation(self):
+ port = {'name': 'port', 'tags': self._tags[0]}
+ body = self.create_port(self.network, **port)
+ self.assertEqual(self._tags[0], body['tags'])
diff --git a/neutron_tempest_plugin/api/test_security_groups.py b/neutron_tempest_plugin/api/test_security_groups.py
index cdd7017..0221e79 100644
--- a/neutron_tempest_plugin/api/test_security_groups.py
+++ b/neutron_tempest_plugin/api/test_security_groups.py
@@ -23,6 +23,9 @@
from neutron_tempest_plugin.api import base
from neutron_tempest_plugin.api import base_security_groups
+from oslo_log import log
+
+LOG = log.getLogger(__name__)
class SecGroupTest(base.BaseAdminNetworkTest):
@@ -76,6 +79,39 @@
self.assertIn(
security_group_rule['id'], observerd_security_group_rules_ids)
+ @decorators.idempotent_id('b5923b1a-4d33-44e1-af25-088dcb55b02b')
+ def test_list_security_group_rules_contains_all_rules(self):
+ """Test list security group rules.
+
+ This test checks if all SG rules which belongs to the tenant OR
+ which belongs to the tenant's security group are listed.
+ """
+ security_group = self.create_security_group()
+ protocol = random.choice(list(base_security_groups.V4_PROTOCOL_NAMES))
+ security_group_rule = self.create_security_group_rule(
+ security_group=security_group,
+ project={'id': self.admin_client.tenant_id},
+ client=self.admin_client,
+ protocol=protocol,
+ direction=constants.INGRESS_DIRECTION)
+
+ # Create also other SG with some custom rule to check that regular user
+ # can't see this rule
+ admin_security_group = self.create_security_group(
+ project={'id': self.admin_client.tenant_id},
+ client=self.admin_client)
+ admin_security_group_rule = self.create_security_group_rule(
+ security_group=admin_security_group,
+ project={'id': self.admin_client.tenant_id},
+ client=self.admin_client,
+ protocol=protocol,
+ direction=constants.INGRESS_DIRECTION)
+
+ rules = self.client.list_security_group_rules()['security_group_rules']
+ rules_ids = [rule['id'] for rule in rules]
+ self.assertIn(security_group_rule['id'], rules_ids)
+ self.assertNotIn(admin_security_group_rule['id'], rules_ids)
+
@decorators.idempotent_id('7c0ecb10-b2db-11e6-9b14-000c29248b0d')
def test_create_bulk_sec_groups(self):
# Creates 2 sec-groups in one request
@@ -121,8 +157,7 @@
project_id = self.client.tenant_id
self.admin_client.update_quotas(project_id, **{'security_group': val})
self.addCleanup(self.admin_client.update_quotas,
- project_id,
- **{'security_group': sg_quota})
+ project_id, **{'security_group': sg_quota})
def _get_sg_quota(self):
project_id = self.client.tenant_id
@@ -160,9 +195,9 @@
self._create_max_allowed_sg_amount()
quota_set = self._get_sg_quota()
self.assertEqual(quota_set, new_quota,
- "Security group quota was not changed correctly")
+ "Security group quota was not changed correctly")
self.assertEqual(quota_set, self._get_sg_amount(),
- "Amount of security groups doesn't match quota")
+ "Amount of security groups doesn't match quota")
@decorators.idempotent_id('ba95676c-8d9a-4482-b4ec-74d51a4602a6')
def test_sg_quota_decrease_less_than_created(self):
@@ -179,6 +214,56 @@
self.assertGreater(new_sg_amount, sg_amount)
+class BaseSecGroupRulesQuota(base.BaseAdminNetworkTest):
+
+ def _create_max_allowed_sg_rules_amount(self, port_index=1):
+ sg_rules_amount = self._get_sg_rules_amount()
+ sg_rules_quota = self._get_sg_rules_quota()
+ sg_rules_to_create = sg_rules_quota - sg_rules_amount
+ port_index += sg_rules_to_create
+ self._create_security_group_rules(sg_rules_to_create,
+ port_index=port_index)
+
+ def _create_security_group_rules(self, amount, port_index=1):
+ for i in range(amount):
+ self.create_security_group_rules(**{
+ 'project_id': self.client.tenant_id,
+ 'direction': 'ingress',
+ 'port_range_max': port_index + i,
+ 'port_range_min': port_index + i,
+ 'protocol': 'tcp'})
+
+ def _increase_sg_rules_quota(self):
+ sg_rules_quota = self._get_sg_rules_quota()
+ new_sg_rules_quota = 2 * sg_rules_quota
+ self._set_sg_rules_quota(new_sg_rules_quota)
+ return new_sg_rules_quota
+
+ def _decrease_sg_rules_quota(self):
+ sg_rules_quota = self._get_sg_rules_quota()
+ new_sg_rules_quota = sg_rules_quota // 2
+ self._set_sg_rules_quota(new_sg_rules_quota)
+ return new_sg_rules_quota
+
+ def _set_sg_rules_quota(self, val):
+ project_id = self.client.tenant_id
+ self.admin_client.update_quotas(project_id,
+ **{'security_group_rule': val})
+ LOG.info('Trying to update security group rule quota {} '.format(val))
+
+ def _get_sg_rules_quota(self):
+ project_id = self.client.tenant_id
+ quotas = self.admin_client.show_quotas(project_id)
+ return quotas['quota']['security_group_rule']
+
+ def _get_sg_rules_amount(self):
+ project_id = self.client.tenant_id
+ filter_query = {'project_id': project_id}
+ security_group_rules = self.client.list_security_group_rules(
+ **filter_query)
+ return len(security_group_rules['security_group_rules'])
+
+
class SecGroupProtocolTest(base.BaseNetworkTest):
protocol_names = base_security_groups.V4_PROTOCOL_NAMES
@@ -272,19 +357,19 @@
name=data_utils.rand_name('test-sg'),
project={'id': self.admin_client.tenant_id})
- def _make_admin_sg_shared_to_tenant_id(self, tenant_id):
+ def _make_admin_sg_shared_to_project_id(self, project_id):
sg = self._create_security_group()
rbac_policy = self.admin_client.create_rbac_policy(
object_type='security_group',
object_id=sg['id'],
action='access_as_shared',
- target_tenant=tenant_id,
+ target_tenant=project_id,
)['rbac_policy']
return {'security_group': sg, 'rbac_policy': rbac_policy}
@decorators.idempotent_id('2a41eb8f-2a35-11e9-bae9-acde48001122')
def test_policy_target_update(self):
- res = self._make_admin_sg_shared_to_tenant_id(
+ res = self._make_admin_sg_shared_to_project_id(
self.client.tenant_id)
# change to client2
update_res = self.admin_client.update_rbac_policy(
@@ -298,7 +383,7 @@
@decorators.idempotent_id('2a619a8a-2a35-11e9-90d9-acde48001122')
def test_port_presence_prevents_policy_rbac_policy_deletion(self):
- res = self._make_admin_sg_shared_to_tenant_id(
+ res = self._make_admin_sg_shared_to_project_id(
self.client2.tenant_id)
sg_id = res['security_group']['id']
net = self.create_network(client=self.client2)
@@ -332,7 +417,7 @@
# ensure that 'client2' can't see the rbac-policy sharing the
# sg to it because the rbac-policy belongs to 'client'
self.assertNotIn(rbac_policy['id'], [p['id'] for p in
- self.client2.list_rbac_policies()['rbac_policies']])
+ self.client2.list_rbac_policies()['rbac_policies']])
@decorators.idempotent_id('2a9fd480-2a35-11e9-9cb6-acde48001122')
def test_filter_fields(self):
@@ -341,14 +426,14 @@
object_type='security_group', object_id=sg['id'],
action='access_as_shared', target_tenant=self.client2.tenant_id)
field_args = (('id',), ('id', 'action'), ('object_type', 'object_id'),
- ('tenant_id', 'target_tenant'))
+ ('project_id', 'target_tenant'))
for fields in field_args:
res = self.admin_client.list_rbac_policies(fields=fields)
self.assertEqual(set(fields), set(res['rbac_policies'][0].keys()))
@decorators.idempotent_id('2abf8f9e-2a35-11e9-85f7-acde48001122')
def test_rbac_policy_show(self):
- res = self._make_admin_sg_shared_to_tenant_id(
+ res = self._make_admin_sg_shared_to_project_id(
self.client.tenant_id)
p1 = res['rbac_policy']
p2 = self.admin_client.create_rbac_policy(
@@ -384,7 +469,7 @@
@decorators.idempotent_id('2aff3900-2a35-11e9-96b3-acde48001122')
def test_regular_client_blocked_from_sharing_anothers_policy(self):
- sg = self._make_admin_sg_shared_to_tenant_id(
+ sg = self._make_admin_sg_shared_to_project_id(
self.client.tenant_id)['security_group']
with testtools.ExpectedException(exceptions.BadRequest):
self.client.create_rbac_policy(
diff --git a/neutron_tempest_plugin/common/shell.py b/neutron_tempest_plugin/common/shell.py
index bd4a7a3..eebb07d 100644
--- a/neutron_tempest_plugin/common/shell.py
+++ b/neutron_tempest_plugin/common/shell.py
@@ -46,7 +46,7 @@
:param timeout: command execution timeout in seconds
- :param check: when False it doesn't raises ShellCommandError when
+ :param check: when False it doesn't raises ShellCommandFailed when
exit status is not zero. True by default
:returns: STDOUT text when command execution terminates with zero exit
@@ -57,7 +57,7 @@
try to read STDOUT and STDERR buffers (not fully implemented) before
raising the exception.
- :raises ShellCommandError: when command execution terminates with non-zero
+ :raises ShellCommandFailed: when command execution terminates with non-zero
exit status.
"""
ssh_client = ssh_client or SSH_PROXY_CLIENT
@@ -110,7 +110,7 @@
except lib_exc.SSHExecCommandFailed as ex:
# Please note class SSHExecCommandFailed has been re-based on
- # top of ShellCommandError
+ # top of ShellCommandFailed
stdout = ex.stdout
stderr = ex.stderr
exit_status = ex.exit_status
@@ -174,7 +174,7 @@
stdout=self.stdout)
elif self.exit_status != 0:
- raise exceptions.ShellCommandError(command=self.command,
- exit_status=self.exit_status,
- stderr=self.stderr,
- stdout=self.stdout)
+ raise exceptions.ShellCommandFailed(command=self.command,
+ exit_status=self.exit_status,
+ stderr=self.stderr,
+ stdout=self.stdout)
diff --git a/neutron_tempest_plugin/common/utils.py b/neutron_tempest_plugin/common/utils.py
index 631f75b..c8ff194 100644
--- a/neutron_tempest_plugin/common/utils.py
+++ b/neutron_tempest_plugin/common/utils.py
@@ -26,6 +26,7 @@
from urllib import parse as urlparse
import eventlet
+from tempest.lib import exceptions
SCHEMA_PORT_MAPPING = {
"http": 80,
@@ -106,3 +107,22 @@
if scheme in SCHEMA_PORT_MAPPING and not port:
netloc = netloc + ":" + str(SCHEMA_PORT_MAPPING[scheme])
return urlparse.urlunparse((scheme, netloc, url, params, query, fragment))
+
+
+def kill_nc_process(ssh_client):
+ cmd = "killall -q nc"
+ try:
+ ssh_client.exec_command(cmd)
+ except exceptions.SSHExecCommandFailed:
+ pass
+
+
+def spawn_http_server(ssh_client, port, message):
+ cmd = ("(echo -e 'HTTP/1.1 200 OK\r\n'; echo '%(msg)s') "
+ "| sudo nc -lp %(port)d &" % {'msg': message, 'port': port})
+ ssh_client.exec_command(cmd)
+
+
+def call_url_remote(ssh_client, url):
+ cmd = "curl %s --retry 3 --connect-timeout 2" % url
+ return ssh_client.exec_command(cmd)
diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py
index 2f2f913..28d6b76 100644
--- a/neutron_tempest_plugin/config.py
+++ b/neutron_tempest_plugin/config.py
@@ -123,6 +123,12 @@
default=False,
help='Allow creation of shared resources.'
'The default value is false.'),
+ cfg.BoolOpt('is_igmp_snooping_enabled',
+ default=False,
+ help='Indicates whether IGMP snooping is enabled or not. '
+ 'If True, multicast test(s) will assert that multicast '
+ 'traffic is not being flooded to all ports. Defaults '
+ 'to False.'),
]
# TODO(amuller): Redo configuration options registration as part of the planned
diff --git a/neutron_tempest_plugin/exceptions.py b/neutron_tempest_plugin/exceptions.py
index 895cb40..398bc1c 100644
--- a/neutron_tempest_plugin/exceptions.py
+++ b/neutron_tempest_plugin/exceptions.py
@@ -96,5 +96,5 @@
exceptions.SSHExecCommandFailed, ShellCommandFailed)
# Above code created a new SSHExecCommandFailed class based on top
-# of ShellCommandError
+# of ShellCommandFailed
assert issubclass(exceptions.SSHExecCommandFailed, ShellCommandFailed)
diff --git a/neutron_tempest_plugin/scenario/admin/test_floatingip.py b/neutron_tempest_plugin/scenario/admin/test_floatingip.py
index 511452c..a08acc3 100644
--- a/neutron_tempest_plugin/scenario/admin/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/admin/test_floatingip.py
@@ -85,7 +85,7 @@
server_ssh_clients.append(ssh.Client(
fips[i]['floating_ip_address'], CONF.validation.image_ssh_user,
pkey=self.keypair['private_key']))
- return server_ssh_clients, fips
+ return servers, server_ssh_clients, fips
@decorators.idempotent_id('6bba729b-3fb6-494b-9e1e-82bbd89a1045')
def test_two_vms_fips(self):
@@ -99,6 +99,7 @@
hyper = self._list_hypervisors()[0]['hypervisor_hostname']
# Get availability zone list to pass it for vm creation
avail_zone = self._list_availability_zones()[0]['zoneName']
- server_ssh_clients, fips = self._create_vms(hyper, avail_zone)
+ servers, server_ssh_clients, fips = self._create_vms(hyper, avail_zone)
self.check_remote_connectivity(
- server_ssh_clients[0], fips[1]['floating_ip_address'])
+ server_ssh_clients[0], fips[1]['floating_ip_address'],
+ servers=servers)
diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py
index 42bd33b..7b66494 100644
--- a/neutron_tempest_plugin/scenario/base.py
+++ b/neutron_tempest_plugin/scenario/base.py
@@ -12,6 +12,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
+import distutils
+import re
import subprocess
from debtcollector import removals
@@ -30,6 +32,7 @@
from neutron_tempest_plugin.common import shell
from neutron_tempest_plugin.common import ssh
from neutron_tempest_plugin import config
+from neutron_tempest_plugin import exceptions
from neutron_tempest_plugin.scenario import constants
CONF = config.CONF
@@ -37,6 +40,45 @@
LOG = log.getLogger(__name__)
+def get_ncat_version(ssh_client=None):
+ cmd = "ncat --version 2>&1"
+ try:
+ version_result = shell.execute(cmd, ssh_client=ssh_client).stdout
+ except exceptions.ShellCommandFailed:
+ m = None
+ else:
+ m = re.match(r"Ncat: Version ([\d.]+) *.", version_result)
+ # NOTE(slaweq): by default lets assume we have ncat 7.60 which is in Ubuntu
+ # 18.04 which is used on u/s gates
+ return distutils.version.StrictVersion(m.group(1) if m else '7.60')
+
+
+def get_ncat_server_cmd(port, protocol, msg):
+ udp = ''
+ if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
+ udp = '-u'
+ cmd = "nc %(udp)s -p %(port)s -lk " % {
+ 'udp': udp, 'port': port}
+ if CONF.neutron_plugin_options.default_image_is_advanced:
+ cmd += "-c 'echo %s' &" % msg
+ else:
+ cmd += "-e echo %s &" % msg
+ return cmd
+
+
+def get_ncat_client_cmd(ip_address, port, protocol):
+ udp = ''
+ if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
+ udp = '-u'
+ cmd = 'echo "knock knock" | nc '
+ ncat_version = get_ncat_version()
+ if ncat_version > distutils.version.StrictVersion('7.60'):
+ cmd += '-z '
+ cmd += '-w 1 %(udp)s %(host)s %(port)s' % {
+ 'udp': udp, 'host': ip_address, 'port': port}
+ return cmd
+
+
class BaseTempestTestCase(base_api.BaseNetworkTest):
def create_server(self, flavor_ref, image_ref, key_name, networks,
@@ -426,13 +468,10 @@
Listener is created always on remote host.
"""
- udp = ''
- if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
- udp = '-u'
- cmd = "sudo nc %(udp)s -p %(port)s -lk -c echo %(msg)s &" % {
- 'udp': udp, 'port': port, 'msg': echo_msg}
try:
- return ssh_client.exec_command(cmd)
+ return ssh_client.execute_script(
+ get_ncat_server_cmd(port, protocol, echo_msg),
+ become_root=True)
except lib_exc.SSHTimeout as ssh_e:
LOG.debug(ssh_e)
self._log_console_output([server])
@@ -443,11 +482,7 @@
Client is always executed locally on host where tests are executed.
"""
- udp = ''
- if protocol.lower() == neutron_lib_constants.PROTO_NAME_UDP:
- udp = '-u'
- cmd = 'echo "knock knock" | nc -w 1 %(udp)s %(host)s %(port)s' % {
- 'udp': udp, 'host': ip_address, 'port': port}
+ cmd = get_ncat_client_cmd(ip_address, port, protocol)
result = shell.execute_local_command(cmd)
self.assertEqual(0, result.exit_status)
return result.stdout
diff --git a/neutron_tempest_plugin/scenario/test_connectivity.py b/neutron_tempest_plugin/scenario/test_connectivity.py
index 5aa8f73..1a7468a 100644
--- a/neutron_tempest_plugin/scenario/test_connectivity.py
+++ b/neutron_tempest_plugin/scenario/test_connectivity.py
@@ -66,6 +66,8 @@
for vm in vms:
self.wait_for_server_active(vm['server'])
+ return vms
+
@decorators.idempotent_id('8944b90d-1766-4669-bd8a-672b5d106bb7')
def test_connectivity_through_2_routers(self):
ap1_net = self.create_network()
@@ -109,7 +111,7 @@
routes=[{"destination": ap1_subnet['cidr'],
"nexthop": ap1_wan_port['fixed_ips'][0]['ip_address']}])
- self._create_servers(ap1_internal_port, ap2_internal_port)
+ servers = self._create_servers(ap1_internal_port, ap2_internal_port)
ap1_fip = self.create_and_associate_floatingip(
ap1_internal_port['id'])
@@ -118,7 +120,8 @@
pkey=self.keypair['private_key'])
self.check_remote_connectivity(
- ap1_sshclient, ap2_internal_port['fixed_ips'][0]['ip_address'])
+ ap1_sshclient, ap2_internal_port['fixed_ips'][0]['ip_address'],
+ servers=servers)
@decorators.idempotent_id('b72c3b77-3396-4144-b05d-9cd3c0099893')
def test_connectivity_router_east_west_traffic(self):
@@ -145,7 +148,7 @@
self.create_router_interface(router['id'], subnet_1['id'])
self.create_router_interface(router['id'], subnet_2['id'])
- self._create_servers(internal_port_1, internal_port_2)
+ servers = self._create_servers(internal_port_1, internal_port_2)
fip = self.create_and_associate_floatingip(
internal_port_1['id'])
@@ -155,7 +158,7 @@
self.check_remote_connectivity(
sshclient, internal_port_2['fixed_ips'][0]['ip_address'],
- ping_count=10)
+ ping_count=10, servers=servers)
@utils.requires_ext(extension="dvr", service="network")
@decorators.idempotent_id('69d3650a-5c32-40bc-ae56-5c4c849ddd37')
@@ -237,7 +240,8 @@
fip['floating_ip_address'], CONF.validation.image_ssh_user,
pkey=self.keypair['private_key'])
- self.check_remote_connectivity(sshclient, str(gw_ip), ping_count=10)
+ self.check_remote_connectivity(
+ sshclient, str(gw_ip), ping_count=10, servers=[vm])
self.check_remote_connectivity(
sshclient, dvr_router_port['fixed_ips'][0]['ip_address'],
- ping_count=10)
+ ping_count=10, servers=[vm])
diff --git a/neutron_tempest_plugin/scenario/test_floatingip.py b/neutron_tempest_plugin/scenario/test_floatingip.py
index e276a02..37df5be 100644
--- a/neutron_tempest_plugin/scenario/test_floatingip.py
+++ b/neutron_tempest_plugin/scenario/test_floatingip.py
@@ -134,10 +134,12 @@
# Check connectivity
self.check_remote_connectivity(ssh_client,
- dest_server['port']['fixed_ips'][0]['ip_address'])
+ dest_server['port']['fixed_ips'][0]['ip_address'],
+ servers=[src_server, dest_server])
if self.dest_has_fip:
self.check_remote_connectivity(ssh_client,
- dest_server['fip']['floating_ip_address'])
+ dest_server['fip']['floating_ip_address'],
+ servers=[src_server, dest_server])
class FloatingIpSameNetwork(FloatingIpTestCasesMixin,
@@ -200,7 +202,8 @@
pkey=self.keypair['private_key'],
proxy_client=proxy_client)
self.check_remote_connectivity(ssh_client,
- gateway_external_ip)
+ gateway_external_ip,
+ servers=[proxy, src_server])
class FloatingIPPortDetailsTest(FloatingIpTestCasesMixin,
@@ -418,7 +421,8 @@
self.fip = self.create_floatingip(port=ports[0])
self.check_connectivity(self.fip['floating_ip_address'],
CONF.validation.image_ssh_user,
- self.keypair['private_key'])
+ self.keypair['private_key'],
+ servers=servers)
self.client.update_floatingip(self.fip['id'], port_id=ports[1]['id'])
def _wait_for_fip_associated():
diff --git a/neutron_tempest_plugin/scenario/test_internal_dns.py b/neutron_tempest_plugin/scenario/test_internal_dns.py
index 13ca797..d19286c 100644
--- a/neutron_tempest_plugin/scenario/test_internal_dns.py
+++ b/neutron_tempest_plugin/scenario/test_internal_dns.py
@@ -69,11 +69,14 @@
# in very long boot times.
self.check_remote_connectivity(
ssh_client, leia_port['fixed_ips'][0]['ip_address'],
- timeout=CONF.validation.ping_timeout * 10)
+ timeout=CONF.validation.ping_timeout * 10,
+ servers=[self.server, leia])
resolv_conf = ssh_client.exec_command('cat /etc/resolv.conf')
self.assertIn('openstackgate.local', resolv_conf)
self.assertNotIn('starwars', resolv_conf)
- self.check_remote_connectivity(ssh_client, 'leia')
- self.check_remote_connectivity(ssh_client, 'leia.openstackgate.local')
+ self.check_remote_connectivity(ssh_client, 'leia',
+ servers=[self.server, leia])
+ self.check_remote_connectivity(ssh_client, 'leia.openstackgate.local',
+ servers=[self.server, leia])
diff --git a/neutron_tempest_plugin/scenario/test_mtu.py b/neutron_tempest_plugin/scenario/test_mtu.py
index df730c6..31319ec 100644
--- a/neutron_tempest_plugin/scenario/test_mtu.py
+++ b/neutron_tempest_plugin/scenario/test_mtu.py
@@ -129,7 +129,8 @@
for fip in (fip1, fip2):
self.check_connectivity(
fip['floating_ip_address'],
- self.username, self.keypair['private_key'])
+ self.username, self.keypair['private_key'],
+ servers=[server1, server2])
return server_ssh_client1, fip1, server_ssh_client2, fip2
@testtools.skipUnless(
diff --git a/neutron_tempest_plugin/scenario/test_multicast.py b/neutron_tempest_plugin/scenario/test_multicast.py
index a39a0c3..566ac95 100644
--- a/neutron_tempest_plugin/scenario/test_multicast.py
+++ b/neutron_tempest_plugin/scenario/test_multicast.py
@@ -111,6 +111,14 @@
'result_file': result_file}
+def get_unregistered_script(group, result_file):
+ return """#!/bin/bash
+export LC_ALL=en_US.UTF-8
+tcpdump -i any -s0 -vv host %(group)s -vvneA -s0 -l &> %(result_file)s &
+ """ % {'group': group,
+ 'result_file': result_file}
+
+
class BaseMulticastTest(object):
credentials = ['primary']
@@ -125,6 +133,7 @@
multicast_message = "Big Bang"
receiver_output_file = "/tmp/receiver_mcast_out"
sender_output_file = "/tmp/sender_mcast_out"
+ unregistered_output_file = "/tmp/unregistered_mcast_out"
@classmethod
def skip_checks(cls):
@@ -198,16 +207,16 @@
server['ssh_client'] = ssh.Client(server['fip']['floating_ip_address'],
self.username,
pkey=self.keypair['private_key'])
- self._check_python_installed_on_server(server['ssh_client'],
- server['id'])
+ self._check_cmd_installed_on_server(server['ssh_client'],
+ server['id'], PYTHON3_BIN)
return server
- def _check_python_installed_on_server(self, ssh_client, server_id):
+ def _check_cmd_installed_on_server(self, ssh_client, server_id, cmd):
try:
- ssh_client.execute_script('which %s' % PYTHON3_BIN)
+ ssh_client.execute_script('which %s' % cmd)
except exceptions.SSHScriptFailed:
raise self.skipException(
- "%s is not available on server %s" % (PYTHON3_BIN, server_id))
+ "%s is not available on server %s" % (cmd, server_id))
def _prepare_sender(self, server, mcast_address):
check_script = get_sender_script(
@@ -226,10 +235,23 @@
server['fip']['floating_ip_address'],
self.username,
pkey=self.keypair['private_key'])
- self._check_python_installed_on_server(ssh_client, server['id'])
+ self._check_cmd_installed_on_server(ssh_client, server['id'],
+ PYTHON3_BIN)
server['ssh_client'].execute_script(
'echo "%s" > ~/multicast_traffic_receiver.py' % check_script)
+ def _prepare_unregistered(self, server, mcast_address):
+ check_script = get_unregistered_script(
+ group=mcast_address, result_file=self.unregistered_output_file)
+ ssh_client = ssh.Client(
+ server['fip']['floating_ip_address'],
+ self.username,
+ pkey=self.keypair['private_key'])
+ self._check_cmd_installed_on_server(ssh_client, server['id'],
+ 'tcpdump')
+ server['ssh_client'].execute_script(
+ 'echo "%s" > ~/unregistered_traffic_receiver.sh' % check_script)
+
@test.unstable_test("bug 1850288")
@decorators.idempotent_id('113486fc-24c9-4be4-8361-03b1c9892867')
def test_multicast_between_vms_on_same_network(self):
@@ -241,9 +263,26 @@
receivers = [self._create_server() for _ in range(1)]
# Sender can be also receiver of multicast traffic
receivers.append(sender)
- self._check_multicast_conectivity(sender=sender, receivers=receivers)
+ unregistered = self._create_server()
+ self._check_multicast_conectivity(sender=sender, receivers=receivers,
+ unregistered=unregistered)
- def _check_multicast_conectivity(self, sender, receivers):
+ def _is_multicast_traffic_expected(self, mcast_address):
+ """Checks if multicast traffic is expected to arrive.
+
+ Checks if multicast traffic is expected to arrive to the
+ unregistered VM.
+
+ If IGMP snooping is enabled, multicast traffic should not be
+ flooded unless the destination IP is in the range of 224.0.0.X
+ [0].
+
+ [0] https://tools.ietf.org/html/rfc4541 (See section 2.1.2)
+ """
+ return (mcast_address.startswith('224.0.0') or not
+ CONF.neutron_plugin_options.is_igmp_snooping_enabled)
+
+ def _check_multicast_conectivity(self, sender, receivers, unregistered):
"""Test multi-cast messaging between two servers
[Sender server] -> ... some network topology ... -> [Receiver server]
@@ -257,6 +296,12 @@
path=file_path))
return msg in result
+ self._prepare_unregistered(unregistered, mcast_address)
+
+ # Run the unregistered node script
+ unregistered['ssh_client'].execute_script(
+ "bash ~/unregistered_traffic_receiver.sh", become_root=True)
+
self._prepare_sender(sender, mcast_address)
receiver_ids = []
for receiver in receivers:
@@ -295,6 +340,18 @@
for receiver_id in receiver_ids:
self.assertIn(receiver_id, replies_result)
+ # Kill the tcpdump command running on the unregistered node so
+ # tcpdump flushes its output to the output file
+ unregistered['ssh_client'].execute_script(
+ "killall tcpdump && sleep 2", become_root=True)
+
+ unregistered_result = unregistered['ssh_client'].execute_script(
+ "cat {path} || echo '{path} not exists yet'".format(
+ path=self.unregistered_output_file))
+ num_of_pckt = (1 if self._is_multicast_traffic_expected(mcast_address)
+ else 0)
+ self.assertIn('%d packets captured' % num_of_pckt, unregistered_result)
+
class MulticastTestIPv4(BaseMulticastTest, base.BaseTempestTestCase):
diff --git a/neutron_tempest_plugin/scenario/test_port_forwardings.py b/neutron_tempest_plugin/scenario/test_port_forwardings.py
index 7283887..2d77b65 100644
--- a/neutron_tempest_plugin/scenario/test_port_forwardings.py
+++ b/neutron_tempest_plugin/scenario/test_port_forwardings.py
@@ -14,12 +14,12 @@
# under the License.
from neutron_lib import constants
-from neutron_lib.utils import test
from oslo_log import log
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin.common import utils
from neutron_tempest_plugin import config
from neutron_tempest_plugin.scenario import base
@@ -81,27 +81,32 @@
return servers
def _test_udp_port_forwarding(self, servers):
+
+ def _message_received(server, ssh_client, expected_msg):
+ self.nc_listen(server,
+ ssh_client,
+ server['port_forwarding_udp']['internal_port'],
+ constants.PROTO_NAME_UDP,
+ expected_msg)
+ received_msg = self.nc_client(
+ self.fip['floating_ip_address'],
+ server['port_forwarding_udp']['external_port'],
+ constants.PROTO_NAME_UDP)
+ return expected_msg in received_msg
+
for server in servers:
- msg = "%s-UDP-test" % server['name']
+ expected_msg = "%s-UDP-test" % server['name']
ssh_client = ssh.Client(
self.fip['floating_ip_address'],
CONF.validation.image_ssh_user,
pkey=self.keypair['private_key'],
port=server['port_forwarding_tcp']['external_port'])
- self.nc_listen(server,
- ssh_client,
- server['port_forwarding_udp']['internal_port'],
- constants.PROTO_NAME_UDP,
- msg)
- for server in servers:
- expected_msg = "%s-UDP-test" % server['name']
- self.assertIn(
- expected_msg, self.nc_client(
- self.fip['floating_ip_address'],
- server['port_forwarding_udp']['external_port'],
- constants.PROTO_NAME_UDP))
+ utils.wait_until_true(
+ lambda: _message_received(server, ssh_client, expected_msg),
+ exception=RuntimeError(
+ "Timed out waiting for message from server {!r} ".format(
+ server['id'])))
- @test.unstable_test("bug 1850800")
@decorators.idempotent_id('ab40fc48-ca8d-41a0-b2a3-f6679c847bfe')
def test_port_forwarding_to_2_servers(self):
udp_sg_rule = {'protocol': constants.PROTO_NAME_UDP,
diff --git a/neutron_tempest_plugin/scenario/test_qos.py b/neutron_tempest_plugin/scenario/test_qos.py
index a59441c..e84fb3c 100644
--- a/neutron_tempest_plugin/scenario/test_qos.py
+++ b/neutron_tempest_plugin/scenario/test_qos.py
@@ -21,7 +21,6 @@
from tempest.common import utils as tutils
from tempest.common import waiters
from tempest.lib import decorators
-from tempest.lib import exceptions
from neutron_tempest_plugin.api import base as base_api
from neutron_tempest_plugin.common import ssh
@@ -93,16 +92,8 @@
raise sc_exceptions.FileCreationFailedException(
file=self.FILE_PATH)
- @staticmethod
- def _kill_nc_process(ssh_client):
- cmd = "killall -q nc"
- try:
- ssh_client.exec_command(cmd, timeout=5)
- except exceptions.SSHExecCommandFailed:
- pass
-
def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
- self._kill_nc_process(ssh_client)
+ utils.kill_nc_process(ssh_client)
cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
'port': port, 'file_path': self.FILE_PATH})
ssh_client.exec_command(cmd, timeout=5)
@@ -131,7 +122,7 @@
except socket.timeout:
LOG.warning('Socket timeout while reading the remote file, bytes '
'read: %s', total_bytes_read)
- self._kill_nc_process(ssh_client)
+ utils.kill_nc_process(ssh_client)
return False
finally:
client_socket.close()
diff --git a/neutron_tempest_plugin/scenario/test_security_groups.py b/neutron_tempest_plugin/scenario/test_security_groups.py
index 7b43a7e..dc14857 100644
--- a/neutron_tempest_plugin/scenario/test_security_groups.py
+++ b/neutron_tempest_plugin/scenario/test_security_groups.py
@@ -16,9 +16,11 @@
from tempest.common import waiters
from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from neutron_tempest_plugin.common import ssh
+from neutron_tempest_plugin.common import utils
from neutron_tempest_plugin import config
from neutron_tempest_plugin.scenario import base
from neutron_tempest_plugin.scenario import constants as const
@@ -30,6 +32,39 @@
credentials = ['primary', 'admin']
required_extensions = ['router', 'security-group']
+ def _verify_http_connection(self, ssh_client, ssh_server,
+ test_ip, test_port, should_pass=True):
+ """Verify if HTTP connection works using remote hosts.
+
+ :param ssh.Client ssh_client: The client host active SSH client.
+ :param ssh.Client ssh_server: The HTTP server host active SSH client.
+ :param string test_ip: IP address of HTTP server
+ :param string test_port: Port of HTTP server
+ :param bool should_pass: Wheter test should pass or not.
+
+ :return: if passed or not
+ :rtype: bool
+ """
+ utils.kill_nc_process(ssh_server)
+ url = 'http://%s:%d' % (test_ip, test_port)
+ utils.spawn_http_server(ssh_server, port=test_port, message='foo_ok')
+ try:
+ ret = utils.call_url_remote(ssh_client, url)
+ if should_pass:
+ self.assertIn('foo_ok', ret)
+ return
+ self.assertNotIn('foo_ok', ret)
+ except Exception as e:
+ if not should_pass:
+ return
+ raise e
+
+ @classmethod
+ def setup_credentials(cls):
+ super(NetworkSecGroupTest, cls).setup_credentials()
+ cls.project_id = cls.os_primary.credentials.tenant_id
+ cls.network_client = cls.os_admin.network_client
+
@classmethod
def resource_setup(cls):
super(NetworkSecGroupTest, cls).resource_setup()
@@ -40,6 +75,12 @@
cls.create_router_interface(router['id'], cls.subnet['id'])
cls.keypair = cls.create_keypair()
+ def setUp(self):
+ super(NetworkSecGroupTest, self).setUp()
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.network_client.reset_quotas, self.project_id)
+ self.network_client.update_quotas(self.project_id, security_group=-1)
+
def create_vm_testing_sec_grp(self, num_servers=2, security_groups=None,
ports=None):
"""Create instance for security group testing
@@ -100,11 +141,12 @@
# make sure ICMP connectivity works
self.check_remote_connectivity(server_ssh_clients[0], fips[1][
- 'fixed_ip_address'], should_succeed=should_succeed)
+ 'fixed_ip_address'], should_succeed=should_succeed,
+ servers=servers)
@decorators.idempotent_id('3d73ec1a-2ec6-45a9-b0f8-04a283d9d764')
def test_default_sec_grp_scenarios(self):
- server_ssh_clients, fips, _ = self.create_vm_testing_sec_grp()
+ server_ssh_clients, fips, servers = self.create_vm_testing_sec_grp()
# Check ssh connectivity when you add sec group rule, enabling ssh
self.create_loginable_secgroup_rule(
self.os_primary.network_client.list_security_groups()[
@@ -121,7 +163,8 @@
# Check ICMP connectivity between VMs without specific rule for that
# It should work though the rule is not configured
self.check_remote_connectivity(
- server_ssh_clients[0], fips[1]['fixed_ip_address'])
+ server_ssh_clients[0], fips[1]['fixed_ip_address'],
+ servers=servers)
# Check ICMP connectivity from VM to external network
subnets = self.os_admin.network_client.list_subnets(
@@ -132,7 +175,8 @@
ext_net_ip = subnet['gateway_ip']
break
self.assertTrue(ext_net_ip)
- self.check_remote_connectivity(server_ssh_clients[0], ext_net_ip)
+ self.check_remote_connectivity(server_ssh_clients[0], ext_net_ip,
+ servers=servers)
@decorators.idempotent_id('3d73ec1a-2ec6-45a9-b0f8-04a283d9d864')
def test_protocol_number_rule(self):
@@ -256,7 +300,8 @@
rule_list, secgroup_id=ssh_secgrp['security_group']['id'])
# verify ICMP connectivity between instances works
self.check_remote_connectivity(
- server_ssh_clients[0], fips[1]['fixed_ip_address'])
+ server_ssh_clients[0], fips[1]['fixed_ip_address'],
+ servers=servers)
# make sure ICMP connectivity doesn't work from framework
self.ping_ip_address(fips[0]['floating_ip_address'],
should_succeed=False)
@@ -293,3 +338,65 @@
self.check_connectivity(fip['floating_ip_address'],
CONF.validation.image_ssh_user,
self.keypair['private_key'])
+
+ @decorators.idempotent_id('f07d0159-8f9e-4faa-87f5-a869ab0ad489')
+ def test_multiple_ports_portrange_remote(self):
+ ssh_clients, fips, servers = self.create_vm_testing_sec_grp(
+ num_servers=3)
+ secgroups = []
+ ports = []
+
+ # Create remote and test security groups
+ for i in range(0, 2):
+ secgroups.append(
+ self.create_security_group(name='secgrp-%d' % i))
+ # configure sec groups to support SSH connectivity
+ self.create_loginable_secgroup_rule(
+ secgroup_id=secgroups[-1]['id'])
+
+ # Configure security groups, first two servers as remotes
+ for i, server in enumerate(servers):
+ port = self.client.list_ports(
+ network_id=self.network['id'], device_id=server['server'][
+ 'id'])['ports'][0]
+ ports.append(port)
+ secgroup = secgroups[0 if i in range(0, 2) else 1]
+ self.client.update_port(port['id'], security_groups=[
+ secgroup['id']])
+
+ # verify SSH functionality
+ for fip in fips:
+ self.check_connectivity(fip['floating_ip_address'],
+ CONF.validation.image_ssh_user,
+ self.keypair['private_key'])
+
+ test_ip = ports[2]['fixed_ips'][0]['ip_address']
+
+ # verify that conections are not working
+ for port in range(80, 84):
+ self._verify_http_connection(
+ ssh_clients[0],
+ ssh_clients[2],
+ test_ip, port,
+ should_pass=False)
+
+ # add two remote-group rules with port-ranges
+ rule_list = [{'protocol': constants.PROTO_NUM_TCP,
+ 'direction': constants.INGRESS_DIRECTION,
+ 'port_range_min': '80',
+ 'port_range_max': '81',
+ 'remote_group_id': secgroups[0]['id']},
+ {'protocol': constants.PROTO_NUM_TCP,
+ 'direction': constants.INGRESS_DIRECTION,
+ 'port_range_min': '82',
+ 'port_range_max': '83',
+ 'remote_group_id': secgroups[0]['id']}]
+ self.create_secgroup_rules(
+ rule_list, secgroup_id=secgroups[1]['id'])
+
+ # verify that conections are working
+ for port in range(80, 84):
+ self._verify_http_connection(
+ ssh_clients[0],
+ ssh_clients[2],
+ test_ip, port)
diff --git a/neutron_tempest_plugin/scenario/test_trunk.py b/neutron_tempest_plugin/scenario/test_trunk.py
index 6d855f1..7fd6c52 100644
--- a/neutron_tempest_plugin/scenario/test_trunk.py
+++ b/neutron_tempest_plugin/scenario/test_trunk.py
@@ -282,7 +282,8 @@
self.create_pingable_secgroup_rule(self.security_group['id'])
self.check_remote_connectivity(
vm1.ssh_client,
- vm2.subport['fixed_ips'][0]['ip_address'])
+ vm2.subport['fixed_ips'][0]['ip_address'],
+ servers=[vm1, vm2])
@testtools.skipUnless(
(CONF.neutron_plugin_options.advanced_image_ref or
@@ -308,11 +309,12 @@
use_advanced_image=use_advanced_image)
normal_network_server = self._create_server_with_network(self.network)
vlan_network_server = self._create_server_with_network(vlan_network)
+ vms = [normal_network_server, vlan_network_server]
self._configure_vlan_subport(vm=trunk_network_server,
vlan_tag=vlan_tag,
vlan_subnet=vlan_subnet)
- for vm in [normal_network_server, vlan_network_server]:
+ for vm in vms:
self.wait_for_server_active(vm.server)
# allow ICMP traffic
@@ -323,14 +325,16 @@
self.check_remote_connectivity(
trunk_network_server.ssh_client,
normal_network_server.port['fixed_ips'][0]['ip_address'],
- should_succeed=True)
+ should_succeed=True,
+ servers=vms)
# Ping from trunk_network_server to vlan_network_server via VLAN
# interface should success
self.check_remote_connectivity(
trunk_network_server.ssh_client,
vlan_network_server.port['fixed_ips'][0]['ip_address'],
- should_succeed=True)
+ should_succeed=True,
+ servers=vms)
# Delete the trunk
self.delete_trunk(
@@ -344,7 +348,8 @@
self.check_remote_connectivity(
trunk_network_server.ssh_client,
normal_network_server.port['fixed_ips'][0]['ip_address'],
- should_succeed=True)
+ should_succeed=True,
+ servers=vms)
# Ping from trunk_network_server to vlan_network_server via VLAN
# interface should fail after trunk deleted
diff --git a/neutron_tempest_plugin/services/network/json/network_client.py b/neutron_tempest_plugin/services/network/json/network_client.py
index 521e2be..ddb6f95 100644
--- a/neutron_tempest_plugin/services/network/json/network_client.py
+++ b/neutron_tempest_plugin/services/network/json/network_client.py
@@ -893,6 +893,15 @@
self.expected_success(204, resp.status)
return service_client.ResponseBody(resp, body)
+ def list_security_group_rules(self, **kwargs):
+ uri = '%s/security-group-rules' % self.uri_prefix
+ if kwargs:
+ uri += '?' + urlparse.urlencode(kwargs, doseq=1)
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = jsonutils.loads(body)
+ return service_client.ResponseBody(resp, body)
+
def create_security_group_rule(self, direction, security_group_id,
**kwargs):
post_body = {'security_group_rule': kwargs}
diff --git a/releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml b/releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml
new file mode 100644
index 0000000..7d49171
--- /dev/null
+++ b/releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+ - |
+ Python 2.7 support has been dropped. Last release of neutron-tempest-plugin
+ to support py2.7 is OpenStack Train. The minimum version of Python now
+ supported by neutron-tempest-plugin is Python 3.6.
diff --git a/releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml b/releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml
new file mode 100644
index 0000000..032be1f
--- /dev/null
+++ b/releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - |
+ Enhanced the ``test_multicast_between_vms_on_same_network`` adding
+ IGMP test coverage to it. A new VM running tcpdump is spawned as
+ part of the test to verify whether the traffic is reaching it or not.
+upgrade:
+ - |
+ Add a new configuration option called ``is_igmp_snooping_enabled``
+ to enable/disable IGMP testing as part of the
+ ``test_multicast_between_vms_on_same_network`` test case.
diff --git a/requirements.txt b/requirements.txt
index 9a5e99f..d3fa3eb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,6 @@
pbr!=2.1.0,>=2.0.0 # Apache-2.0
neutron-lib>=1.25.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0
-ipaddress>=1.0.17;python_version<'3.3' # PSF
netaddr>=0.7.18 # BSD
os-ken>=0.3.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index ff12b10..1ac729c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,7 @@
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
home-page = https://opendev.org/openstack/neutron-tempest-plugin
+requires-python = >=3.6
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -13,8 +14,6 @@
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
- Programming Language :: Python :: 2
- Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
diff --git a/test-requirements.txt b/test-requirements.txt
index 8b251f6..905420c 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,8 +7,7 @@
coverage!=4.4,>=4.0 # Apache-2.0
flake8-import-order==0.12 # LGPLv3
python-subunit>=1.0.0 # Apache-2.0/BSD
-sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
-sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD
+sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2 # BSD
oslotest>=3.2.0 # Apache-2.0
stestr>=1.0.0 # Apache-2.0
testtools>=2.2.0 # MIT
diff --git a/tox.ini b/tox.ini
index 95352a2..19e006a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,11 @@
[tox]
-minversion = 2.0
+minversion = 3.1
envlist = pep8
skipsdist = True
+ignore_basepython_conflict = True
[testenv]
+basepython = python3
usedevelop = True
setenv =
VIRTUAL_ENV={envdir}
@@ -19,7 +21,6 @@
commands = stestr run --slowest {posargs}
[testenv:pep8]
-basepython = python3
commands =
sh ./tools/misc-sanity-checks.sh
flake8
@@ -27,11 +28,9 @@
sh
[testenv:venv]
-basepython = python3
commands = {posargs}
[testenv:cover]
-basepython = python3
setenv =
{[testenv]setenv}
PYTHON=coverage run --source neutron_tempest_plugin --parallel-mode
@@ -42,16 +41,13 @@
coverage xml -o cover/coverage.xml
[testenv:docs]
-basepython = python3
commands = python setup.py build_sphinx
[testenv:releasenotes]
-basepython = python3
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:debug]
-basepython = python3
commands = oslo_debug_helper -t neutron_tempest_plugin/ {posargs}
[flake8]