diff options
Diffstat (limited to 'openstackclient')
| -rw-r--r-- | openstackclient/common/command.py | 42 | ||||
| -rw-r--r-- | openstackclient/common/utils.py | 22 | ||||
| -rw-r--r-- | openstackclient/compute/v2/flavor.py | 28 | ||||
| -rw-r--r-- | openstackclient/compute/v2/server.py | 121 | ||||
| -rw-r--r-- | openstackclient/image/v1/image.py | 65 | ||||
| -rw-r--r-- | openstackclient/image/v2/image.py | 84 | ||||
| -rw-r--r-- | openstackclient/network/v2/port.py | 42 | ||||
| -rw-r--r-- | openstackclient/tests/common/test_command.py | 32 | ||||
| -rw-r--r-- | openstackclient/tests/compute/v2/test_security_group.py | 45 | ||||
| -rw-r--r-- | openstackclient/tests/compute/v2/test_security_group_rule.py | 59 | ||||
| -rw-r--r-- | openstackclient/tests/compute/v2/test_server.py | 146 | ||||
| -rw-r--r-- | openstackclient/tests/image/v1/test_image.py | 9 | ||||
| -rw-r--r-- | openstackclient/tests/image/v2/test_image.py | 63 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/fakes.py | 77 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/test_port.py | 53 |
15 files changed, 560 insertions, 328 deletions
diff --git a/openstackclient/common/command.py b/openstackclient/common/command.py new file mode 100644 index 00000000..b8d9fc6f --- /dev/null +++ b/openstackclient/common/command.py @@ -0,0 +1,42 @@ +# Copyright 2016 NEC Corporation +# +# 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. + +import abc +import logging + +from cliff import command +from cliff import lister +from cliff import show +import six + + +class CommandMeta(abc.ABCMeta): + def __new__(mcs, name, bases, cls_dict): + if 'log' not in cls_dict: + cls_dict['log'] = logging.getLogger( + cls_dict['__module__'] + '.' + name) + return super(CommandMeta, mcs).__new__(mcs, name, bases, cls_dict) + + +@six.add_metaclass(CommandMeta) +class Command(command.Command): + pass + + +class Lister(Command, lister.Lister): + pass + + +class ShowOne(Command, show.ShowOne): + pass diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 096c995b..3ae30c8f 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -26,28 +26,32 @@ from oslo_utils import importutils from openstackclient.common import exceptions -def log_method(log, level=logging.DEBUG): - """Logs a method and its arguments when entered.""" +class log_method(object): - def decorator(func): + def __init__(self, log=None, level=logging.DEBUG): + self._log = log + self._level = level + + def __call__(self, func): func_name = func.__name__ + if not self._log: + self._log = logging.getLogger(func.__class__.__name__) @six.wraps(func) - def wrapper(self, *args, **kwargs): - if log.isEnabledFor(level): + def wrapper(*args, **kwargs): + if self._log.isEnabledFor(self._level): pretty_args = [] if args: pretty_args.extend(str(a) for a in args) if kwargs: pretty_args.extend( "%s=%s" % (k, v) for k, v in six.iteritems(kwargs)) - log.log(level, "%s(%s)", func_name, ", ".join(pretty_args)) - return func(self, *args, **kwargs) + self._log.log(self._level, "%s(%s)", + func_name, ", ".join(pretty_args)) + return func(*args, **kwargs) return wrapper - return decorator - def find_resource(manager, name_or_id, **kwargs): """Helper for the _find_* methods. diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index b34197e0..1af5fe70 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -15,22 +15,16 @@ """Flavor action implementations""" -import logging import six -from cliff import command -from cliff import lister -from cliff import show - +from openstackclient.common import command from openstackclient.common import parseractions from openstackclient.common import utils -class CreateFlavor(show.ShowOne): +class CreateFlavor(command.ShowOne): """Create new flavor""" - log = logging.getLogger(__name__ + ".CreateFlavor") - def get_parser(self, prog_name): parser = super(CreateFlavor, self).get_parser(prog_name) parser.add_argument( @@ -128,8 +122,6 @@ class CreateFlavor(show.ShowOne): class DeleteFlavor(command.Command): """Delete flavor""" - log = logging.getLogger(__name__ + ".DeleteFlavor") - def get_parser(self, prog_name): parser = super(DeleteFlavor, self).get_parser(prog_name) parser.add_argument( @@ -147,11 +139,9 @@ class DeleteFlavor(command.Command): compute_client.flavors.delete(flavor.id) -class ListFlavor(lister.Lister): +class ListFlavor(command.Lister): """List flavors""" - log = logging.getLogger(__name__ + ".ListFlavor") - def get_parser(self, prog_name): parser = super(ListFlavor, self).get_parser(prog_name) public_group = parser.add_mutually_exclusive_group() @@ -231,11 +221,9 @@ class ListFlavor(lister.Lister): ) for s in data)) -class ShowFlavor(show.ShowOne): +class ShowFlavor(command.ShowOne): """Display flavor details""" - log = logging.getLogger(__name__ + ".ShowFlavor") - def get_parser(self, prog_name): parser = super(ShowFlavor, self).get_parser(prog_name) parser.add_argument( @@ -258,11 +246,9 @@ class ShowFlavor(show.ShowOne): return zip(*sorted(six.iteritems(flavor))) -class SetFlavor(show.ShowOne): +class SetFlavor(command.ShowOne): """Set flavor properties""" - log = logging.getLogger(__name__ + ".SetFlavor") - def get_parser(self, prog_name): parser = super(SetFlavor, self).get_parser(prog_name) parser.add_argument( @@ -292,11 +278,9 @@ class SetFlavor(show.ShowOne): return zip(*sorted(six.iteritems(flavor))) -class UnsetFlavor(show.ShowOne): +class UnsetFlavor(command.ShowOne): """Unset flavor properties""" - log = logging.getLogger(__name__ + ".UnsetFlavor") - def get_parser(self, prog_name): parser = super(UnsetFlavor, self).get_parser(prog_name) parser.add_argument( diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index f5876d4f..b4db6216 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -18,14 +18,11 @@ import argparse import getpass import io -import logging import os import six import sys -from cliff import command -from cliff import lister -from cliff import show +from openstackclient.common import command try: from novaclient.v2 import servers @@ -173,8 +170,6 @@ def _show_progress(progress): class AddServerSecurityGroup(command.Command): """Add security group to server""" - log = logging.getLogger(__name__ + '.AddServerSecurityGroup') - def get_parser(self, prog_name): parser = super(AddServerSecurityGroup, self).get_parser(prog_name) parser.add_argument( @@ -209,8 +204,6 @@ class AddServerSecurityGroup(command.Command): class AddServerVolume(command.Command): """Add volume to server""" - log = logging.getLogger(__name__ + '.AddServerVolume') - def get_parser(self, prog_name): parser = super(AddServerVolume, self).get_parser(prog_name) parser.add_argument( @@ -252,11 +245,9 @@ class AddServerVolume(command.Command): ) -class CreateServer(show.ShowOne): +class CreateServer(command.ShowOne): """Create a new server""" - log = logging.getLogger(__name__ + '.CreateServer') - def get_parser(self, prog_name): parser = super(CreateServer, self).get_parser(prog_name) parser.add_argument( @@ -379,7 +370,7 @@ class CreateServer(show.ShowOne): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.volume @@ -556,11 +547,9 @@ class CreateServer(show.ShowOne): return zip(*sorted(six.iteritems(details))) -class CreateServerImage(show.ShowOne): +class CreateServerImage(command.ShowOne): """Create a new disk image from a running server""" - log = logging.getLogger(__name__ + '.CreateServerImage') - def get_parser(self, prog_name): parser = super(CreateServerImage, self).get_parser(prog_name) parser.add_argument( @@ -580,7 +569,7 @@ class CreateServerImage(show.ShowOne): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image @@ -622,8 +611,6 @@ class CreateServerImage(show.ShowOne): class DeleteServer(command.Command): """Delete server(s)""" - log = logging.getLogger(__name__ + '.DeleteServer') - def get_parser(self, prog_name): parser = super(DeleteServer, self).get_parser(prog_name) parser.add_argument( @@ -639,7 +626,7 @@ class DeleteServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for server in parsed_args.server: @@ -660,11 +647,9 @@ class DeleteServer(command.Command): raise SystemExit -class ListServer(lister.Lister): +class ListServer(command.Lister): """List servers""" - log = logging.getLogger(__name__ + '.ListServer') - def get_parser(self, prog_name): parser = super(ListServer, self).get_parser(prog_name) parser.add_argument( @@ -756,7 +741,7 @@ class ListServer(lister.Lister): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity @@ -877,8 +862,6 @@ class LockServer(command.Command): """Lock server(s). A non-admin user will not be able to execute actions""" - log = logging.getLogger(__name__ + '.LockServer') - def get_parser(self, prog_name): parser = super(LockServer, self).get_parser(prog_name) parser.add_argument( @@ -889,7 +872,7 @@ class LockServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -913,8 +896,6 @@ class LockServer(command.Command): class MigrateServer(command.Command): """Migrate server to different host""" - log = logging.getLogger(__name__ + '.MigrateServer') - def get_parser(self, prog_name): parser = super(MigrateServer, self).get_parser(prog_name) parser.add_argument( @@ -963,7 +944,7 @@ class MigrateServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -996,8 +977,6 @@ class MigrateServer(command.Command): class PauseServer(command.Command): """Pause server(s)""" - log = logging.getLogger(__name__ + '.PauseServer') - def get_parser(self, prog_name): parser = super(PauseServer, self).get_parser(prog_name) parser.add_argument( @@ -1008,7 +987,7 @@ class PauseServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for server in parsed_args.server: @@ -1021,8 +1000,6 @@ class PauseServer(command.Command): class RebootServer(command.Command): """Perform a hard or soft server reboot""" - log = logging.getLogger(__name__ + '.RebootServer') - def get_parser(self, prog_name): parser = super(RebootServer, self).get_parser(prog_name) parser.add_argument( @@ -1054,7 +1031,7 @@ class RebootServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute server = utils.find_resource( @@ -1073,11 +1050,9 @@ class RebootServer(command.Command): raise SystemExit -class RebuildServer(show.ShowOne): +class RebuildServer(command.ShowOne): """Rebuild server""" - log = logging.getLogger(__name__ + '.RebuildServer') - def get_parser(self, prog_name): parser = super(RebuildServer, self).get_parser(prog_name) parser.add_argument( @@ -1103,7 +1078,7 @@ class RebuildServer(show.ShowOne): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1133,8 +1108,6 @@ class RebuildServer(show.ShowOne): class RemoveServerSecurityGroup(command.Command): """Remove security group from server""" - log = logging.getLogger(__name__ + '.RemoveServerSecurityGroup') - def get_parser(self, prog_name): parser = super(RemoveServerSecurityGroup, self).get_parser(prog_name) parser.add_argument( @@ -1169,8 +1142,6 @@ class RemoveServerSecurityGroup(command.Command): class RemoveServerVolume(command.Command): """Remove volume from server""" - log = logging.getLogger(__name__ + '.RemoveServerVolume') - def get_parser(self, prog_name): parser = super(RemoveServerVolume, self).get_parser(prog_name) parser.add_argument( @@ -1206,11 +1177,9 @@ class RemoveServerVolume(command.Command): ) -class RescueServer(show.ShowOne): +class RescueServer(command.ShowOne): """Put server in rescue mode""" - log = logging.getLogger(__name__ + '.RescueServer') - def get_parser(self, prog_name): parser = super(RescueServer, self).get_parser(prog_name) parser.add_argument( @@ -1220,7 +1189,7 @@ class RescueServer(show.ShowOne): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1234,8 +1203,6 @@ class RescueServer(show.ShowOne): class ResizeServer(command.Command): """Scale server to a new flavor""" - log = logging.getLogger(__name__ + '.ResizeServer') - def get_parser(self, prog_name): parser = super(ResizeServer, self).get_parser(prog_name) phase_group = parser.add_mutually_exclusive_group() @@ -1266,7 +1233,7 @@ class ResizeServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1300,8 +1267,6 @@ class ResizeServer(command.Command): class ResumeServer(command.Command): """Resume server(s)""" - log = logging.getLogger(__name__ + '.ResumeServer') - def get_parser(self, prog_name): parser = super(ResumeServer, self).get_parser(prog_name) parser.add_argument( @@ -1312,7 +1277,7 @@ class ResumeServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1326,8 +1291,6 @@ class ResumeServer(command.Command): class SetServer(command.Command): """Set server properties""" - log = logging.getLogger(__name__ + '.SetServer') - def get_parser(self, prog_name): parser = super(SetServer, self).get_parser(prog_name) parser.add_argument( @@ -1354,7 +1317,7 @@ class SetServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1385,8 +1348,6 @@ class SetServer(command.Command): class ShelveServer(command.Command): """Shelve server(s)""" - log = logging.getLogger(__name__ + '.ShelveServer') - def get_parser(self, prog_name): parser = super(ShelveServer, self).get_parser(prog_name) parser.add_argument( @@ -1397,7 +1358,7 @@ class ShelveServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for server in parsed_args.server: @@ -1407,11 +1368,9 @@ class ShelveServer(command.Command): ).shelve() -class ShowServer(show.ShowOne): +class ShowServer(command.ShowOne): """Show server details""" - log = logging.getLogger(__name__ + '.ShowServer') - def get_parser(self, prog_name): parser = super(ShowServer, self).get_parser(prog_name) parser.add_argument( @@ -1427,7 +1386,7 @@ class ShowServer(show.ShowOne): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute server = utils.find_resource(compute_client.servers, @@ -1447,8 +1406,6 @@ class ShowServer(show.ShowOne): class SshServer(command.Command): """Ssh to server""" - log = logging.getLogger(__name__ + '.SshServer') - def get_parser(self, prog_name): parser = super(SshServer, self).get_parser(prog_name) parser.add_argument( @@ -1550,7 +1507,7 @@ class SshServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1594,8 +1551,6 @@ class SshServer(command.Command): class StartServer(command.Command): """Start server(s).""" - log = logging.getLogger(__name__ + '.StartServer') - def get_parser(self, prog_name): parser = super(StartServer, self).get_parser(prog_name) parser.add_argument( @@ -1606,7 +1561,7 @@ class StartServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for server in parsed_args.server: @@ -1619,8 +1574,6 @@ class StartServer(command.Command): class StopServer(command.Command): """Stop server(s).""" - log = logging.getLogger(__name__ + '.StopServer') - def get_parser(self, prog_name): parser = super(StopServer, self).get_parser(prog_name) parser.add_argument( @@ -1631,7 +1584,7 @@ class StopServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for server in parsed_args.server: @@ -1644,8 +1597,6 @@ class StopServer(command.Command): class SuspendServer(command.Command): """Suspend server(s)""" - log = logging.getLogger(__name__ + '.SuspendServer') - def get_parser(self, prog_name): parser = super(SuspendServer, self).get_parser(prog_name) parser.add_argument( @@ -1656,7 +1607,7 @@ class SuspendServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1670,8 +1621,6 @@ class SuspendServer(command.Command): class UnlockServer(command.Command): """Unlock server(s)""" - log = logging.getLogger(__name__ + '.UnlockServer') - def get_parser(self, prog_name): parser = super(UnlockServer, self).get_parser(prog_name) parser.add_argument( @@ -1682,7 +1631,7 @@ class UnlockServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1696,8 +1645,6 @@ class UnlockServer(command.Command): class UnpauseServer(command.Command): """Unpause server(s)""" - log = logging.getLogger(__name__ + '.UnpauseServer') - def get_parser(self, prog_name): parser = super(UnpauseServer, self).get_parser(prog_name) parser.add_argument( @@ -1708,7 +1655,7 @@ class UnpauseServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1722,8 +1669,6 @@ class UnpauseServer(command.Command): class UnrescueServer(command.Command): """Restore server from rescue mode""" - log = logging.getLogger(__name__ + '.UnrescueServer') - def get_parser(self, prog_name): parser = super(UnrescueServer, self).get_parser(prog_name) parser.add_argument( @@ -1733,7 +1678,7 @@ class UnrescueServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -1746,8 +1691,6 @@ class UnrescueServer(command.Command): class UnsetServer(command.Command): """Unset server properties""" - log = logging.getLogger(__name__ + '.UnsetServer') - def get_parser(self, prog_name): parser = super(UnsetServer, self).get_parser(prog_name) parser.add_argument( @@ -1765,7 +1708,7 @@ class UnsetServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute server = utils.find_resource( @@ -1783,8 +1726,6 @@ class UnsetServer(command.Command): class UnshelveServer(command.Command): """Unshelve server(s)""" - log = logging.getLogger(__name__ + '.UnshelveServer') - def get_parser(self, prog_name): parser = super(UnshelveServer, self).get_parser(prog_name) parser.add_argument( @@ -1795,7 +1736,7 @@ class UnshelveServer(command.Command): ) return parser - @utils.log_method(log) + @utils.log_method() def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for server in parsed_args.server: diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 0382501e..c18f3fc7 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -35,6 +35,7 @@ from glanceclient.common import utils as gc_utils from openstackclient.api import utils as api_utils from openstackclient.common import parseractions from openstackclient.common import utils +from openstackclient.i18n import _ # noqa DEFAULT_CONTAINER_FORMAT = 'bare' @@ -93,11 +94,6 @@ class CreateImage(show.ShowOne): "(default: %s)" % DEFAULT_DISK_FORMAT, ) parser.add_argument( - "--owner", - metavar="<project>", - help="Image owner project name or ID", - ) - parser.add_argument( "--size", metavar="<size>", help="Image size, in bytes (only used with --location and" @@ -178,12 +174,32 @@ class CreateImage(show.ShowOne): help="Set a property on this image " "(repeat option to set multiple properties)", ) + # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early + # 2.x release. Do not remove before Jan 2017 + # and a 3.x release. + project_group = parser.add_mutually_exclusive_group() + project_group.add_argument( + "--project", + metavar="<project>", + help="Set an alternate project on this image (name or ID)", + ) + project_group.add_argument( + "--owner", + metavar="<project>", + help=argparse.SUPPRESS, + ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) image_client = self.app.client_manager.image + if getattr(parsed_args, 'owner', None) is not None: + self.log.warning(_( + 'The --owner option is deprecated, ' + 'please use --project instead.' + )) + # Build an attribute dict from the parsed args, only include # attributes that were actually set on the command line kwargs = {} @@ -198,6 +214,12 @@ class CreateImage(show.ShowOne): # Only include a value in kwargs for attributes that are # actually present on the command line kwargs[attr] = val + + # Special case project option back to API attribute name 'owner' + val = getattr(parsed_args, 'project', None) + if val: + kwargs['owner'] = val + # Handle exclusive booleans with care # Avoid including attributes in kwargs if an option is not # present on the command line. These exclusive booleans are not @@ -383,7 +405,7 @@ class ListImage(lister.Lister): 'Status', 'Visibility', 'Protected', - 'Owner', + 'Project', 'Properties', ) else: @@ -477,11 +499,6 @@ class SetImage(command.Command): help="New image name", ) parser.add_argument( - "--owner", - metavar="<project>", - help="New image owner project (name or ID)", - ) - parser.add_argument( "--min-disk", metavar="<disk-gb>", type=int, @@ -590,12 +607,32 @@ class SetImage(command.Command): metavar="<checksum>", help="Image hash used for verification", ) + # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early + # 2.x release. Do not remove before Jan 2017 + # and a 3.x release. + project_group = parser.add_mutually_exclusive_group() + project_group.add_argument( + "--project", + metavar="<project>", + help="Set an alternate project on this image (name or ID)", + ) + project_group.add_argument( + "--owner", + metavar="<project>", + help=argparse.SUPPRESS, + ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) image_client = self.app.client_manager.image + if getattr(parsed_args, 'owner', None) is not None: + self.log.warning(_( + 'The --owner option is deprecated, ' + 'please use --project instead.' + )) + kwargs = {} copy_attrs = ('name', 'owner', 'min_disk', 'min_ram', 'properties', 'container_format', 'disk_format', 'size', 'store', @@ -607,6 +644,12 @@ class SetImage(command.Command): # Only include a value in kwargs for attributes that are # actually present on the command line kwargs[attr] = val + + # Special case project option back to API attribute name 'owner' + val = getattr(parsed_args, 'project', None) + if val: + kwargs['owner'] = val + # Handle exclusive booleans with care # Avoid including attributes in kwargs if an option is not # present on the command line. These exclusive booleans are not diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 106da9ef..123599ed 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -28,6 +28,7 @@ from openstackclient.api import utils as api_utils from openstackclient.common import exceptions from openstackclient.common import parseractions from openstackclient.common import utils +from openstackclient.i18n import _ # noqa from openstackclient.identity import common @@ -148,11 +149,6 @@ class CreateImage(show.ShowOne): "(default: %s)" % DEFAULT_DISK_FORMAT, ) parser.add_argument( - "--owner", - metavar="<owner>", - help="Image owner project name or ID", - ) - parser.add_argument( "--min-disk", metavar="<disk-gb>", type=int, @@ -220,6 +216,20 @@ class CreateImage(show.ShowOne): help="Set a tag on this image " "(repeat option to set multiple tags)", ) + # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early + # 2.x release. Do not remove before Jan 2017 + # and a 3.x release. + project_group = parser.add_mutually_exclusive_group() + project_group.add_argument( + "--project", + metavar="<project>", + help="Set an alternate project on this image (name or ID)", + ) + project_group.add_argument( + "--owner", + metavar="<project>", + help=argparse.SUPPRESS, + ) common.add_project_domain_option_to_parser(parser) for deadopt in self.deadopts: parser.add_argument( @@ -246,8 +256,7 @@ class CreateImage(show.ShowOne): kwargs = {} copy_attrs = ('name', 'id', 'container_format', 'disk_format', - 'min_disk', 'min_ram', - 'tags', 'owner') + 'min_disk', 'min_ram', 'tags') for attr in copy_attrs: if attr in parsed_args: val = getattr(parsed_args, attr, None) @@ -255,6 +264,7 @@ class CreateImage(show.ShowOne): # Only include a value in kwargs for attributes that # are actually present on the command line kwargs[attr] = val + # properties should get flattened into the general kwargs if getattr(parsed_args, 'properties', None): for k, v in six.iteritems(parsed_args.properties): @@ -275,6 +285,21 @@ class CreateImage(show.ShowOne): if parsed_args.private: kwargs['visibility'] = 'private' + # Handle deprecated --owner option + project_arg = parsed_args.project + if parsed_args.owner: + project_arg = parsed_args.owner + self.log.warning(_( + 'The --owner option is deprecated, ' + 'please use --project instead.' + )) + if project_arg: + kwargs['owner'] = common.find_project( + identity_client, + project_arg, + parsed_args.project_domain, + ).id + # open the file first to ensure any failures are handled before the # image is created fp = gc_utils.get_data_file(parsed_args) @@ -458,7 +483,7 @@ class ListImage(lister.Lister): 'Status', 'Visibility', 'Protected', - 'Owner', + 'Project', 'Tags', ) else: @@ -599,11 +624,6 @@ class SetImage(command.Command): help="New image name" ) parser.add_argument( - "--owner", - metavar="<project>", - help="New image owner project (name or ID)", - ) - parser.add_argument( "--min-disk", type=int, metavar="<disk-gb>", @@ -713,6 +733,20 @@ class SetImage(command.Command): action="store_true", help="Activate the image", ) + # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early + # 2.x release. Do not remove before Jan 2017 + # and a 3.x release. + project_group = parser.add_mutually_exclusive_group() + project_group.add_argument( + "--project", + metavar="<project>", + help="Set an alternate project on this image (name or ID)", + ) + project_group.add_argument( + "--owner", + metavar="<project>", + help=argparse.SUPPRESS, + ) common.add_project_domain_option_to_parser(parser) for deadopt in self.deadopts: parser.add_argument( @@ -738,7 +772,7 @@ class SetImage(command.Command): copy_attrs = ('architecture', 'container_format', 'disk_format', 'file', 'instance_id', 'kernel_id', 'locations', 'min_disk', 'min_ram', 'name', 'os_distro', 'os_version', - 'owner', 'prefix', 'progress', 'ramdisk_id', 'tags') + 'prefix', 'progress', 'ramdisk_id', 'tags') for attr in copy_attrs: if attr in parsed_args: val = getattr(parsed_args, attr, None) @@ -767,6 +801,21 @@ class SetImage(command.Command): if parsed_args.private: kwargs['visibility'] = 'private' + # Handle deprecated --owner option + project_arg = parsed_args.project + if parsed_args.owner: + project_arg = parsed_args.owner + self.log.warning(_( + 'The --owner option is deprecated, ' + 'please use --project instead.' + )) + if project_arg: + kwargs['owner'] = common.find_project( + identity_client, + project_arg, + parsed_args.project_domain, + ).id + # Checks if anything that requires getting the image if not (kwargs or parsed_args.deactivate or parsed_args.activate): self.log.warning("No arguments specified") @@ -791,13 +840,6 @@ class SetImage(command.Command): # Tags should be extended, but duplicates removed kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags))) - if parsed_args.owner: - kwargs['owner'] = common.find_project( - identity_client, - parsed_args.owner, - parsed_args.project_domain, - ).id - try: image = image_client.images.update(image.id, **kwargs) except Exception as e: diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py new file mode 100644 index 00000000..ad906a28 --- /dev/null +++ b/openstackclient/network/v2/port.py @@ -0,0 +1,42 @@ +# 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. +# + +"""Port action implementations""" + +import logging + +from cliff import command + + +class DeletePort(command.Command): + """Delete port(s)""" + + log = logging.getLogger(__name__ + '.DeletePort') + + def get_parser(self, prog_name): + parser = super(DeletePort, self).get_parser(prog_name) + parser.add_argument( + 'port', + metavar="<port>", + nargs="+", + help=("Port(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.network + + for port in parsed_args.port: + res = client.find_port(port) + client.delete_port(res) diff --git a/openstackclient/tests/common/test_command.py b/openstackclient/tests/common/test_command.py new file mode 100644 index 00000000..1b2584bd --- /dev/null +++ b/openstackclient/tests/common/test_command.py @@ -0,0 +1,32 @@ +# Copyright 2016 NEC Corporation +# +# 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. + +import mock + +from openstackclient.common import command +from openstackclient.tests import utils as test_utils + + +class FakeCommand(command.Command): + def take_action(self, parsed_args): + pass + + +class TestCommand(test_utils.TestCase): + + def test_command_has_logger(self): + cmd = FakeCommand(mock.Mock(), mock.Mock()) + self.assertTrue(hasattr(cmd, 'log')) + self.assertEqual('openstackclient.tests.common.test_command.' + 'FakeCommand', cmd.log.name) diff --git a/openstackclient/tests/compute/v2/test_security_group.py b/openstackclient/tests/compute/v2/test_security_group.py index fdb659a8..87cc4870 100644 --- a/openstackclient/tests/compute/v2/test_security_group.py +++ b/openstackclient/tests/compute/v2/test_security_group.py @@ -56,6 +56,19 @@ class TestSecurityGroup(compute_fakes.TestComputev2): class TestSecurityGroupCreate(TestSecurityGroup): + columns = ( + 'description', + 'id', + 'name', + 'tenant_id', + ) + data = ( + security_group_description, + security_group_id, + security_group_name, + identity_fakes.project_id, + ) + def setUp(self): super(TestSecurityGroupCreate, self).setUp() @@ -86,20 +99,8 @@ class TestSecurityGroupCreate(TestSecurityGroup): security_group_name, ) - collist = ( - 'description', - 'id', - 'name', - 'tenant_id', - ) - self.assertEqual(collist, columns) - datalist = ( - security_group_description, - security_group_id, - security_group_name, - identity_fakes.project_id, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_security_group_create_description(self): arglist = [ @@ -121,20 +122,8 @@ class TestSecurityGroupCreate(TestSecurityGroup): security_group_description, ) - collist = ( - 'description', - 'id', - 'name', - 'tenant_id', - ) - self.assertEqual(collist, columns) - datalist = ( - security_group_description, - security_group_id, - security_group_name, - identity_fakes.project_id, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) class TestSecurityGroupList(TestSecurityGroup): diff --git a/openstackclient/tests/compute/v2/test_security_group_rule.py b/openstackclient/tests/compute/v2/test_security_group_rule.py index 0e7ee05d..a2f9b108 100644 --- a/openstackclient/tests/compute/v2/test_security_group_rule.py +++ b/openstackclient/tests/compute/v2/test_security_group_rule.py @@ -116,6 +116,15 @@ class TestSecurityGroupRule(compute_fakes.TestComputev2): class TestSecurityGroupRuleCreate(TestSecurityGroupRule): + columns = ( + 'id', + 'ip_protocol', + 'ip_range', + 'parent_group_id', + 'port_range', + 'remote_security_group', + ) + def setUp(self): super(TestSecurityGroupRuleCreate, self).setUp() @@ -156,15 +165,7 @@ class TestSecurityGroupRuleCreate(TestSecurityGroupRule): None, ) - collist = ( - 'id', - 'ip_protocol', - 'ip_range', - 'parent_group_id', - 'port_range', - 'remote_security_group', - ) - self.assertEqual(collist, columns) + self.assertEqual(self.columns, columns) datalist = ( security_group_rule_id, 'tcp', @@ -208,15 +209,7 @@ class TestSecurityGroupRuleCreate(TestSecurityGroupRule): None, ) - collist = ( - 'id', - 'ip_protocol', - 'ip_range', - 'parent_group_id', - 'port_range', - 'remote_security_group', - ) - self.assertEqual(collist, columns) + self.assertEqual(self.columns, columns) datalist = ( security_group_rule_id, 'tcp', @@ -264,15 +257,7 @@ class TestSecurityGroupRuleCreate(TestSecurityGroupRule): security_group_id, ) - collist = ( - 'id', - 'ip_protocol', - 'ip_range', - 'parent_group_id', - 'port_range', - 'remote_security_group', - ) - self.assertEqual(collist, columns) + self.assertEqual(self.columns, columns) datalist = ( security_group_rule_id, 'tcp', @@ -315,15 +300,7 @@ class TestSecurityGroupRuleCreate(TestSecurityGroupRule): None, ) - collist = ( - 'id', - 'ip_protocol', - 'ip_range', - 'parent_group_id', - 'port_range', - 'remote_security_group', - ) - self.assertEqual(collist, columns) + self.assertEqual(self.columns, columns) datalist = ( security_group_rule_id, 'udp', @@ -369,15 +346,7 @@ class TestSecurityGroupRuleCreate(TestSecurityGroupRule): None, ) - collist = ( - 'id', - 'ip_protocol', - 'ip_range', - 'parent_group_id', - 'port_range', - 'remote_security_group', - ) - self.assertEqual(collist, columns) + self.assertEqual(self.columns, columns) datalist = ( security_group_rule_id, 'icmp', diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py index 72fdbafc..f6b62291 100644 --- a/openstackclient/tests/compute/v2/test_server.py +++ b/openstackclient/tests/compute/v2/test_server.py @@ -89,6 +89,26 @@ class TestServer(compute_fakes.TestComputev2): class TestServerCreate(TestServer): + columns = ( + 'addresses', + 'flavor', + 'id', + 'name', + 'networks', + 'properties', + ) + + def datalist(self): + datalist = ( + '', + self.flavor.name + ' ()', + self.new_server.id, + self.new_server.name, + self.new_server.networks, + '', + ) + return datalist + def setUp(self): super(TestServerCreate, self).setUp() @@ -171,24 +191,8 @@ class TestServerCreate(TestServer): **kwargs ) - collist = ( - 'addresses', - 'flavor', - 'id', - 'name', - 'networks', - 'properties', - ) - self.assertEqual(collist, columns) - datalist = ( - '', - self.flavor.name + ' ()', - self.new_server.id, - self.new_server.name, - self.new_server.networks, - '', - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) def test_server_create_with_network(self): arglist = [ @@ -272,24 +276,8 @@ class TestServerCreate(TestServer): **kwargs ) - collist = ( - 'addresses', - 'flavor', - 'id', - 'name', - 'networks', - 'properties', - ) - self.assertEqual(collist, columns) - datalist = ( - '', - self.flavor.name + ' ()', - self.new_server.id, - self.new_server.name, - self.new_server.networks, - '', - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) @mock.patch('openstackclient.compute.v2.server.io.open') def test_server_create_userdata(self, mock_open): @@ -345,24 +333,8 @@ class TestServerCreate(TestServer): **kwargs ) - collist = ( - 'addresses', - 'flavor', - 'id', - 'name', - 'networks', - 'properties', - ) - self.assertEqual(collist, columns) - datalist = ( - '', - self.flavor.name + ' ()', - self.new_server.id, - self.new_server.name, - self.new_server.networks, - '', - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping(self): arglist = [ @@ -414,24 +386,8 @@ class TestServerCreate(TestServer): **kwargs ) - collist = ( - 'addresses', - 'flavor', - 'id', - 'name', - 'networks', - 'properties', - ) - self.assertEqual(collist, columns) - datalist = ( - '', - self.flavor.name + ' ()', - self.new_server.id, - self.new_server.name, - self.new_server.networks, - '', - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) class TestServerDelete(TestServer): @@ -536,6 +492,26 @@ class TestServerDelete(TestServer): class TestServerImageCreate(TestServer): + columns = ( + 'id', + 'name', + 'owner', + 'protected', + 'tags', + 'visibility', + ) + + def datalist(self): + datalist = ( + self.image.id, + self.image.name, + self.image.owner, + self.image.protected, + self.image.tags, + self.image.visibility, + ) + return datalist + def setUp(self): super(TestServerImageCreate, self).setUp() @@ -569,17 +545,8 @@ class TestServerImageCreate(TestServer): self.server.name, ) - collist = ('id', 'name', 'owner', 'protected', 'tags', 'visibility') - self.assertEqual(collist, columns) - datalist = ( - self.image.id, - self.image.name, - self.image.owner, - self.image.protected, - self.image.tags, - self.image.visibility, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) def test_server_image_create_name(self): arglist = [ @@ -601,17 +568,8 @@ class TestServerImageCreate(TestServer): 'img-nam', ) - collist = ('id', 'name', 'owner', 'protected', 'tags', 'visibility') - self.assertEqual(collist, columns) - datalist = ( - self.image.id, - self.image.name, - self.image.owner, - self.image.protected, - self.image.tags, - self.image.visibility, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) class TestServerList(TestServer): diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py index 4d964bdb..60b7f309 100644 --- a/openstackclient/tests/image/v1/test_image.py +++ b/openstackclient/tests/image/v1/test_image.py @@ -103,6 +103,7 @@ class TestImageCreate(TestImage): '--min-ram', '4', '--protected', '--private', + '--project', 'q', image_fakes.image_name, ] verifylist = [ @@ -114,6 +115,7 @@ class TestImageCreate(TestImage): ('unprotected', False), ('public', False), ('private', True), + ('project', 'q'), ('name', image_fakes.image_name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -130,6 +132,7 @@ class TestImageCreate(TestImage): min_ram=4, protected=True, is_public=False, + owner='q', data=mock.ANY, ) @@ -358,7 +361,7 @@ class TestImageList(TestImage): 'Status', 'Visibility', 'Protected', - 'Owner', + 'Project', 'Properties', ) @@ -484,22 +487,22 @@ class TestImageSet(TestImage): def test_image_set_options(self): arglist = [ '--name', 'new-name', - '--owner', 'new-owner', '--min-disk', '2', '--min-ram', '4', '--container-format', 'ovf', '--disk-format', 'vmdk', '--size', '35165824', + '--project', 'new-owner', image_fakes.image_name, ] verifylist = [ ('name', 'new-name'), - ('owner', 'new-owner'), ('min_disk', 2), ('min_ram', 4), ('container_format', 'ovf'), ('disk_format', 'vmdk'), ('size', 35165824), + ('project', 'new-owner'), ('image', image_fakes.image_name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py index 02182413..3534a3a4 100644 --- a/openstackclient/tests/image/v2/test_image.py +++ b/openstackclient/tests/image/v2/test_image.py @@ -131,11 +131,11 @@ class TestImageCreate(TestImage): '--disk-format', 'fs', '--min-disk', '10', '--min-ram', '4', - '--owner', self.new_image.owner, ('--protected' if self.new_image.protected else '--unprotected'), ('--private' if self.new_image.visibility == 'private' else '--public'), + '--project', self.new_image.owner, '--project-domain', identity_fakes.domain_id, self.new_image.name, ] @@ -144,11 +144,11 @@ class TestImageCreate(TestImage): ('disk_format', 'fs'), ('min_disk', 10), ('min_ram', 4), - ('owner', self.new_image.owner), ('protected', self.new_image.protected), ('unprotected', not self.new_image.protected), ('public', self.new_image.visibility == 'public'), ('private', self.new_image.visibility == 'private'), + ('project', self.new_image.owner), ('project_domain', identity_fakes.domain_id), ('name', self.new_image.name), ] @@ -217,6 +217,40 @@ class TestImageCreate(TestImage): parsed_args, ) + def test_image_create_with_unexist_project(self): + self.project_mock.get.side_effect = exceptions.NotFound(None) + self.project_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--container-format', 'ovf', + '--disk-format', 'fs', + '--min-disk', '10', + '--min-ram', '4', + '--protected', + '--private', + '--project', 'unexist_owner', + image_fakes.image_name, + ] + verifylist = [ + ('container_format', 'ovf'), + ('disk_format', 'fs'), + ('min_disk', 10), + ('min_ram', 4), + ('protected', True), + ('unprotected', False), + ('public', False), + ('private', True), + ('project', 'unexist_owner'), + ('name', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + @mock.patch('glanceclient.common.utils.get_data_file', name='Open') def test_image_create_file(self, mock_open): mock_file = mock.MagicMock(name='File') @@ -575,7 +609,7 @@ class TestImageList(TestImage): 'Status', 'Visibility', 'Protected', - 'Owner', + 'Project', 'Tags', ) @@ -755,21 +789,21 @@ class TestImageSet(TestImage): def test_image_set_options(self): arglist = [ '--name', 'new-name', - '--owner', identity_fakes.project_name, '--min-disk', '2', '--min-ram', '4', '--container-format', 'ovf', '--disk-format', 'vmdk', + '--project', identity_fakes.project_name, '--project-domain', identity_fakes.domain_id, image_fakes.image_id, ] verifylist = [ ('name', 'new-name'), - ('owner', identity_fakes.project_name), ('min_disk', 2), ('min_ram', 4), ('container_format', 'ovf'), ('disk_format', 'vmdk'), + ('project', identity_fakes.project_name), ('project_domain', identity_fakes.domain_id), ('image', image_fakes.image_id), ] @@ -809,6 +843,25 @@ class TestImageSet(TestImage): exceptions.CommandError, self.cmd.take_action, parsed_args) + def test_image_set_with_unexist_project(self): + self.project_mock.get.side_effect = exceptions.NotFound(None) + self.project_mock.find.side_effect = exceptions.NotFound(None) + + arglist = [ + '--project', 'unexist_owner', + image_fakes.image_id, + ] + verifylist = [ + ('project', 'unexist_owner'), + ('image', image_fakes.image_id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, parsed_args) + def test_image_set_bools1(self): arglist = [ '--protected', diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py index 80760a77..de885c62 100644 --- a/openstackclient/tests/network/v2/fakes.py +++ b/openstackclient/tests/network/v2/fakes.py @@ -145,6 +145,83 @@ class FakeNetwork(object): return mock.MagicMock(side_effect=networks) +class FakePort(object): + """Fake one or more ports.""" + + @staticmethod + def create_one_port(attrs={}, methods={}): + """Create a fake port. + + :param Dictionary attrs: + A dictionary with all attributes + :param Dictionary methods: + A dictionary with all methods + :return: + A FakeResource object, with id, name, admin_state_up, + status, tenant_id + """ + # Set default attributes. + port_attrs = { + 'id': 'port-id-' + uuid.uuid4().hex, + 'name': 'port-name-' + uuid.uuid4().hex, + 'status': 'ACTIVE', + 'admin_state_up': True, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + port_attrs.update(attrs) + + # Set default methods. + port_methods = {} + + # Overwrite default methods. + port_methods.update(methods) + + port = fakes.FakeResource(info=copy.deepcopy(port_attrs), + methods=copy.deepcopy(port_methods), + loaded=True) + return port + + @staticmethod + def create_ports(attrs={}, methods={}, count=2): + """Create multiple fake ports. + + :param Dictionary attrs: + A dictionary with all attributes + :param Dictionary methods: + A dictionary with all methods + :param int count: + The number of ports to fake + :return: + A list of FakeResource objects faking the ports + """ + ports = [] + for i in range(0, count): + ports.append(FakePort.create_one_port(attrs, methods)) + + return ports + + @staticmethod + def get_ports(ports=None, count=2): + """Get an iterable MagicMock object with a list of faked ports. + + If ports list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List ports: + A list of FakeResource objects faking ports + :param int count: + The number of ports to fake + :return: + An iterable Mock object with side_effect set to a list of faked + ports + """ + if ports is None: + ports = FakePort.create_ports(count) + return mock.MagicMock(side_effect=ports) + + class FakeRouter(object): """Fake one or more routers.""" diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py new file mode 100644 index 00000000..a1ddefa1 --- /dev/null +++ b/openstackclient/tests/network/v2/test_port.py @@ -0,0 +1,53 @@ +# 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. +# + +import mock + +from openstackclient.network.v2 import port +from openstackclient.tests.network.v2 import fakes as network_fakes + + +class TestPort(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestPort, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestDeletePort(TestPort): + + # The port to delete. + _port = network_fakes.FakePort.create_one_port() + + def setUp(self): + super(TestDeletePort, self).setUp() + + self.network.delete_port = mock.Mock(return_value=None) + self.network.find_port = mock.Mock(return_value=self._port) + # Get the command object to test + self.cmd = port.DeletePort(self.app, self.namespace) + + def test_delete(self): + arglist = [ + self._port.name, + ] + verifylist = [ + ('port', [self._port.name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.delete_port.assert_called_with(self._port) + self.assertIsNone(result) |
