From b2686a224a61317e4b9aff48c53a198d04561550 Mon Sep 17 00:00:00 2001 From: Andrey Pavlov Date: Wed, 15 Sep 2021 07:16:32 +0300 Subject: [PATCH] various fixes - bump version from 2 to 3 for cinderclient - update aws ec2 interface for create_colume and create_network_interface. add cilent_token param - fix using of SQLAlchemy.in_ operator in UT - remove lower-constraints job. it's buggy Change-Id: Ie3e5a5930d5a8159050ecc0900239935558dddd7 --- .zuul.yaml | 1 - ec2api/api/cloud.py | 14 ++-- ec2api/api/image.py | 4 +- ec2api/api/network_interface.py | 18 ++++- ec2api/api/volume.py | 17 ++++- ec2api/tests/unit/base.py | 4 +- ec2api/tests/unit/test_clients.py | 4 +- ec2api/tests/unit/test_db_api.py | 6 +- lower-constraints.txt | 114 ------------------------------ tox.ini | 6 -- 10 files changed, 52 insertions(+), 136 deletions(-) delete mode 100644 lower-constraints.txt diff --git a/.zuul.yaml b/.zuul.yaml index 1f9bc5f5..36384dc7 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,7 +1,6 @@ - project: templates: - check-requirements - - openstack-lower-constraints-jobs - openstack-python3-victoria-jobs - publish-openstack-docs-pti check: diff --git a/ec2api/api/cloud.py b/ec2api/api/cloud.py index e8b5a746..05ee3a49 100644 --- a/ec2api/api/cloud.py +++ b/ec2api/api/cloud.py @@ -819,10 +819,10 @@ class CloudController(object): @module_and_param_types(volume, 'str', 'int', 'snap_id', 'str', 'int', - 'bool', 'str') + 'bool', 'str', 'str') def create_volume(self, context, availability_zone=None, size=None, snapshot_id=None, volume_type=None, iops=None, - encrypted=None, kms_key_id=None): + encrypted=None, kms_key_id=None, client_token=None): """Creates an EBS volume. Args: @@ -846,6 +846,8 @@ class CloudController(object): kms_key_id (str): The full ARN of AWS KMS master key to use when creating the encrypted volume. Not used now. + client_token (str): Unique, case-sensitive identifier that you + provide to ensure the idempotency of the request. Returns: Information about the volume. @@ -1691,13 +1693,15 @@ class VpcCloudController(CloudController): 'dummy', 'int', 'str', - 'sg_ids') + 'sg_ids', + 'str') def create_network_interface(self, context, subnet_id, private_ip_address=None, private_ip_addresses=None, secondary_private_ip_address_count=None, description=None, - security_group_id=None): + security_group_id=None, + client_token=None): """Creates a network interface in the specified subnet. Args: @@ -1724,6 +1728,8 @@ class VpcCloudController(CloudController): description (str): A description for the network interface. security_group_id (list of str): The list of security group IDs for the network interface. + client_token (str): Unique, case-sensitive identifier that you + provide to ensure the idempotency of the request. Returns: The network interface that was created. diff --git a/ec2api/api/image.py b/ec2api/api/image.py index 3605c130..60f345b1 100644 --- a/ec2api/api/image.py +++ b/ec2api/api/image.py @@ -156,13 +156,13 @@ def create_image(context, instance_id, name=None, description=None, image['os_id'] = os_image_id db_api.update_item(context, image) except Exception: - LOG.exception('Failed to complete image %s creation', image.id) + LOG.exception('Failed to complete image %s creation', image['id']) try: image['state'] = 'failed' db_api.update_item(context, image) except Exception: LOG.warning("Couldn't set 'failed' state for db image %s", - image.id, exc_info=True) + image['id'], exc_info=True) try: os_instance.start() diff --git a/ec2api/api/network_interface.py b/ec2api/api/network_interface.py index 1b03a09d..53223c06 100644 --- a/ec2api/api/network_interface.py +++ b/ec2api/api/network_interface.py @@ -47,7 +47,22 @@ def create_network_interface(context, subnet_id, private_ip_addresses=None, secondary_private_ip_address_count=None, description=None, - security_group_id=None): + security_group_id=None, + client_token=None): + + if client_token: + result = describe_network_interfaces(context, + filter=[{'name': 'client-token', + 'value': [client_token]}]) + if result['networkInterfaceSet']: + if len(result['networkInterfaceSet']) > 1: + LOG.error('describe_network_interfaces returns %s ' + 'network_interfaces, but 1 is expected.', + len(result['networkInterfaceSet'])) + LOG.error('Requested client token: %s', client_token) + LOG.error('Result: %s', result) + return result['networkInterfaceSet'][0] + subnet = ec2utils.get_db_item(context, subnet_id) if subnet is None: raise exception.InvalidSubnetIDNotFound(id=subnet_id) @@ -206,6 +221,7 @@ class NetworkInterfaceDescriber(common.TaggableItemsDescriber): 'attachment.attach.time': ('attachment', 'attachTime'), 'attachment.delete-on-termination': ('attachment', 'deleteOnTermination'), + 'client-token': 'clientToken', 'description': 'description', 'group-id': ['groupSet', 'groupId'], 'group-name': ['groupSet', 'groupName'], diff --git a/ec2api/api/volume.py b/ec2api/api/volume.py index 7f3b1468..2610e20e 100644 --- a/ec2api/api/volume.py +++ b/ec2api/api/volume.py @@ -37,7 +37,21 @@ Validator = common.Validator def create_volume(context, availability_zone=None, size=None, snapshot_id=None, volume_type=None, iops=None, - encrypted=None, kms_key_id=None): + encrypted=None, kms_key_id=None, client_token=None): + + if client_token: + result = describe_volumes(context, + filter=[{'name': 'client-token', + 'value': [client_token]}]) + if result['volumeSet']: + if len(result['volumeSet']) > 1: + LOG.error('describe_volumes returns %s ' + 'volumes, but 1 is expected.', + len(result['volumeSet'])) + LOG.error('Requested client token: %s', client_token) + LOG.error('Result: %s', result) + return result['volumeSet'][0] + if snapshot_id is not None: snapshot = ec2utils.get_db_item(context, snapshot_id) os_snapshot_id = snapshot['os_id'] @@ -121,6 +135,7 @@ class VolumeDescriber(common.TaggableItemsDescriber): SORT_KEY = 'volumeId' FILTER_MAP = { 'availability-zone': 'availabilityZone', + 'client-token': 'clientToken', 'create-time': 'createTime', 'encrypted': 'encrypted', 'size': 'size', diff --git a/ec2api/tests/unit/base.py b/ec2api/tests/unit/base.py index ebfad2ad..ee6873db 100644 --- a/ec2api/tests/unit/base.py +++ b/ec2api/tests/unit/base.py @@ -58,7 +58,7 @@ def create_context(is_os_admin=False): if is_os_admin else mock.sentinel.session) session.get_endpoint = mock.Mock(name="get_endpoint") - session.get_endpoint.return_value = 'v2' + session.get_endpoint.return_value = 'v3' return ec2api.context.RequestContext(fakes.ID_OS_USER, fakes.ID_OS_PROJECT, is_os_admin=is_os_admin, session=session) @@ -121,7 +121,7 @@ class MockOSMixin(object): def mock_cinder(self): cinder_patcher = mock.patch('cinderclient.client.Client') - cinder = mock.create_autospec(cinderclient.Client('2')) + cinder = mock.create_autospec(cinderclient.Client('3')) cinder_patcher.start().return_value = cinder self.addCleanup(cinder_patcher.stop) return cinder diff --git a/ec2api/tests/unit/test_clients.py b/ec2api/tests/unit/test_clients.py index 6ddc89b1..b14873ae 100644 --- a/ec2api/tests/unit/test_clients.py +++ b/ec2api/tests/unit/test_clients.py @@ -116,7 +116,7 @@ class ClientsTestCase(base.BaseTestCase): context = mock.NonCallableMock(session=mock.sentinel.session) res = clients.cinder(context) self.assertEqual(cinder.return_value, res) - cinder.assert_called_with('2', service_type='volumev2', + cinder.assert_called_with('3', service_type='volumev2', session=mock.sentinel.session) @mock.patch('keystoneclient.client.Client') @@ -124,5 +124,5 @@ class ClientsTestCase(base.BaseTestCase): context = mock.NonCallableMock(session=mock.sentinel.session) res = clients.keystone(context) self.assertEqual(keystone.return_value, res) - keystone.assert_called_with(auth_url='v2', + keystone.assert_called_with(auth_url='v3', session=mock.sentinel.session) diff --git a/ec2api/tests/unit/test_db_api.py b/ec2api/tests/unit/test_db_api.py index 9bbcee59..314a78fa 100644 --- a/ec2api/tests/unit/test_db_api.py +++ b/ec2api/tests/unit/test_db_api.py @@ -242,7 +242,7 @@ class DbApiTestCase(base.DbTestCase): (item_id, other_item_id)) self.assertEqual(1, len(items)) items = db_api.get_items_by_ids(self.context, - (fakes.random_ec2_id('fake')),) + (fakes.random_ec2_id('fake'),)) self.assertEqual(0, len(items)) items = db_api.get_items_by_ids(self.context, (item_id, fakes.random_ec2_id('fake'))) @@ -294,7 +294,7 @@ class DbApiTestCase(base.DbTestCase): [public_item_ids[0]]) self.assertEqual(0, len(items)) items = db_api.get_public_items(self.context, 'fake', - fakes.random_ec2_id('fake')) + [fakes.random_ec2_id('fake')]) self.assertEqual(0, len(items)) items = db_api.get_public_items(self.context, 'fake0', []) self.assertEqual(0, len(items)) @@ -454,6 +454,6 @@ class DbApiTestCase(base.DbTestCase): 'key': 'key', 'value': 'val2'} db_api.add_tags(self.other_context, [tag2]) - db_api.delete_tags(self.context, item_id) + db_api.delete_tags(self.context, [item_id]) self.assertThat(db_api.get_tags(self.other_context), matchers.ListMatches([tag2])) diff --git a/lower-constraints.txt b/lower-constraints.txt deleted file mode 100644 index f30a2f42..00000000 --- a/lower-constraints.txt +++ /dev/null @@ -1,114 +0,0 @@ -alabaster==0.7.10 -alembic==0.9.8 -appdirs==1.4.3 -asn1crypto==0.24.0 -astroid==1.3.8 -Babel==2.5.3 -botocore==1.9.7 -certifi==2018.1.18 -cffi==1.11.5 -chardet==3.0.4 -cliff==2.11.0 -cmd2==0.8.1 -coverage==4.5.1 -cryptography==2.1.4 -debtcollector==1.19.0 -decorator==4.2.1 -deprecation==2.0 -docutils==0.14 -dogpile.cache==0.6.5 -dulwich==0.19.0 -enum-compat==0.0.2 -eventlet==0.20.0 -extras==1.0.0 -fasteners==0.14.1 -fixtures==3.0.0 -future==0.16.0 -greenlet==0.4.13 -httplib2==0.10.3 -idna==2.6 -imagesize==1.0.0 -iso8601==0.1.12 -Jinja2==2.10 -jmespath==0.9.3 -jsonpatch==1.21 -jsonpointer==2.0 -jsonschema==2.6.0 -keystoneauth1==3.14.0 -linecache2==1.0.0 -logilab-common==1.4.1 -lxml==4.1.1 -Mako==1.0.7 -MarkupSafe==1.1.1 -mccabe==0.2.1 -monotonic==1.4 -mox3==0.25.0 -msgpack==0.5.6 -munch==2.2.0 -netaddr==0.7.19 -netifaces==0.10.6 -nose==1.3.7 -openstacksdk==0.12.0 -os-api-ref==1.5.0 -os-client-config==1.29.0 -os-service-types==1.2.0 -osc-lib==1.10.0 -oslo.cache==1.29.0 -oslo.concurrency==3.26.0 -oslo.config==5.2.0 -oslo.context==2.20.0 -oslo.db==4.40.0 -oslo.i18n==3.20.0 -oslo.log==3.37.0 -oslo.serialization==2.25.0 -oslo.service==1.30.0 -oslo.utils==3.36.0 -oslotest==3.3.0 -packaging==17.1 -Paste==2.0.3 -PasteDeploy==1.5.2 -pbr==3.1.1 -prettytable==0.7.2 -Pygments==2.2.0 -pyinotify==0.9.6 -pylint==1.4.5 -pyOpenSSL==17.5.0 -pyparsing==2.2.0 -pyperclip==1.6.0 -python-cinderclient==3.5.0 -python-dateutil==2.7.0 -python-editor==1.0.3 -python-glanceclient==2.16.0 -python-keystoneclient==3.15.0 -python-mimeparse==1.6.0 -python-neutronclient==6.7.0 -python-novaclient==10.1.0 -python-openstackclient==3.14.0 -python-subunit==1.2.0 -pytz==2018.3 -PyYAML==3.13 -repoze.lru==0.7 -requests==2.18.4 -requestsexceptions==1.4.0 -rfc3986==1.1.0 -Routes==2.4.1 -simplejson==3.13.2 -six==1.11.0 -snowballstemmer==1.2.1 -SQLAlchemy==1.2.5 -sqlalchemy-migrate==0.11.0 -sqlparse==0.2.4 -stestr==2.0.0 -stevedore==1.28.0 -Tempita==0.5.2 -testrepository==0.0.20 -testresources==2.0.1 -testscenarios==0.5.0 -testtools==2.3.0 -traceback2==1.4.0 -unittest2==1.1.0 -urllib3==1.22 -voluptuous==0.11.1 -warlock==1.3.0 -WebOb==1.7.4 -wrapt==1.10.11 diff --git a/tox.ini b/tox.ini index f6ce7ed9..211eb321 100644 --- a/tox.ini +++ b/tox.ini @@ -63,9 +63,3 @@ max-complexity=25 extension = N537 = checks:no_translate_logs paths = ./ec2api/hacking - -[testenv:lower-constraints] -deps = - -c{toxinidir}/lower-constraints.txt - -r{toxinidir}/test-requirements.txt - -r{toxinidir}/doc/requirements.txt