summaryrefslogtreecommitdiff
diff options
authorThomas Goirand <zigo@debian.org>2019-02-21 09:20:30 +0100
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2019-02-21 16:40:38 +0000
commitf6e5ea4b4aad1f2ae797be1c623e7f861aa5b688 (patch)
treec972a89b675f184e931d4304192177fbd85e661e
parentdf63d3be8514ca60093cc461b7c83fc05e2226e4 (diff)
parent613e5ab1174167bfb91141bd0dd09ba84626782e (diff)
3.16.2-1 (patches applied)applied/3.16.2-1applied/debian/buster
Imported using git-ubuntu import.
-rw-r--r--.gitreview1
-rw-r--r--.zuul.yaml9
-rw-r--r--debian/changelog11
-rw-r--r--debian/control2
-rwxr-xr-xdebian/rules4
-rw-r--r--doc/source/cli/command-objects/volume.rst8
-rw-r--r--openstackclient/common/project_purge.py4
-rw-r--r--openstackclient/compute/v2/server.py62
-rw-r--r--openstackclient/identity/v3/endpoint.py2
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server.py10
-rw-r--r--openstackclient/tests/functional/image/base.py24
-rw-r--r--openstackclient/tests/functional/image/v1/test_image.py55
-rw-r--r--openstackclient/tests/functional/image/v2/test_image.py107
-rw-r--r--openstackclient/tests/functional/volume/v1/test_transfer_request.py37
-rw-r--r--openstackclient/tests/functional/volume/v2/test_transfer_request.py42
-rw-r--r--openstackclient/tests/functional/volume/v3/test_transfer_request.py2
-rw-r--r--openstackclient/tests/unit/common/test_project_purge.py30
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py3
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py163
-rw-r--r--openstackclient/tests/unit/identity/v3/test_endpoint.py41
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py47
-rw-r--r--openstackclient/volume/v2/volume.py14
-rw-r--r--releasenotes/notes/floating-ip-multi-port-9779e88f590cae23.yaml14
-rw-r--r--tox.ini8
24 files changed, 480 insertions, 220 deletions
diff --git a/.gitreview b/.gitreview
index 784c190..22e670e 100644
--- a/.gitreview
+++ b/.gitreview
@@ -2,3 +2,4 @@
host=review.openstack.org
port=29418
project=openstack/python-openstackclient.git
+defaultbranch=stable/rocky
diff --git a/.zuul.yaml b/.zuul.yaml
index 4541fbd..ee3abfc 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -192,6 +192,12 @@
templates:
- openstackclient-plugin-jobs
- osc-tox-unit-tips
+ - openstack-python-jobs
+ - openstack-python35-jobs
+ - publish-openstack-sphinx-docs
+ - check-requirements
+ - release-notes-jobs
+ - lib-forward-testing
check:
jobs:
- osc-functional-devstack
@@ -210,3 +216,6 @@
jobs:
- osc-functional-devstack
- openstack-tox-lower-constraints
+ post:
+ jobs:
+ - openstack-tox-cover
diff --git a/debian/changelog b/debian/changelog
index 8767c2e..4dcf550 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,14 @@
+python-openstackclient (3.16.2-1) unstable; urgency=medium
+
+ [ Ondřej Nový ]
+ * Running wrap-and-sort -bast
+ * Use 'python3 -m sphinx' instead of sphinx-build for building docs
+
+ [ Thomas Goirand ]
+ * New upstream point release.
+
+ -- Thomas Goirand <zigo@debian.org> Thu, 21 Feb 2019 09:20:30 +0100
+
python-openstackclient (3.16.0-3) unstable; urgency=high
* Remove the test suite which is doing apt-get, and that isn't advised.
diff --git a/debian/control b/debian/control
index 6ff1983..7ac2448 100644
--- a/debian/control
+++ b/debian/control
@@ -86,10 +86,10 @@ Build-Depends-Indep:
python3-searchlightclient (>= 1.0.0) <!nodoc>,
python3-senlinclient (>= 1.1.0) <!nodoc>,
python3-six,
+ python3-sphinxcontrib.apidoc <!nodoc>,
python3-stestr <!nocheck>,
python3-stevedore (>= 1.20.0),
python3-subunit <!nocheck>,
- python3-sphinxcontrib.apidoc <!nodoc>,
python3-tempest (>= 1:17.1.0) <!nocheck>,
python3-testtools (>= 2.2.0) <!nocheck>,
python3-troveclient (>= 2.2.0) <!nodoc>,
diff --git a/debian/rules b/debian/rules
index 55e2865..ed57127 100755
--- a/debian/rules
+++ b/debian/rules
@@ -22,14 +22,14 @@ endif
override_dh_sphinxdoc:
ifeq (,$(findstring nodoc, $(DEB_BUILD_OPTIONS)))
- sphinx-build -b man doc/source doc/build/man
+ python3 -m sphinx -b man doc/source doc/build/man
PYTHONPATH=. python3 -m sphinx -b html doc/source $(CURDIR)/debian/python-openstackclient-doc/usr/share/doc/python-openstackclient-doc/html
dh_sphinxdoc -O--buildsystem=python2
endif
override_dh_installman:
ifeq (,$(findstring nodoc, $(DEB_BUILD_OPTIONS)))
- sphinx-build -b man doc/source doc/build/man
+ python3 -m sphinx -b man doc/source doc/build/man
dh_installman
endif
diff --git a/doc/source/cli/command-objects/volume.rst b/doc/source/cli/command-objects/volume.rst
index a06a5d4..8df48d4 100644
--- a/doc/source/cli/command-objects/volume.rst
+++ b/doc/source/cli/command-objects/volume.rst
@@ -15,7 +15,7 @@ Create new volume
openstack volume create
[--size <size>]
[--type <volume-type>]
- [--image <image> | --snapshot <snapshot> | --source <volume> | --source-replicated <replicated-volume>]
+ [--image <image> | --snapshot <snapshot> | --source <volume> ]
[--description <description>]
[--user <user>]
[--project <project>]
@@ -31,7 +31,7 @@ Create new volume
.. option:: --size <size>
Volume size in GB
- (Required unless --snapshot or --source or --source-replicated is specified)
+ (Required unless --snapshot or --source is specified)
.. option:: --type <volume-type>
@@ -54,10 +54,6 @@ Create new volume
Volume to clone (name or ID)
-.. option:: --source-replicated <replicated-volume>
-
- Replicated volume to clone (name or ID)
-
.. option:: --description <description>
Volume description
diff --git a/openstackclient/common/project_purge.py b/openstackclient/common/project_purge.py
index 5b1d007..76ed456 100644
--- a/openstackclient/common/project_purge.py
+++ b/openstackclient/common/project_purge.py
@@ -85,7 +85,7 @@ class ProjectPurge(command.Command):
# servers
try:
compute_client = self.app.client_manager.compute
- search_opts = {'tenant_id': project_id}
+ search_opts = {'tenant_id': project_id, 'all_tenants': True}
data = compute_client.servers.list(search_opts=search_opts)
self.delete_objects(
compute_client.servers.delete, data, 'server', dry_run)
@@ -110,7 +110,7 @@ class ProjectPurge(command.Command):
# volumes, snapshots, backups
volume_client = self.app.client_manager.volume
- search_opts = {'project_id': project_id}
+ search_opts = {'project_id': project_id, 'all_tenants': True}
try:
data = volume_client.volume_snapshots.list(search_opts=search_opts)
self.delete_objects(
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index a6a5908..86ac1f8 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -23,6 +23,7 @@ import os
from novaclient import api_versions
from novaclient.v2 import servers
+from openstack import exceptions as sdk_exceptions
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -251,13 +252,16 @@ class AddFloatingIP(network_common.NetworkAndComputeCommand):
parser.add_argument(
"ip_address",
metavar="<ip-address>",
- help=_("Floating IP address to assign to server (IP only)"),
+ help=_("Floating IP address to assign to the first available "
+ "server port (IP only)"),
)
parser.add_argument(
"--fixed-ip-address",
metavar="<ip-address>",
help=_(
- "Fixed IP address to associate with this floating IP address"
+ "Fixed IP address to associate with this floating IP address. "
+ "The first server port containing the fixed IP address will "
+ "be used"
),
)
return parser
@@ -274,12 +278,45 @@ class AddFloatingIP(network_common.NetworkAndComputeCommand):
compute_client.servers,
parsed_args.server,
)
- port = list(client.ports(device_id=server.id))[0]
- attrs['port_id'] = port.id
+ ports = list(client.ports(device_id=server.id))
+ # If the fixed IP address was specified, we need to find the
+ # corresponding port.
if parsed_args.fixed_ip_address:
- attrs['fixed_ip_address'] = parsed_args.fixed_ip_address
-
- client.update_ip(obj, **attrs)
+ fip_address = parsed_args.fixed_ip_address
+ attrs['fixed_ip_address'] = fip_address
+ for port in ports:
+ for ip in port.fixed_ips:
+ if ip['ip_address'] == fip_address:
+ attrs['port_id'] = port.id
+ break
+ else:
+ continue
+ break
+ if 'port_id' not in attrs:
+ msg = _('No port found for fixed IP address %s')
+ raise exceptions.CommandError(msg % fip_address)
+ client.update_ip(obj, **attrs)
+ else:
+ # It's possible that one or more ports are not connected to a
+ # router and thus could fail association with a floating IP.
+ # Try each port until one succeeds. If none succeed, re-raise the
+ # last exception.
+ error = None
+ for port in ports:
+ attrs['port_id'] = port.id
+ try:
+ client.update_ip(obj, **attrs)
+ except sdk_exceptions.NotFoundException as exp:
+ # 404 ExternalGatewayForFloatingIPNotFound from neutron
+ LOG.info('Skipped port %s because it is not attached to '
+ 'an external gateway', port.id)
+ error = exp
+ continue
+ else:
+ error = None
+ break
+ if error:
+ raise error
def take_action_compute(self, client, parsed_args):
client.api.floating_ip_add(
@@ -809,9 +846,14 @@ class CreateServer(command.ShowOne):
raise exceptions.CommandError(msg)
nics = nics[0]
else:
- # Default to empty list if nothing was specified, let nova side to
- # decide the default behavior.
- nics = []
+ # Compute API version >= 2.37 requires a value, so default to
+ # 'auto' to maintain legacy behavior if a nic wasn't specified.
+ if compute_client.api_version >= api_versions.APIVersion('2.37'):
+ nics = 'auto'
+ else:
+ # Default to empty list if nothing was specified, let nova
+ # side to decide the default behavior.
+ nics = []
# Check security group exist and convert ID to name
security_group_names = []
diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py
index 3229240..858b503 100644
--- a/openstackclient/identity/v3/endpoint.py
+++ b/openstackclient/identity/v3/endpoint.py
@@ -199,7 +199,7 @@ class ListEndpoint(command.Lister):
metavar='<project>',
help=_('Project to list filters (name or ID)'),
)
- common.add_project_domain_option_to_parser(list_group)
+ common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index bba16f6..3cb72d9 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -618,7 +618,9 @@ class ServerTests(common.ComputeTestCase):
server_name
)
except exceptions.CommandFailed as e:
- self.assertIn('nics are required after microversion 2.36',
- e.stderr)
- else:
- self.fail('CommandFailed should be raised.')
+ # If we got here, it shouldn't be because a nics value wasn't
+ # provided to the server; it is likely due to something else in
+ # the functional tests like there being multiple available
+ # networks and the test didn't specify a specific network.
+ self.assertNotIn('nics are required after microversion 2.36',
+ e.stderr)
diff --git a/openstackclient/tests/functional/image/base.py b/openstackclient/tests/functional/image/base.py
new file mode 100644
index 0000000..4b2ab64
--- /dev/null
+++ b/openstackclient/tests/functional/image/base.py
@@ -0,0 +1,24 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from openstackclient.tests.functional import base
+
+
+class BaseImageTests(base.TestCase):
+ """Functional tests for Image commands"""
+
+ @classmethod
+ def setUpClass(cls):
+ super(BaseImageTests, cls).setUpClass()
+ # TODO(dtroyer): maybe do image API discovery here to determine
+ # what is available, it isn't in the service catalog
+ cls.haz_v1_api = False
diff --git a/openstackclient/tests/functional/image/v1/test_image.py b/openstackclient/tests/functional/image/v1/test_image.py
index fa073f9..30490bf 100644
--- a/openstackclient/tests/functional/image/v1/test_image.py
+++ b/openstackclient/tests/functional/image/v1/test_image.py
@@ -15,50 +15,47 @@ import uuid
import fixtures
-from openstackclient.tests.functional import base
+from openstackclient.tests.functional.image import base
-class ImageTests(base.TestCase):
- """Functional tests for image. """
+class ImageTests(base.BaseImageTests):
+ """Functional tests for Image commands"""
- NAME = uuid.uuid4().hex
- OTHER_NAME = uuid.uuid4().hex
+ def setUp(self):
+ super(ImageTests, self).setUp()
+ if not self.haz_v1_api:
+ self.skipTest('No Image v1 API present')
- @classmethod
- def setUpClass(cls):
- super(ImageTests, cls).setUpClass()
- json_output = json.loads(cls.openstack(
+ self.name = uuid.uuid4().hex
+ json_output = json.loads(self.openstack(
'--os-image-api-version 1 '
'image create -f json ' +
- cls.NAME
+ self.name
))
- cls.image_id = json_output["id"]
- cls.assertOutput(cls.NAME, json_output['name'])
+ self.image_id = json_output["id"]
+ self.assertOutput(self.name, json_output['name'])
+
+ ver_fixture = fixtures.EnvironmentVariable(
+ 'OS_IMAGE_API_VERSION', '1'
+ )
+ self.useFixture(ver_fixture)
- @classmethod
- def tearDownClass(cls):
+ def tearDown(self):
try:
- cls.openstack(
+ self.openstack(
'--os-image-api-version 1 '
'image delete ' +
- cls.image_id
+ self.image_id
)
finally:
- super(ImageTests, cls).tearDownClass()
-
- def setUp(self):
- super(ImageTests, self).setUp()
- ver_fixture = fixtures.EnvironmentVariable(
- 'OS_IMAGE_API_VERSION', '1'
- )
- self.useFixture(ver_fixture)
+ super(ImageTests, self).tearDown()
def test_image_list(self):
json_output = json.loads(self.openstack(
'image list -f json '
))
self.assertIn(
- self.NAME,
+ self.name,
[img['Name'] for img in json_output]
)
@@ -72,11 +69,11 @@ class ImageTests(base.TestCase):
'--min-ram 5 ' +
'--disk-format qcow2 ' +
'--public ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
self.assertEqual(
4,
@@ -100,11 +97,11 @@ class ImageTests(base.TestCase):
'--property a=b ' +
'--property c=d ' +
'--public ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
self.assertEqual(
"a='b', c='d'",
diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py
index 3037b90..3185c3b 100644
--- a/openstackclient/tests/functional/image/v2/test_image.py
+++ b/openstackclient/tests/functional/image/v2/test_image.py
@@ -16,60 +16,55 @@ import uuid
import fixtures
# from glanceclient import exc as image_exceptions
-from openstackclient.tests.functional import base
+from openstackclient.tests.functional.image import base
-class ImageTests(base.TestCase):
- """Functional tests for image. """
+class ImageTests(base.BaseImageTests):
+ """Functional tests for Image commands"""
- NAME = uuid.uuid4().hex
- OTHER_NAME = uuid.uuid4().hex
+ def setUp(self):
+ super(ImageTests, self).setUp()
- @classmethod
- def setUpClass(cls):
- super(ImageTests, cls).setUpClass()
- cls.image_tag = 'my_tag'
- json_output = json.loads(cls.openstack(
+ self.name = uuid.uuid4().hex
+ self.image_tag = 'my_tag'
+ json_output = json.loads(self.openstack(
'--os-image-api-version 2 '
'image create -f json --tag {tag} {name}'.format(
- tag=cls.image_tag, name=cls.NAME)
+ tag=self.image_tag, name=self.name)
))
- cls.image_id = json_output["id"]
- cls.assertOutput(cls.NAME, json_output['name'])
+ self.image_id = json_output["id"]
+ self.assertOutput(self.name, json_output['name'])
+
+ ver_fixture = fixtures.EnvironmentVariable(
+ 'OS_IMAGE_API_VERSION', '2'
+ )
+ self.useFixture(ver_fixture)
- @classmethod
- def tearDownClass(cls):
+ def tearDown(self):
try:
- cls.openstack(
+ self.openstack(
'--os-image-api-version 2 '
'image delete ' +
- cls.image_id
+ self.image_id
)
finally:
- super(ImageTests, cls).tearDownClass()
-
- def setUp(self):
- super(ImageTests, self).setUp()
- ver_fixture = fixtures.EnvironmentVariable(
- 'OS_IMAGE_API_VERSION', '2'
- )
- self.useFixture(ver_fixture)
+ super(ImageTests, self).tearDown()
def test_image_list(self):
json_output = json.loads(self.openstack(
'image list -f json '
))
self.assertIn(
- self.NAME,
+ self.name,
[img['Name'] for img in json_output]
)
def test_image_list_with_name_filter(self):
json_output = json.loads(self.openstack(
- 'image list --name ' + self.NAME + ' -f json'
+ 'image list --name ' + self.name + ' -f json'
))
self.assertIn(
- self.NAME,
+ self.name,
[img['Name'] for img in json_output]
)
@@ -101,11 +96,11 @@ class ImageTests(base.TestCase):
'--min-disk 4 ' +
'--min-ram 5 ' +
'--public ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
self.assertEqual(
4,
@@ -126,31 +121,31 @@ class ImageTests(base.TestCase):
'--property a=b ' +
'--property c=d ' +
'--public ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
- self.assertEqual(
- "a='b', c='d'",
- json_output["properties"],
- )
+ # NOTE(dtroyer): Don't do a full-string compare so we are tolerant of
+ # new artributes in the returned data
+ self.assertIn("a='b'", json_output["properties"])
+ self.assertIn("c='d'", json_output["properties"])
self.openstack(
'image unset ' +
'--property a ' +
'--property c ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
- self.assertNotIn(
- 'properties',
- json_output,
- )
+ # NOTE(dtroyer): Don't do a full-string compare so we are tolerant of
+ # new artributes in the returned data
+ self.assertNotIn("a='b'", json_output["properties"])
+ self.assertNotIn("c='d'", json_output["properties"])
# Test tags
self.assertNotIn(
@@ -160,11 +155,11 @@ class ImageTests(base.TestCase):
self.openstack(
'image set ' +
'--tag 01 ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
self.assertIn(
'01',
@@ -174,11 +169,11 @@ class ImageTests(base.TestCase):
self.openstack(
'image unset ' +
'--tag 01 ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
self.assertNotIn(
'01',
@@ -222,7 +217,7 @@ class ImageTests(base.TestCase):
json_output = json.loads(self.openstack(
'image show -f json ' +
- self.NAME
+ self.name
))
# NOTE(dtroyer): Until OSC supports --shared flags in create and set
# we can not properly test membership. Sometimes the
@@ -230,47 +225,47 @@ class ImageTests(base.TestCase):
if json_output["visibility"] == 'shared':
self.openstack(
'image add project ' +
- self.NAME + ' ' +
+ self.name + ' ' +
my_project_id
)
# self.addCleanup(
# self.openstack,
# 'image remove project ' +
- # self.NAME + ' ' +
+ # self.name + ' ' +
# my_project_id
# )
self.openstack(
'image set ' +
'--accept ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image list -f json ' +
'--shared'
))
self.assertIn(
- self.NAME,
+ self.name,
[img['Name'] for img in json_output]
)
self.openstack(
'image set ' +
'--reject ' +
- self.NAME
+ self.name
)
json_output = json.loads(self.openstack(
'image list -f json ' +
'--shared'
))
# self.assertNotIn(
- # self.NAME,
+ # self.name,
# [img['Name'] for img in json_output]
# )
self.openstack(
'image remove project ' +
- self.NAME + ' ' +
+ self.name + ' ' +
my_project_id
)
@@ -280,11 +275,11 @@ class ImageTests(base.TestCase):
# image_exceptions.HTTPForbidden,
# self.openstack,
# 'image add project ' +
- # self.NAME + ' ' +
+ # self.name + ' ' +
# my_project_id
# )
# self.openstack(
# 'image set ' +
# '--share ' +
- # self.NAME
+ # self.name
# )
diff --git a/openstackclient/tests/functional/volume/v1/test_transfer_request.py b/openstackclient/tests/functional/volume/v1/test_transfer_request.py
index 73191fc..0399e6c 100644
--- a/openstackclient/tests/functional/volume/v1/test_transfer_request.py
+++ b/openstackclient/tests/functional/volume/v1/test_transfer_request.py
@@ -29,20 +29,13 @@ class TransferRequestTests(common.BaseVolumeTests):
'volume create -f json --size 1 ' + cls.VOLUME_NAME))
cls.assertOutput(cls.VOLUME_NAME, cmd_output['name'])
- cmd_output = json.loads(cls.openstack(
- 'volume transfer request create -f json ' +
- cls.VOLUME_NAME +
- ' --name ' + cls.NAME))
- cls.assertOutput(cls.NAME, cmd_output['name'])
+ cls.wait_for_status("volume", cls.VOLUME_NAME, "available")
@classmethod
def tearDownClass(cls):
try:
- raw_output_transfer = cls.openstack(
- 'volume transfer request delete ' + cls.NAME)
raw_output_volume = cls.openstack(
'volume delete ' + cls.VOLUME_NAME)
- cls.assertOutput('', raw_output_transfer)
cls.assertOutput('', raw_output_volume)
finally:
super(TransferRequestTests, cls).tearDownClass()
@@ -79,12 +72,28 @@ class TransferRequestTests(common.BaseVolumeTests):
'volume delete ' + volume_name)
self.assertEqual('', raw_output)
- def test_volume_transfer_request_list(self):
+ def test_volume_transfer_request_list_show(self):
+ name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'volume transfer request list -f json'))
- self.assertIn(self.NAME, [req['Name'] for req in cmd_output])
+ 'volume transfer request create -f json ' +
+ ' --name ' + name + ' ' +
+ self.VOLUME_NAME
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume transfer request delete ' + name
+ )
+ self.assertOutput(name, cmd_output['name'])
+ auth_key = cmd_output['auth_key']
+ self.assertTrue(auth_key)
- def test_volume_transfer_request_show(self):
cmd_output = json.loads(self.openstack(
- 'volume transfer request show -f json ' + self.NAME))
- self.assertEqual(self.NAME, cmd_output['name'])
+ 'volume transfer request list -f json'
+ ))
+ self.assertIn(name, [req['Name'] for req in cmd_output])
+
+ cmd_output = json.loads(self.openstack(
+ 'volume transfer request show -f json ' +
+ name
+ ))
+ self.assertEqual(name, cmd_output['name'])
diff --git a/openstackclient/tests/functional/volume/v2/test_transfer_request.py b/openstackclient/tests/functional/volume/v2/test_transfer_request.py
index 33495af..33d8ce7 100644
--- a/openstackclient/tests/functional/volume/v2/test_transfer_request.py
+++ b/openstackclient/tests/functional/volume/v2/test_transfer_request.py
@@ -21,29 +21,24 @@ class TransferRequestTests(common.BaseVolumeTests):
NAME = uuid.uuid4().hex
VOLUME_NAME = uuid.uuid4().hex
+ API_VERSION = '2'
@classmethod
def setUpClass(cls):
super(TransferRequestTests, cls).setUpClass()
cmd_output = json.loads(cls.openstack(
+ '--os-volume-api-version ' + cls.API_VERSION + ' ' +
'volume create -f json --size 1 ' + cls.VOLUME_NAME))
cls.assertOutput(cls.VOLUME_NAME, cmd_output['name'])
- cmd_output = json.loads(cls.openstack(
- 'volume transfer request create -f json ' +
- cls.VOLUME_NAME +
- ' --name ' + cls.NAME))
- cls.assertOutput(cls.NAME, cmd_output['name'])
+ cls.wait_for_status("volume", cls.VOLUME_NAME, "available")
@classmethod
def tearDownClass(cls):
try:
- raw_output_transfer = cls.openstack(
- 'volume transfer request delete ' + cls.NAME)
raw_output_volume = cls.openstack(
'volume delete ' + cls.VOLUME_NAME)
- cls.assertOutput('', raw_output_transfer)
cls.assertOutput('', raw_output_volume)
finally:
super(TransferRequestTests, cls).tearDownClass()
@@ -80,12 +75,31 @@ class TransferRequestTests(common.BaseVolumeTests):
'volume delete ' + volume_name)
self.assertEqual('', raw_output)
- def test_volume_transfer_request_list(self):
+ def test_volume_transfer_request_list_show(self):
+ name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'volume transfer request list -f json'))
- self.assertIn(self.NAME, [req['Name'] for req in cmd_output])
+ '--os-volume-api-version ' + self.API_VERSION + ' ' +
+ 'volume transfer request create -f json ' +
+ ' --name ' + name + ' ' +
+ self.VOLUME_NAME
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume transfer request delete ' + name
+ )
+ self.assertEqual(name, cmd_output['name'])
+ auth_key = cmd_output['auth_key']
+ self.assertTrue(auth_key)
+
+ cmd_output = json.loads(self.openstack(
+ '--os-volume-api-version ' + self.API_VERSION + ' ' +
+ 'volume transfer request list -f json'
+ ))
+ self.assertIn(name, [req['Name'] for req in cmd_output])
- def test_volume_transfer_request_show(self):
cmd_output = json.loads(self.openstack(
- 'volume transfer request show -f json ' + self.NAME))
- self.assertEqual(self.NAME, cmd_output['name'])
+ '--os-volume-api-version ' + self.API_VERSION + ' ' +
+ 'volume transfer request show -f json ' +
+ name
+ ))
+ self.assertEqual(name, cmd_output['name'])
diff --git a/openstackclient/tests/functional/volume/v3/test_transfer_request.py b/openstackclient/tests/functional/volume/v3/test_transfer_request.py
index b325323..f16dfaf 100644
--- a/openstackclient/tests/functional/volume/v3/test_transfer_request.py
+++ b/openstackclient/tests/functional/volume/v3/test_transfer_request.py
@@ -17,3 +17,5 @@ from openstackclient.tests.functional.volume.v3 import common
class TransferRequestTests(common.BaseVolumeTests, v2.TransferRequestTests):
"""Functional tests for transfer request. """
+
+ API_VERSION = '3'
diff --git a/openstackclient/tests/unit/common/test_project_purge.py b/openstackclient/tests/unit/common/test_project_purge.py
index 2385eae..6e8ce18 100644
--- a/openstackclient/tests/unit/common/test_project_purge.py
+++ b/openstackclient/tests/unit/common/test_project_purge.py
@@ -117,10 +117,11 @@ class TestProjectPurge(TestProjectPurgeInit):
self.projects_mock.get.assert_called_once_with(self.project.id)
self.projects_mock.delete.assert_called_once_with(self.project.id)
self.servers_mock.list.assert_called_once_with(
- search_opts={'tenant_id': self.project.id})
+ search_opts={'tenant_id': self.project.id, 'all_tenants': True})
kwargs = {'filters': {'owner': self.project.id}}
self.images_mock.list.assert_called_once_with(**kwargs)
- volume_search_opts = {'project_id': self.project.id}
+ volume_search_opts = {'project_id': self.project.id,
+ 'all_tenants': True}
self.volumes_mock.list.assert_called_once_with(
search_opts=volume_search_opts)
self.snapshots_mock.list.assert_called_once_with(
@@ -152,10 +153,11 @@ class TestProjectPurge(TestProjectPurgeInit):
self.projects_mock.get.assert_called_once_with(self.project.id)
self.projects_mock.delete.assert_not_called()
self.servers_mock.list.assert_called_once_with(
- search_opts={'tenant_id': self.project.id})
+ search_opts={'tenant_id': self.project.id, 'all_tenants': True})
kwargs = {'filters': {'owner': self.project.id}}
self.images_mock.list.assert_called_once_with(**kwargs)
- volume_search_opts = {'project_id': self.project.id}
+ volume_search_opts = {'project_id': self.project.id,
+ 'all_tenants': True}
self.volumes_mock.list.assert_called_once_with(
search_opts=volume_search_opts)
self.snapshots_mock.list.assert_called_once_with(
@@ -187,10 +189,11 @@ class TestProjectPurge(TestProjectPurgeInit):
self.projects_mock.get.assert_called_once_with(self.project.id)
self.projects_mock.delete.assert_not_called()
self.servers_mock.list.assert_called_once_with(
- search_opts={'tenant_id': self.project.id})
+ search_opts={'tenant_id': self.project.id, 'all_tenants': True})
kwargs = {'filters': {'owner': self.project.id}}
self.images_mock.list.assert_called_once_with(**kwargs)
- volume_search_opts = {'project_id': self.project.id}
+ volume_search_opts = {'project_id': self.project.id,
+ 'all_tenants': True}
self.volumes_mock.list.assert_called_once_with(
search_opts=volume_search_opts)
self.snapshots_mock.list.assert_called_once_with(
@@ -223,10 +226,11 @@ class TestProjectPurge(TestProjectPurgeInit):
self.projects_mock.get.assert_not_called()
self.projects_mock.delete.assert_called_once_with(self.project.id)
self.servers_mock.list.assert_called_once_with(
- search_opts={'tenant_id': self.project.id})
+ search_opts={'tenant_id': self.project.id, 'all_tenants': True})
kwargs = {'filters': {'owner': self.project.id}}
self.images_mock.list.assert_called_once_with(**kwargs)
- volume_search_opts = {'project_id': self.project.id}
+ volume_search_opts = {'project_id': self.project.id,
+ 'all_tenants': True}
self.volumes_mock.list.assert_called_once_with(
search_opts=volume_search_opts)
self.snapshots_mock.list.assert_called_once_with(
@@ -259,10 +263,11 @@ class TestProjectPurge(TestProjectPurgeInit):
self.projects_mock.get.assert_called_once_with(self.project.id)
self.projects_mock.delete.assert_called_once_with(self.project.id)
self.servers_mock.list.assert_called_once_with(
- search_opts={'tenant_id': self.project.id})
+ search_opts={'tenant_id': self.project.id, 'all_tenants': True})
kwargs = {'filters': {'owner': self.project.id}}
self.images_mock.list.assert_called_once_with(**kwargs)
- volume_search_opts = {'project_id': self.project.id}
+ volume_search_opts = {'project_id': self.project.id,
+ 'all_tenants': True}
self.volumes_mock.list.assert_called_once_with(
search_opts=volume_search_opts)
self.snapshots_mock.list.assert_called_once_with(
@@ -295,10 +300,11 @@ class TestProjectPurge(TestProjectPurgeInit):
self.projects_mock.get.assert_called_once_with(self.project.id)
self.projects_mock.delete.assert_called_once_with(self.project.id)
self.servers_mock.list.assert_called_once_with(
- search_opts={'tenant_id': self.project.id})
+ search_opts={'tenant_id': self.project.id, 'all_tenants': True})
kwargs = {'filters': {'owner': self.project.id}}
self.images_mock.list.assert_called_once_with(**kwargs)
- volume_search_opts = {'project_id': self.project.id}
+ volume_search_opts = {'project_id': self.project.id,
+ 'all_tenants': True}
self.volumes_mock.list.assert_called_once_with(
search_opts=volume_search_opts)
self.snapshots_mock.list.assert_called_once_with(
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 9a065be..234bbd9 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -17,6 +17,7 @@ import copy
import uuid
import mock
+from novaclient import api_versions
from openstackclient.api import compute_v2
from openstackclient.tests.unit import fakes
@@ -201,6 +202,8 @@ class FakeComputev2Client(object):
self.management_url = kwargs['endpoint']
+ self.api_version = api_versions.APIVersion('2.1')
+
class TestComputev2(utils.TestCommand):
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index b3d3268..45fb400 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -19,6 +19,7 @@ import getpass
import mock
from mock import call
from novaclient import api_versions
+from openstack import exceptions as sdk_exceptions
from osc_lib import exceptions
from osc_lib import utils as common_utils
from oslo_utils import timeutils
@@ -222,11 +223,11 @@ class TestServerAddFloatingIPNetwork(
self.network.ports = mock.Mock(return_value=[_port])
arglist = [
_server.id,
- _floating_ip['ip'],
+ _floating_ip['floating_ip_address'],
]
verifylist = [
('server', _server.id),
- ('ip_address', _floating_ip['ip']),
+ ('ip_address', _floating_ip['floating_ip_address']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -237,7 +238,7 @@ class TestServerAddFloatingIPNetwork(
}
self.network.find_ip.assert_called_once_with(
- _floating_ip['ip'],
+ _floating_ip['floating_ip_address'],
ignore_missing=False,
)
self.network.ports.assert_called_once_with(
@@ -248,6 +249,64 @@ class TestServerAddFloatingIPNetwork(
**attrs
)
+ def test_server_add_floating_ip_default_no_external_gateway(self,
+ success=False):
+ _server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = _server
+ _port = network_fakes.FakePort.create_one_port()
+ _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+ self.network.find_ip = mock.Mock(return_value=_floating_ip)
+ return_value = [_port]
+ # In the success case, we'll have two ports, where the first port is
+ # not attached to an external gateway but the second port is.
+ if success:
+ return_value.append(_port)
+ self.network.ports = mock.Mock(return_value=return_value)
+ side_effect = [sdk_exceptions.NotFoundException()]
+ if success:
+ side_effect.append(None)
+ self.network.update_ip = mock.Mock(side_effect=side_effect)
+ arglist = [
+ _server.id,
+ _floating_ip['floating_ip_address'],
+ ]
+ verifylist = [
+ ('server', _server.id),
+ ('ip_address', _floating_ip['floating_ip_address']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ if success:
+ self.cmd.take_action(parsed_args)
+ else:
+ self.assertRaises(sdk_exceptions.NotFoundException,
+ self.cmd.take_action, parsed_args)
+
+ attrs = {
+ 'port_id': _port.id,
+ }
+
+ self.network.find_ip.assert_called_once_with(
+ _floating_ip['floating_ip_address'],
+ ignore_missing=False,
+ )
+ self.network.ports.assert_called_once_with(
+ device_id=_server.id,
+ )
+ if success:
+ self.assertEqual(2, self.network.update_ip.call_count)
+ calls = [mock.call(_floating_ip, **attrs)] * 2
+ self.network.update_ip.assert_has_calls(calls)
+ else:
+ self.network.update_ip.assert_called_once_with(
+ _floating_ip,
+ **attrs
+ )
+
+ def test_server_add_floating_ip_default_one_external_gateway(self):
+ self.test_server_add_floating_ip_default_no_external_gateway(
+ success=True)
+
def test_server_add_floating_ip_fixed(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
@@ -255,26 +314,31 @@ class TestServerAddFloatingIPNetwork(
_floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
self.network.find_ip = mock.Mock(return_value=_floating_ip)
self.network.ports = mock.Mock(return_value=[_port])
+ # The user has specified a fixed ip that matches one of the ports
+ # already attached to the instance.
arglist = [
- '--fixed-ip-address', _floating_ip['fixed_ip'],
+ '--fixed-ip-address', _port.fixed_ips[0]['ip_address'],
_server.id,
- _floating_ip['ip'],
+ _floating_ip['floating_ip_address'],
]
verifylist = [
- ('fixed_ip_address', _floating_ip['fixed_ip']),
+ ('fixed_ip_address', _port.fixed_ips[0]['ip_address']),
('server', _server.id),
- ('ip_address', _floating_ip['ip']),
+ ('ip_address', _floating_ip['floating_ip_address']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
+ # We expect the update_ip call to specify a new fixed_ip_address which
+ # will overwrite the floating ip's existing fixed_ip_address.
attrs = {
'port_id': _port.id,
+ 'fixed_ip_address': _port.fixed_ips[0]['ip_address'],
}
self.network.find_ip.assert_called_once_with(
- _floating_ip['ip'],
+ _floating_ip['floating_ip_address'],
ignore_missing=False,
)
self.network.ports.assert_called_once_with(
@@ -285,6 +349,40 @@ class TestServerAddFloatingIPNetwork(
**attrs
)
+ def test_server_add_floating_ip_fixed_no_port_found(self):
+ _server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = _server
+ _port = network_fakes.FakePort.create_one_port()
+ _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+ self.network.find_ip = mock.Mock(return_value=_floating_ip)
+ self.network.ports = mock.Mock(return_value=[_port])
+ # The user has specified a fixed ip that does not match any of the
+ # ports already attached to the instance.
+ nonexistent_ip = '10.0.0.9'
+ arglist = [
+ '--fixed-ip-address', nonexistent_ip,
+ _server.id,
+ _floating_ip['floating_ip_address'],
+ ]
+ verifylist = [
+ ('fixed_ip_address', nonexistent_ip),
+ ('server', _server.id),
+ ('ip_address', _floating_ip['floating_ip_address']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ self.network.find_ip.assert_called_once_with(
+ _floating_ip['floating_ip_address'],
+ ignore_missing=False,
+ )
+ self.network.ports.assert_called_once_with(
+ device_id=_server.id,
+ )
+ self.network.update_ip.assert_not_called()
+
class TestServerAddPort(TestServer):
@@ -845,6 +943,55 @@ class TestServerCreate(TestServer):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data)
+ def test_server_create_with_auto_network_default_v2_37(self):
+ """Tests creating a server without specifying --nic using 2.37."""
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # Since check_parser doesn't handle compute global options like
+ # --os-compute-api-version, we have to mock the construction of
+ # the novaclient client object with our own APIVersion.
+ with mock.patch.object(self.app.client_manager.compute, 'api_version',
+ api_versions.APIVersion('2.37')):
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ meta=None,
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='auto',
+ scheduler_hints={},
+ config_drive=None,
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ self.image,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+
def test_server_create_with_none_network(self):
arglist = [
'--image', 'image1',
diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py
index bfe930d..62dcf58 100644
--- a/openstackclient/tests/unit/identity/v3/test_endpoint.py
+++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py
@@ -439,6 +439,47 @@ class TestEndpointList(TestEndpoint):
)
self.assertEqual(datalist, tuple(data))
+ def test_endpoint_list_project_with_project_domain(self):
+ project = identity_fakes.FakeProject.create_one_project()
+ domain = identity_fakes.FakeDomain.create_one_domain()
+
+ self.ep_filter_mock.list_endpoints_for_project.return_value = [
+ self.endpoint
+ ]
+ self.projects_mock.get.return_value = project
+
+ arglist = [
+ '--project', project.name,
+ '--project-domain', domain.name
+ ]
+ verifylist = [
+ ('project', project.name),
+ ('project_domain', domain.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+ self.ep_filter_mock.list_endpoints_for_project.assert_called_with(
+ project=project.id
+ )
+
+ self.assertEqual(self.columns, columns)
+ datalist = (
+ (
+ self.endpoint.id,
+ self.endpoint.region,
+ self.service.name,
+ self.service.type,
+ True,
+ self.endpoint.interface,
+ self.endpoint.url,
+ ),
+ )
+ self.assertEqual(datalist, tuple(data))
+
class TestEndpointSet(TestEndpoint):
diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py
index 304aa91..971567c 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume.py
@@ -133,7 +133,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -185,7 +184,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=consistency_group.id,
- source_replica=None,
multiattach=True,
scheduler_hints={'k': 'v'},
)
@@ -231,7 +229,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -277,7 +274,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -317,7 +313,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -359,7 +354,6 @@ class TestVolumeCreate(TestVolume):
imageRef=image.id,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -401,7 +395,6 @@ class TestVolumeCreate(TestVolume):
imageRef=image.id,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -442,7 +435,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -484,7 +476,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -530,7 +521,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -585,7 +575,6 @@ class TestVolumeCreate(TestVolume):
imageRef=None,
source_volid=None,
consistencygroup_id=None,
- source_replica=None,
multiattach=False,
scheduler_hints=None,
)
@@ -598,40 +587,6 @@ class TestVolumeCreate(TestVolume):
self.volumes_mock.update_readonly_flag.assert_called_with(
self.new_volume.id, True)
- def test_volume_create_with_source_replicated(self):
- self.volumes_mock.get.return_value = self.new_volume
- arglist = [
- '--source-replicated', self.new_volume.id,
- self.new_volume.name,
- ]
- verifylist = [
- ('source_replicated', self.new_volume.id),
- ('name', self.new_volume.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
- self.volumes_mock.create.assert_called_once_with(
- size=None,
- snapshot_id=None,
- name=self.new_volume.name,
- description=None,
- volume_type=None,
- user_id=None,
- project_id=None,
- availability_zone=None,
- metadata=None,
- imageRef=None,
- source_volid=None,
- consistencygroup_id=None,
- source_replica=self.new_volume.id,
- multiattach=False,
- scheduler_hints=None,
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
-
def test_volume_create_without_size(self):
arglist = [
self.new_volume.name,
@@ -649,7 +604,6 @@ class TestVolumeCreate(TestVolume):
'--image', 'source_image',
'--source', 'source_volume',
'--snapshot', 'source_snapshot',
- '--source-replicated', 'source_replicated_volume',
'--size', str(self.new_volume.size),
self.new_volume.name,
]
@@ -657,7 +611,6 @@ class TestVolumeCreate(TestVolume):
('image', 'source_image'),
('source', 'source_volume'),
('snapshot', 'source_snapshot'),
- ('source-replicated', 'source_replicated_volume'),
('size', self.new_volume.size),
('name', self.new_volume.name),
]
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index ee3d2f2..fc648ce 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -14,6 +14,7 @@
"""Volume V2 Volume action implementations"""
+import argparse
import copy
import logging
@@ -37,7 +38,7 @@ def _check_size_arg(args):
volume is not specified.
"""
- if ((args.snapshot or args.source or args.source_replicated)
+ if ((args.snapshot or args.source)
is None and args.size is None):
msg = _("--size is a required option if snapshot "
"or source volume is not specified.")
@@ -59,7 +60,7 @@ class CreateVolume(command.ShowOne):
metavar="<size>",
type=int,
help=_("Volume size in GB (Required unless --snapshot or "
- "--source or --source-replicated is specified)"),
+ "--source is specified)"),
)
parser.add_argument(
"--type",
@@ -85,7 +86,7 @@ class CreateVolume(command.ShowOne):
source_group.add_argument(
"--source-replicated",
metavar="<replicated-volume>",
- help=_("Replicated volume to clone (name or ID)"),
+ help=argparse.SUPPRESS,
)
parser.add_argument(
"--description",
@@ -168,12 +169,6 @@ class CreateVolume(command.ShowOne):
volume_client.volumes,
parsed_args.source).id
- replicated_source_volume = None
- if parsed_args.source_replicated:
- replicated_source_volume = utils.find_resource(
- volume_client.volumes,
- parsed_args.source_replicated).id
-
consistency_group = None
if parsed_args.consistency_group:
consistency_group = utils.find_resource(
@@ -227,7 +222,6 @@ class CreateVolume(command.ShowOne):
imageRef=image,
source_volid=source_volume,
consistencygroup_id=consistency_group,
- source_replica=replicated_source_volume,
multiattach=parsed_args.multi_attach,
scheduler_hints=parsed_args.hint,
)
diff --git a/releasenotes/notes/floating-ip-multi-port-9779e88f590cae23.yaml b/releasenotes/notes/floating-ip-multi-port-9779e88f590cae23.yaml
new file mode 100644
index 0000000..738190f
--- /dev/null
+++ b/releasenotes/notes/floating-ip-multi-port-9779e88f590cae23.yaml
@@ -0,0 +1,14 @@
+---
+fixes:
+ - |
+ The ``openstack server add floating ip`` command has been fixed to handle
+ servers with multiple ports attached. Previously, the command was using
+ the first port in the port list when attempting to associate the floating
+ ip. This could fail if the server had multiple ports and the first port
+ in the list was not attached to an external gateway. Another way it could
+ fail is if the ``--fixed-ip-address`` option was passed and the first port
+ did not have the specified fixed IP address attached to it.
+ Now, the ``openstack server add floating ip`` command will find the port
+ attached to the specified ``--fixed-ip-address``, if provided, else it will
+ try multiple ports until one is found attached to an external gateway. If
+ a suitable port is not found in the port list, an error will be returned.
diff --git a/tox.ini b/tox.ini
index 4aad012..cfc945d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -11,7 +11,7 @@ setenv = VIRTUAL_ENV={envdir}
OS_STDERR_CAPTURE=1
OS_TEST_TIMEOUT=60
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky}
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt
commands = stestr run {posargs}
@@ -88,7 +88,7 @@ commands =
[testenv:venv]
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands = {posargs}
@@ -110,7 +110,7 @@ commands =
[testenv:docs]
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands =
@@ -119,7 +119,7 @@ commands =
[testenv:releasenotes]
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/rocky}
-r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html