summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/api.py2
-rw-r--r--openstackclient/api/utils.py2
-rw-r--r--openstackclient/common/clientmanager.py10
-rw-r--r--openstackclient/common/commandmanager.py2
-rw-r--r--openstackclient/common/exceptions.py25
-rw-r--r--openstackclient/common/limits.py4
-rw-r--r--openstackclient/common/module.py20
-rw-r--r--openstackclient/common/utils.py27
-rw-r--r--openstackclient/compute/v2/aggregate.py31
-rw-r--r--openstackclient/compute/v2/flavor.py6
-rw-r--r--openstackclient/compute/v2/floatingip.py23
-rw-r--r--openstackclient/compute/v2/host.py57
-rw-r--r--openstackclient/compute/v2/keypair.py3
-rw-r--r--openstackclient/compute/v2/security_group.py151
-rw-r--r--openstackclient/compute/v2/server.py4
-rw-r--r--openstackclient/compute/v2/service.py65
-rw-r--r--openstackclient/identity/v3/token.py4
-rw-r--r--openstackclient/image/v2/image.py5
-rw-r--r--openstackclient/network/utils.py41
-rw-r--r--openstackclient/network/v2/floating_ip.py85
-rw-r--r--openstackclient/network/v2/network.py91
-rw-r--r--openstackclient/network/v2/port.py198
-rw-r--r--openstackclient/network/v2/router.py29
-rw-r--r--openstackclient/network/v2/security_group.py179
-rw-r--r--openstackclient/network/v2/security_group_rule.py141
-rw-r--r--openstackclient/network/v2/subnet.py289
-rw-r--r--openstackclient/network/v2/subnet_pool.py136
-rw-r--r--openstackclient/shell.py14
-rw-r--r--openstackclient/tests/common/test_clientmanager.py17
-rw-r--r--openstackclient/tests/common/test_extension.py157
-rw-r--r--openstackclient/tests/common/test_module.py16
-rw-r--r--openstackclient/tests/common/test_parseractions.py12
-rw-r--r--openstackclient/tests/common/test_utils.py12
-rw-r--r--openstackclient/tests/compute/v2/fakes.py164
-rw-r--r--openstackclient/tests/compute/v2/test_aggregate.py392
-rw-r--r--openstackclient/tests/compute/v2/test_host.py75
-rw-r--r--openstackclient/tests/compute/v2/test_hypervisor_stats.py2
-rw-r--r--openstackclient/tests/compute/v2/test_security_group.py127
-rw-r--r--openstackclient/tests/compute/v2/test_security_group_rule.py266
-rw-r--r--openstackclient/tests/compute/v2/test_service.py115
-rw-r--r--openstackclient/tests/identity/v2_0/test_endpoint.py3
-rw-r--r--openstackclient/tests/identity/v2_0/test_project.py4
-rw-r--r--openstackclient/tests/identity/v2_0/test_role.py6
-rw-r--r--openstackclient/tests/identity/v2_0/test_service.py3
-rw-r--r--openstackclient/tests/identity/v2_0/test_token.py3
-rw-r--r--openstackclient/tests/identity/v2_0/test_user.py24
-rw-r--r--openstackclient/tests/identity/v3/test_consumer.py78
-rw-r--r--openstackclient/tests/identity/v3/test_credential.py12
-rw-r--r--openstackclient/tests/identity/v3/test_identity_provider.py85
-rw-r--r--openstackclient/tests/identity/v3/test_mappings.py77
-rw-r--r--openstackclient/tests/identity/v3/test_oauth.py80
-rw-r--r--openstackclient/tests/identity/v3/test_protocol.py5
-rw-r--r--openstackclient/tests/identity/v3/test_role.py18
-rw-r--r--openstackclient/tests/identity/v3/test_service_provider.py87
-rw-r--r--openstackclient/tests/identity/v3/test_token.py3
-rw-r--r--openstackclient/tests/identity/v3/test_trust.py3
-rw-r--r--openstackclient/tests/identity/v3/test_unscoped_saml.py44
-rw-r--r--openstackclient/tests/identity/v3/test_user.py36
-rw-r--r--openstackclient/tests/network/v2/fakes.py52
-rw-r--r--openstackclient/tests/network/v2/test_floating_ip.py178
-rw-r--r--openstackclient/tests/network/v2/test_network.py84
-rw-r--r--openstackclient/tests/network/v2/test_port.py132
-rw-r--r--openstackclient/tests/network/v2/test_router.py38
-rw-r--r--openstackclient/tests/network/v2/test_security_group.py319
-rw-r--r--openstackclient/tests/network/v2/test_security_group_rule.py319
-rw-r--r--openstackclient/tests/network/v2/test_subnet.py414
-rw-r--r--openstackclient/tests/network/v2/test_subnet_pool.py268
-rw-r--r--openstackclient/tests/test_shell.py82
-rw-r--r--openstackclient/tests/utils.py24
-rw-r--r--openstackclient/tests/volume/v2/fakes.py20
-rw-r--r--openstackclient/tests/volume/v2/test_backup.py24
-rw-r--r--openstackclient/tests/volume/v2/test_snapshot.py24
-rw-r--r--openstackclient/tests/volume/v2/test_volume.py83
-rw-r--r--openstackclient/volume/v1/backup.py1
-rw-r--r--openstackclient/volume/v1/snapshot.py1
-rw-r--r--openstackclient/volume/v1/volume.py46
-rw-r--r--openstackclient/volume/v1/volume_type.py50
-rw-r--r--openstackclient/volume/v2/backup.py1
-rw-r--r--openstackclient/volume/v2/snapshot.py1
-rw-r--r--openstackclient/volume/v2/volume.py95
-rw-r--r--openstackclient/volume/v2/volume_type.py10
81 files changed, 4467 insertions, 1369 deletions
diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py
index 6a88e7f7..bd2abd88 100644
--- a/openstackclient/api/api.py
+++ b/openstackclient/api/api.py
@@ -331,7 +331,7 @@ class BaseAPI(KeystoneSession):
:param string path:
The API-specific portion of the URL path
- :param string search:
+ :param string value:
search expression
:param string attr:
name of attribute for secondary search
diff --git a/openstackclient/api/utils.py b/openstackclient/api/utils.py
index ab0e23c2..6407cd44 100644
--- a/openstackclient/api/utils.py
+++ b/openstackclient/api/utils.py
@@ -29,7 +29,7 @@ def simple_filter(
The name of the attribute to filter. If attr does not exist no
match will succeed and no rows will be returned. If attr is
None no filtering will be performed and all rows will be returned.
- :param sring value:
+ :param string value:
The value to filter. None is considered to be a 'no filter' value.
'' matches against a Python empty string.
:param string property_field:
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 56ddcbad..6d23b55e 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -110,6 +110,15 @@ class ClientManager(object):
self._cacert = verify
self._insecure = False
+ # Set up client certificate and key
+ # NOTE(cbrandily): This converts client certificate/key to requests
+ # cert argument: None (no client certificate), a path
+ # to client certificate or a tuple with client
+ # certificate/key paths.
+ self._cert = self._cli_options.cert
+ if self._cert and self._cli_options.key:
+ self._cert = self._cert, self._cli_options.key
+
# Get logging from root logger
root_logger = logging.getLogger('')
LOG.setLevel(root_logger.getEffectiveLevel())
@@ -194,6 +203,7 @@ class ClientManager(object):
auth=self.auth,
session=request_session,
verify=self._verify,
+ cert=self._cert,
user_agent=USER_AGENT,
)
diff --git a/openstackclient/common/commandmanager.py b/openstackclient/common/commandmanager.py
index b809d63a..c190e33e 100644
--- a/openstackclient/common/commandmanager.py
+++ b/openstackclient/common/commandmanager.py
@@ -56,4 +56,4 @@ class CommandManager(cliff.commandmanager.CommandManager):
)
group_list.append(cmd_name)
return group_list
- return self.commands.keys()
+ return list(self.commands.keys())
diff --git a/openstackclient/common/exceptions.py b/openstackclient/common/exceptions.py
index 5f5f5ab1..5f81e6a6 100644
--- a/openstackclient/common/exceptions.py
+++ b/openstackclient/common/exceptions.py
@@ -108,28 +108,3 @@ _code_map = dict((c.http_status, c) for c in [
OverLimit,
HTTPNotImplemented
])
-
-
-def from_response(response, body):
- """Return an instance of a ClientException based on an httplib2 response.
-
- Usage::
-
- resp, body = http.request(...)
- if resp.status != 200:
- raise exception_from_response(resp, body)
- """
- cls = _code_map.get(response.status, ClientException)
- if body:
- if hasattr(body, 'keys'):
- error = body[body.keys()[0]]
- message = error.get('message')
- details = error.get('details')
- else:
- # If we didn't get back a properly formed error message we
- # probably couldn't communicate with Keystone at all.
- message = "Unable to communicate with image service: %s." % body
- details = None
- return cls(code=response.status, message=message, details=details)
- else:
- return cls(code=response.status)
diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py
index bd546c01..1f87abf3 100644
--- a/openstackclient/common/limits.py
+++ b/openstackclient/common/limits.py
@@ -55,8 +55,8 @@ class ShowLimits(command.Lister):
parser.add_argument(
'--domain',
metavar='<domain>',
- help='Domain that owns --project (name or ID)'
- ' [only valid with --absolute]',
+ help='Domain the project belongs to (name or ID)'
+ ' [only valid with --absolute]',
)
return parser
diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py
index a3dea5da..30c67c68 100644
--- a/openstackclient/common/module.py
+++ b/openstackclient/common/module.py
@@ -19,6 +19,7 @@ import six
import sys
from openstackclient.common import command
+from openstackclient.common import utils
class ListCommand(command.Lister):
@@ -29,9 +30,24 @@ class ListCommand(command.Lister):
def take_action(self, parsed_args):
cm = self.app.command_manager
groups = cm.get_command_groups()
-
+ groups = sorted(groups)
columns = ('Command Group', 'Commands')
- return (columns, ((c, cm.get_command_names(group=c)) for c in groups))
+
+ commands = []
+ for group in groups:
+ command_names = cm.get_command_names(group)
+ command_names = sorted(command_names)
+
+ if command_names != []:
+
+ # TODO(bapalm): Fix this when cliff properly supports
+ # handling the detection rather than using the hard-code below.
+ if parsed_args.formatter == 'table':
+ command_names = utils.format_list(command_names, "\n")
+
+ commands.append((group, command_names))
+
+ return (columns, commands)
class ListModule(command.ShowOne):
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index 840da402..daa65c25 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -100,22 +100,15 @@ def find_resource(manager, name_or_id, **kwargs):
else:
pass
- try:
- for resource in manager.list():
- # short circuit and return the first match
- if (resource.get('id') == name_or_id or
- resource.get('name') == name_or_id):
- return resource
- else:
- # we found no match, keep going to bomb out
- pass
- except Exception:
- # in case the list fails for some reason
- pass
-
- # if we hit here, we've failed, report back this error:
- msg = "Could not find resource %s" % name_or_id
- raise exceptions.CommandError(msg)
+ for resource in manager.list():
+ # short circuit and return the first match
+ if (resource.get('id') == name_or_id or
+ resource.get('name') == name_or_id):
+ return resource
+ else:
+ # we found no match, report back this error:
+ msg = "Could not find resource %s" % name_or_id
+ raise exceptions.CommandError(msg)
def format_dict(data):
@@ -281,7 +274,7 @@ def get_client_class(api_name, version, version_map):
client_path = version_map[str(version)]
except (KeyError, ValueError):
msg = "Invalid %s client version '%s'. must be one of: %s" % (
- (api_name, version, ', '.join(version_map.keys())))
+ (api_name, version, ', '.join(list(version_map.keys()))))
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)
diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py
index e47c13a7..1a02a388 100644
--- a/openstackclient/compute/v2/aggregate.py
+++ b/openstackclient/compute/v2/aggregate.py
@@ -290,3 +290,34 @@ class ShowAggregate(command.ShowOne):
info = {}
info.update(data._info)
return zip(*sorted(six.iteritems(info)))
+
+
+class UnsetAggregate(command.Command):
+ """Unset aggregate properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetAggregate, self).get_parser(prog_name)
+ parser.add_argument(
+ "aggregate",
+ metavar="<aggregate>",
+ help="Aggregate to modify (name or ID)",
+ )
+ parser.add_argument(
+ "--property",
+ metavar="<key>",
+ action='append',
+ help='Property to remove from aggregate '
+ '(repeat option to remove multiple properties)',
+ required=True,
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ aggregate = utils.find_resource(
+ compute_client.aggregates,
+ parsed_args.aggregate)
+
+ unset_property = {key: None for key in parsed_args.property}
+ compute_client.aggregates.set_metadata(aggregate,
+ unset_property)
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index b5a7c60c..29e0e9d4 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -76,10 +76,10 @@ class CreateFlavor(command.ShowOne):
)
parser.add_argument(
"--rxtx-factor",
- type=int,
+ type=float,
metavar="<factor>",
- default=1,
- help="RX/TX factor (default 1)",
+ default=1.0,
+ help="RX/TX factor (default 1.0)",
)
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py
index 6212989f..fac4d2e3 100644
--- a/openstackclient/compute/v2/floatingip.py
+++ b/openstackclient/compute/v2/floatingip.py
@@ -15,8 +15,6 @@
"""Floating IP action implementations"""
-import six
-
from openstackclient.common import command
from openstackclient.common import utils
@@ -47,27 +45,6 @@ class AddFloatingIP(command.Command):
server.add_floating_ip(parsed_args.ip_address)
-class CreateFloatingIP(command.ShowOne):
- """Create new floating IP address"""
-
- def get_parser(self, prog_name):
- parser = super(CreateFloatingIP, self).get_parser(prog_name)
- parser.add_argument(
- 'pool',
- metavar='<pool>',
- help='Pool to fetch IP address from (name or ID)',
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- floating_ip = compute_client.floating_ips.create(parsed_args.pool)
-
- info = {}
- info.update(floating_ip._info)
- return zip(*sorted(six.iteritems(info)))
-
-
class RemoveFloatingIP(command.Command):
"""Remove floating IP address from server"""
diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py
index f2257d12..5af25310 100644
--- a/openstackclient/compute/v2/host.py
+++ b/openstackclient/compute/v2/host.py
@@ -44,6 +44,63 @@ class ListHost(command.Lister):
) for s in data))
+class SetHost(command.Command):
+ """Set host properties"""
+ def get_parser(self, prog_name):
+ parser = super(SetHost, self).get_parser(prog_name)
+ parser.add_argument(
+ "host",
+ metavar="<host>",
+ help="The host to modify (name or ID)"
+ )
+ status = parser.add_mutually_exclusive_group()
+ status.add_argument(
+ '--enable',
+ action='store_true',
+ help='Enable the host'
+ )
+ status.add_argument(
+ '--disable',
+ action='store_true',
+ help='Disable the host'
+ )
+ maintenance = parser.add_mutually_exclusive_group()
+ maintenance.add_argument(
+ '--enable-maintenance',
+ action='store_true',
+ help='Enable maintenance mode for the host'
+ )
+ maintenance.add_argument(
+ '--disable-maintenance',
+ action='store_true',
+ help='Disable maintenance mode for the host',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ kwargs = {}
+
+ if parsed_args.enable:
+ kwargs['status'] = True
+ if parsed_args.disable:
+ kwargs['status'] = False
+ if parsed_args.enable_maintenance:
+ kwargs['maintenance_mode'] = True
+ if parsed_args.disable_maintenance:
+ kwargs['maintenance_mode'] = False
+
+ compute_client = self.app.client_manager.compute
+ foundhost = utils.find_resource(
+ compute_client.hosts,
+ parsed_args.host
+ )
+
+ compute_client.hosts.update(
+ foundhost.id,
+ kwargs
+ )
+
+
class ShowHost(command.Lister):
"""Show host command"""
diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py
index 22d918a4..1db0f942 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -48,8 +48,7 @@ class CreateKeypair(command.ShowOne):
public_key = parsed_args.public_key
if public_key:
try:
- with io.open(os.path.expanduser(parsed_args.public_key),
- "rb") as p:
+ with io.open(os.path.expanduser(parsed_args.public_key)) as p:
public_key = p.read()
except IOError as e:
msg = "Key file %s not found: %s"
diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py
index f378af14..ca3bf5dc 100644
--- a/openstackclient/compute/v2/security_group.py
+++ b/openstackclient/compute/v2/security_group.py
@@ -16,15 +16,12 @@
"""Compute v2 Security Group action implementations"""
-import six
-
try:
from novaclient.v2 import security_group_rules
except ImportError:
from novaclient.v1_1 import security_group_rules
from openstackclient.common import command
-from openstackclient.common import parseractions
from openstackclient.common import utils
@@ -56,117 +53,6 @@ def _xform_security_group_rule(sgroup):
return info
-def _xform_and_trim_security_group_rule(sgroup):
- info = _xform_security_group_rule(sgroup)
- # Trim parent security group ID since caller has this information.
- info.pop('parent_group_id', None)
- # Trim keys with empty string values.
- keys_to_trim = [
- 'ip_protocol',
- 'ip_range',
- 'port_range',
- 'remote_security_group',
- ]
- for key in keys_to_trim:
- if key in info and not info[key]:
- info.pop(key)
- return info
-
-
-class CreateSecurityGroup(command.ShowOne):
- """Create a new security group"""
-
- def get_parser(self, prog_name):
- parser = super(CreateSecurityGroup, self).get_parser(prog_name)
- parser.add_argument(
- "name",
- metavar="<name>",
- help="New security group name",
- )
- parser.add_argument(
- "--description",
- metavar="<description>",
- help="Security group description",
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
-
- description = parsed_args.description or parsed_args.name
-
- data = compute_client.security_groups.create(
- parsed_args.name,
- description,
- )
-
- info = {}
- info.update(data._info)
- return zip(*sorted(six.iteritems(info)))
-
-
-class CreateSecurityGroupRule(command.ShowOne):
- """Create a new security group rule"""
-
- def get_parser(self, prog_name):
- parser = super(CreateSecurityGroupRule, self).get_parser(prog_name)
- parser.add_argument(
- 'group',
- metavar='<group>',
- help='Create rule in this security group (name or ID)',
- )
- parser.add_argument(
- "--proto",
- metavar="<proto>",
- default="tcp",
- help="IP protocol (icmp, tcp, udp; default: tcp)",
- )
- source_group = parser.add_mutually_exclusive_group()
- source_group.add_argument(
- "--src-ip",
- metavar="<ip-address>",
- default="0.0.0.0/0",
- help="Source IP address block (may use CIDR notation; default: "
- "0.0.0.0/0)",
- )
- source_group.add_argument(
- "--src-group",
- metavar="<group>",
- help="Source security group (ID only)",
- )
- parser.add_argument(
- "--dst-port",
- metavar="<port-range>",
- default=(0, 0),
- action=parseractions.RangeAction,
- help="Destination port, may be a range: 137:139 (default: 0; "
- "only required for proto tcp and udp)",
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- group = utils.find_resource(
- compute_client.security_groups,
- parsed_args.group,
- )
- if parsed_args.proto.lower() == 'icmp':
- from_port, to_port = -1, -1
- else:
- from_port, to_port = parsed_args.dst_port
- data = compute_client.security_group_rules.create(
- group.id,
- parsed_args.proto,
- from_port,
- to_port,
- parsed_args.src_ip,
- parsed_args.src_group,
- )
-
- info = _xform_security_group_rule(data._info)
- return zip(*sorted(six.iteritems(info)))
-
-
class ListSecurityGroupRule(command.Lister):
"""List security group rules"""
@@ -215,40 +101,3 @@ class ListSecurityGroupRule(command.Lister):
(utils.get_item_properties(
s, columns,
) for s in rules))
-
-
-class ShowSecurityGroup(command.ShowOne):
- """Display security group details"""
-
- def get_parser(self, prog_name):
- parser = super(ShowSecurityGroup, self).get_parser(prog_name)
- parser.add_argument(
- 'group',
- metavar='<group>',
- help='Security group to display (name or ID)',
- )
- return parser
-
- def take_action(self, parsed_args):
-
- compute_client = self.app.client_manager.compute
- info = {}
- info.update(utils.find_resource(
- compute_client.security_groups,
- parsed_args.group,
- )._info)
- rules = []
- for r in info['rules']:
- formatted_rule = _xform_and_trim_security_group_rule(r)
- rules.append(utils.format_dict(formatted_rule))
-
- # Format rules into a list of strings
- info.update(
- {'rules': utils.format_list(rules, separator='\n')}
- )
- # Map 'tenant_id' column to 'project_id'
- info.update(
- {'project_id': info.pop('tenant_id')}
- )
-
- return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 7376eabb..d3b601b0 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -1445,7 +1445,7 @@ class ShowServer(command.ShowOne):
class SshServer(command.Command):
- """Ssh to server"""
+ """SSH to server"""
def get_parser(self, prog_name):
parser = super(SshServer, self).get_parser(prog_name)
@@ -1738,7 +1738,7 @@ class UnsetServer(command.Command):
action='append',
default=[],
help=_('Property key to remove from server '
- '(repeat to unset multiple values)'),
+ '(repeat to remove multiple values)'),
)
return parser
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index 3c062724..2b51af3d 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -17,6 +17,7 @@
from openstackclient.common import command
from openstackclient.common import utils
+from openstackclient.i18n import _ # noqa
class DeleteService(command.Command):
@@ -44,24 +45,42 @@ class ListService(command.Lister):
parser.add_argument(
"--host",
metavar="<host>",
- help="Name of host")
+ help="List services on specified host (name only)")
parser.add_argument(
"--service",
metavar="<service>",
- help="Name of service")
+ help="List only specified service (name only)")
+ parser.add_argument(
+ "--long",
+ action="store_true",
+ default=False,
+ help="List additional fields in output"
+ )
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- columns = (
- "Id",
- "Binary",
- "Host",
- "Zone",
- "Status",
- "State",
- "Updated At"
- )
+ if parsed_args.long:
+ columns = (
+ "Id",
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At",
+ "Disabled Reason"
+ )
+ else:
+ columns = (
+ "Id",
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At"
+ )
data = compute_client.services.list(parsed_args.host,
parsed_args.service)
return (columns,
@@ -95,14 +114,28 @@ class SetService(command.Command):
dest="enabled",
help="Disable a service",
action="store_false")
+ parser.add_argument(
+ "--disable-reason",
+ default=None,
+ metavar="<reason>",
+ help="Reason for disabling the service (in quotas). Note that "
+ "when the service is enabled, this option is ignored.")
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
-
- if parsed_args.enabled:
- action = compute_client.services.enable
+ cs = compute_client.services
+
+ if not parsed_args.enabled:
+ if parsed_args.disable_reason:
+ cs.disable_log_reason(parsed_args.host,
+ parsed_args.service,
+ parsed_args.disable_reason)
+ else:
+ cs.disable(parsed_args.host, parsed_args.service)
else:
- action = compute_client.services.disable
+ if parsed_args.disable_reason:
+ msg = _("argument --disable-reason has been ignored")
+ self.log.info(msg)
- action(parsed_args.host, parsed_args.service)
+ cs.enable(parsed_args.host, parsed_args.service)
diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py
index bf039d2f..62a4c4a3 100644
--- a/openstackclient/identity/v3/token.py
+++ b/openstackclient/identity/v3/token.py
@@ -18,6 +18,7 @@
import six
from openstackclient.common import command
+from openstackclient.common import exceptions
from openstackclient.common import utils
from openstackclient.identity import common
@@ -172,6 +173,9 @@ class IssueToken(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ if not self.app.client_manager.auth_ref:
+ raise exceptions.AuthorizationFailure(
+ "Only an authorized user may issue a new token.")
token = self.app.client_manager.auth_ref.service_catalog.get_token()
if 'tenant_id' in token:
token['project_id'] = token.pop('tenant_id')
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 3f162181..40ddd4b9 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -325,7 +325,10 @@ class CreateImage(command.ShowOne):
parsed_args.disk_format,
)
info = body['os-volume_upload_image']
- info['volume_type'] = info['volume_type']['name']
+ try:
+ info['volume_type'] = info['volume_type']['name']
+ except TypeError:
+ info['volume_type'] = None
else:
image = image_client.images.create(**kwargs)
diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py
new file mode 100644
index 00000000..287f0271
--- /dev/null
+++ b/openstackclient/network/utils.py
@@ -0,0 +1,41 @@
+# 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.
+#
+
+
+# Transform compute security group rule for display.
+def transform_compute_security_group_rule(sg_rule):
+ info = {}
+ info.update(sg_rule)
+ from_port = info.pop('from_port')
+ to_port = info.pop('to_port')
+ if isinstance(from_port, int) and isinstance(to_port, int):
+ port_range = {'port_range': "%u:%u" % (from_port, to_port)}
+ elif from_port is None and to_port is None:
+ port_range = {'port_range': ""}
+ else:
+ port_range = {'port_range': "%s:%s" % (from_port, to_port)}
+ info.update(port_range)
+ if 'cidr' in info['ip_range']:
+ info['ip_range'] = info['ip_range']['cidr']
+ else:
+ info['ip_range'] = ''
+ if info['ip_protocol'] is None:
+ info['ip_protocol'] = ''
+ elif info['ip_protocol'].lower() == 'icmp':
+ info['port_range'] = ''
+ group = info.pop('group')
+ if 'name' in group:
+ info['remote_security_group'] = group['name']
+ else:
+ info['remote_security_group'] = ''
+ return info
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index e0a65a48..b21d6e96 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -18,13 +18,96 @@ from openstackclient.network import common
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
return tuple(sorted(columns))
+def _get_attrs(client_manager, parsed_args):
+ attrs = {}
+ network_client = client_manager.network
+
+ if parsed_args.network is not None:
+ network = network_client.find_network(parsed_args.network,
+ ignore_missing=False)
+ attrs['floating_network_id'] = network.id
+
+ if parsed_args.subnet is not None:
+ subnet = network_client.find_subnet(parsed_args.subnet,
+ ignore_missing=False)
+ attrs['subnet_id'] = subnet.id
+
+ if parsed_args.port is not None:
+ port = network_client.find_port(parsed_args.port,
+ ignore_missing=False)
+ attrs['port_id'] = port.id
+
+ if parsed_args.floating_ip_address is not None:
+ attrs['floating_ip_address'] = parsed_args.floating_ip_address
+
+ if parsed_args.fixed_ip_address is not None:
+ attrs['fixed_ip_address'] = parsed_args.fixed_ip_address
+
+ return attrs
+
+
+class CreateFloatingIP(common.NetworkAndComputeShowOne):
+ """Create floating IP"""
+
+ def update_parser_common(self, parser):
+ # In Compute v2 network, floating IPs could be allocated from floating
+ # IP pools, which are actually external networks. So deprecate the
+ # parameter "pool", and use "network" instead.
+ parser.add_argument(
+ 'network',
+ metavar='<network>',
+ help='Network to allocate floating IP from (name or ID)',
+ )
+ return parser
+
+ def update_parser_network(self, parser):
+ parser.add_argument(
+ '--subnet',
+ metavar='<subnet>',
+ help="Subnet on which you want to create the floating IP "
+ "(name or ID)"
+ )
+ parser.add_argument(
+ '--port',
+ metavar='<port>',
+ help="Port to be associated with the floating IP "
+ "(name or ID)"
+ )
+ parser.add_argument(
+ '--floating-ip-address',
+ metavar='<floating-ip-address>',
+ dest='floating_ip_address',
+ help="Floating IP address"
+ )
+ parser.add_argument(
+ '--fixed-ip-address',
+ metavar='<fixed-ip-address>',
+ dest='fixed_ip_address',
+ help="Fixed IP address mapped to the floating IP"
+ )
+ return parser
+
+ def take_action_network(self, client, parsed_args):
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ obj = client.create_ip(**attrs)
+ columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return (columns, data)
+
+ def take_action_compute(self, client, parsed_args):
+ obj = client.floating_ips.create(parsed_args.network)
+ columns = _get_columns(obj._info)
+ data = utils.get_dict_properties(obj._info, columns)
+ return (columns, data)
+
+
class DeleteFloatingIP(common.NetworkAndComputeCommand):
"""Delete floating IP"""
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 612a1775..ebd5cb63 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -38,7 +38,7 @@ _formatters = {
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
@@ -52,10 +52,14 @@ def _get_attrs(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
- if parsed_args.admin_state is not None:
- attrs['admin_state_up'] = parsed_args.admin_state
- if parsed_args.shared is not None:
- attrs['shared'] = parsed_args.shared
+ if parsed_args.enable:
+ attrs['admin_state_up'] = True
+ if parsed_args.disable:
+ attrs['admin_state_up'] = False
+ if parsed_args.share:
+ attrs['shared'] = True
+ if parsed_args.no_share:
+ attrs['shared'] = False
# "network set" command doesn't support setting project.
if 'project' in parsed_args and parsed_args.project is not None:
@@ -79,8 +83,10 @@ def _get_attrs_compute(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['label'] = str(parsed_args.name)
- if parsed_args.shared is not None:
- attrs['share_address'] = parsed_args.shared
+ if parsed_args.share:
+ attrs['share_address'] = True
+ if parsed_args.no_share:
+ attrs['share_address'] = False
if parsed_args.subnet is not None:
attrs['cidr'] = parsed_args.subnet
@@ -99,15 +105,13 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
share_group = parser.add_mutually_exclusive_group()
share_group.add_argument(
'--share',
- dest='shared',
action='store_true',
default=None,
help='Share the network between projects',
)
share_group.add_argument(
'--no-share',
- dest='shared',
- action='store_false',
+ action='store_true',
help='Do not share the network between projects',
)
return parser
@@ -116,15 +120,13 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=True,
help='Enable network (default)',
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
+ action='store_true',
help='Disable network',
)
parser.add_argument(
@@ -142,6 +144,47 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
'(requires the Network Availability Zone extension, '
'this option can be repeated).',
)
+ external_router_grp = parser.add_mutually_exclusive_group()
+ external_router_grp.add_argument(
+ '--external',
+ action='store_true',
+ help='Set this network as an external network. '
+ 'Requires the "external-net" extension to be enabled.')
+ external_router_grp.add_argument(
+ '--internal',
+ action='store_true',
+ help='Set this network as an internal network (default)')
+ default_router_grp = parser.add_mutually_exclusive_group()
+ default_router_grp.add_argument(
+ '--default',
+ action='store_true',
+ help='Specify if this network should be used as '
+ 'the default external network')
+ default_router_grp.add_argument(
+ '--no-default',
+ action='store_true',
+ help='Do not use the network as the default external network.'
+ 'By default, no network is set as an external network.')
+ parser.add_argument(
+ '--provider-network-type',
+ metavar='<provider-network-type>',
+ choices=['flat', 'gre', 'local',
+ 'vlan', 'vxlan'],
+ help='The physical mechanism by which the virtual network '
+ 'is implemented. The supported options are: '
+ 'flat, gre, local, vlan, vxlan')
+ parser.add_argument(
+ '--provider-physical-network',
+ metavar='<provider-physical-network>',
+ dest='physical_network',
+ help='Name of the physical network over which the virtual '
+ 'network is implemented')
+ parser.add_argument(
+ '--provider-segment',
+ metavar='<provider-segment>',
+ dest='segmentation_id',
+ help='VLAN ID for VLAN networks or Tunnel ID for GRE/VXLAN '
+ 'networks')
return parser
def update_parser_compute(self, parser):
@@ -154,6 +197,20 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
def take_action_network(self, client, parsed_args):
attrs = _get_attrs(self.app.client_manager, parsed_args)
+ if parsed_args.internal:
+ attrs['router:external'] = False
+ if parsed_args.external:
+ attrs['router:external'] = True
+ if parsed_args.no_default:
+ attrs['is_default'] = False
+ if parsed_args.default:
+ attrs['is_default'] = True
+ if parsed_args.provider_network_type:
+ attrs['provider:network_type'] = parsed_args.provider_network_type
+ if parsed_args.physical_network:
+ attrs['provider:physical_network'] = parsed_args.physical_network
+ if parsed_args.segmentation_id:
+ attrs['provider:segmentation_id'] = parsed_args.segmentation_id
obj = client.create_network(**attrs)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
@@ -301,29 +358,25 @@ class SetNetwork(command.Command):
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=None,
help='Enable network',
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
+ action='store_true',
help='Disable network',
)
share_group = parser.add_mutually_exclusive_group()
share_group.add_argument(
'--share',
- dest='shared',
action='store_true',
default=None,
help='Share the network between projects',
)
share_group.add_argument(
'--no-share',
- dest='shared',
- action='store_false',
+ action='store_true',
help='Do not share the network between projects',
)
return parser
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 449dcfd4..a9e80428 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -13,12 +13,20 @@
"""Port action implementations"""
+import argparse
+import logging
+
from openstackclient.common import command
+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 as identity_common
+LOG = logging.getLogger(__name__)
+
+
def _format_admin_state(state):
return 'UP' if state else 'DOWN'
@@ -35,7 +43,7 @@ _formatters = {
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
@@ -56,23 +64,42 @@ def _get_columns(item):
def _get_attrs(client_manager, parsed_args):
attrs = {}
- if parsed_args.name is not None:
- attrs['name'] = str(parsed_args.name)
+ # Handle deprecated options
+ # NOTE(dtroyer): --device-id and --host-id were deprecated in Mar 2016.
+ # Do not remove before 3.x release or Mar 2017.
+ if parsed_args.device_id:
+ attrs['device_id'] = parsed_args.device_id
+ LOG.warning(_(
+ 'The --device-id option is deprecated, '
+ 'please use --device instead.'
+ ))
+ if parsed_args.host_id:
+ attrs['binding:host_id'] = parsed_args.host_id
+ LOG.warning(_(
+ 'The --host-id option is deprecated, '
+ 'please use --host instead.'
+ ))
+
if parsed_args.fixed_ip is not None:
attrs['fixed_ips'] = parsed_args.fixed_ip
- if parsed_args.device_id is not None:
- attrs['device_id'] = parsed_args.device_id
+ if parsed_args.device:
+ attrs['device_id'] = parsed_args.device
if parsed_args.device_owner is not None:
attrs['device_owner'] = parsed_args.device_owner
- if parsed_args.admin_state is not None:
- attrs['admin_state_up'] = parsed_args.admin_state
+ if parsed_args.enable:
+ attrs['admin_state_up'] = True
+ if parsed_args.disable:
+ attrs['admin_state_up'] = False
if parsed_args.binding_profile is not None:
attrs['binding:profile'] = parsed_args.binding_profile
if parsed_args.vnic_type is not None:
attrs['binding:vnic_type'] = parsed_args.vnic_type
- if parsed_args.host_id is not None:
- attrs['binding:host_id'] = parsed_args.host_id
+ if parsed_args.host:
+ attrs['binding:host_id'] = parsed_args.host
+ # It is possible that name is not updated during 'port set'
+ if parsed_args.name is not None:
+ attrs['name'] = str(parsed_args.name)
# The remaining options do not support 'port set' command, so they require
# additional check
if 'mac_address' in parsed_args and parsed_args.mac_address is not None:
@@ -124,18 +151,19 @@ def _prepare_fixed_ips(client_manager, parsed_args):
def _add_updatable_args(parser):
- parser.add_argument(
- '--fixed-ip',
- metavar='subnet=<subnet>,ip-address=<ip-address>',
- action=parseractions.MultiKeyValueAction,
- optional_keys=['subnet', 'ip-address'],
- help='Desired IP and/or subnet (name or ID) for this port: '
- 'subnet=<subnet>,ip-address=<ip-address> '
- '(this option can be repeated)')
- parser.add_argument(
+ # NOTE(dtroyer): --device-id is deprecated in Mar 2016. Do not
+ # remove before 3.x release or Mar 2017.
+ device_group = parser.add_mutually_exclusive_group()
+ device_group.add_argument(
+ '--device',
+ metavar='<device-id>',
+ help='Port device ID',
+ )
+ device_group.add_argument(
'--device-id',
metavar='<device-id>',
- help='Device ID of this port')
+ help=argparse.SUPPRESS,
+ )
parser.add_argument(
'--device-owner',
metavar='<device-owner>',
@@ -145,18 +173,21 @@ def _add_updatable_args(parser):
metavar='<vnic-type>',
choices=['direct', 'direct-physical', 'macvtap',
'normal', 'baremetal'],
- help='VNIC type for this port (direct | direct-physical |'
- ' macvtap | normal(default) | baremetal)')
- parser.add_argument(
- '--binding-profile',
- metavar='<binding-profile>',
- action=parseractions.KeyValueAction,
- help='Custom data to be passed as binding:profile: <key>=<value> '
- '(this option can be repeated)')
- parser.add_argument(
+ help="VNIC type for this port (direct | direct-physical |"
+ " macvtap | normal | baremetal). If unspecified during"
+ " port creation, default value will be 'normal'.")
+ # NOTE(dtroyer): --host-id is deprecated in Mar 2016. Do not
+ # remove before 3.x release or Mar 2017.
+ host_group = parser.add_mutually_exclusive_group()
+ host_group.add_argument(
+ '--host',
+ metavar='<host-id>',
+ help='Allocate port on host <host-id> (ID only)',
+ )
+ host_group.add_argument(
'--host-id',
metavar='<host-id>',
- help='The ID of the host where the port is allocated'
+ help=argparse.SUPPRESS,
)
@@ -172,18 +203,30 @@ class CreatePort(command.ShowOne):
required=True,
help='Network this port belongs to (name or ID)')
_add_updatable_args(parser)
+ parser.add_argument(
+ '--fixed-ip',
+ metavar='subnet=<subnet>,ip-address=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ optional_keys=['subnet', 'ip-address'],
+ help='Desired IP and/or subnet (name or ID) for this port: '
+ 'subnet=<subnet>,ip-address=<ip-address> '
+ '(this option can be repeated)')
+ parser.add_argument(
+ '--binding-profile',
+ metavar='<binding-profile>',
+ action=parseractions.KeyValueAction,
+ help='Custom data to be passed as binding:profile: <key>=<value> '
+ '(this option can be repeated)')
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state',
action='store_true',
default=True,
help='Enable port (default)',
)
admin_group.add_argument(
'--disable',
- dest='admin_state',
- action='store_false',
+ action='store_true',
help='Disable port',
)
parser.add_argument(
@@ -241,6 +284,16 @@ class DeletePort(command.Command):
class ListPort(command.Lister):
"""List ports"""
+ def get_parser(self, prog_name):
+ parser = super(ListPort, self).get_parser(prog_name)
+ parser.add_argument(
+ '--router',
+ metavar='<router>',
+ dest='router',
+ help='List only ports attached to this router (name or ID)',
+ )
+ return parser
+
def take_action(self, parsed_args):
client = self.app.client_manager.network
@@ -257,7 +310,14 @@ class ListPort(command.Lister):
'Fixed IP Addresses',
)
- data = client.ports()
+ filters = {}
+ if parsed_args.router:
+ _router = client.find_router(parsed_args.router,
+ ignore_missing=False)
+ filters = {'device_id': _router.id}
+
+ data = client.ports(**filters)
+
return (column_headers,
(utils.get_item_properties(
s, columns,
@@ -265,6 +325,78 @@ class ListPort(command.Lister):
) for s in data))
+class SetPort(command.Command):
+ """Set port properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetPort, self).get_parser(prog_name)
+ _add_updatable_args(parser)
+ admin_group = parser.add_mutually_exclusive_group()
+ admin_group.add_argument(
+ '--enable',
+ action='store_true',
+ default=None,
+ help='Enable port',
+ )
+ admin_group.add_argument(
+ '--disable',
+ action='store_true',
+ help='Disable port',
+ )
+ parser.add_argument(
+ '--name',
+ metavar="<name>",
+ help=('Set port name'))
+ parser.add_argument(
+ 'port',
+ metavar="<port>",
+ help=("Port to modify (name or ID)")
+ )
+ fixed_ip = parser.add_mutually_exclusive_group()
+ fixed_ip.add_argument(
+ '--fixed-ip',
+ metavar='subnet=<subnet>,ip-address=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ optional_keys=['subnet', 'ip-address'],
+ help='Desired IP and/or subnet (name or ID) for this port: '
+ 'subnet=<subnet>,ip-address=<ip-address> '
+ '(this option can be repeated)')
+ fixed_ip.add_argument(
+ '--no-fixed-ip',
+ action='store_true',
+ help='Clear existing information of fixed-ips')
+ binding_profile = parser.add_mutually_exclusive_group()
+ binding_profile.add_argument(
+ '--binding-profile',
+ metavar='<binding-profile>',
+ action=parseractions.KeyValueAction,
+ help='Custom data to be passed as binding:profile: <key>=<value> '
+ '(this option can be repeated)')
+ binding_profile.add_argument(
+ '--no-binding-profile',
+ action='store_true',
+ help='Clear existing information of binding:profile')
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+
+ _prepare_fixed_ips(self.app.client_manager, parsed_args)
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+
+ if parsed_args.no_fixed_ip:
+ attrs['fixed_ips'] = []
+ if parsed_args.no_binding_profile:
+ attrs['binding:profile'] = {}
+
+ if attrs == {}:
+ msg = "Nothing specified to be set"
+ raise exceptions.CommandError(msg)
+
+ obj = client.find_port(parsed_args.port, ignore_missing=False)
+ client.update_port(obj, **attrs)
+
+
class ShowPort(command.ShowOne):
"""Display port details"""
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index caf6d5ce..cd0f0e4c 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -42,7 +42,7 @@ _formatters = {
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
@@ -53,10 +53,15 @@ def _get_attrs(client_manager, parsed_args):
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
- if parsed_args.admin_state_up is not None:
- attrs['admin_state_up'] = parsed_args.admin_state_up
- if parsed_args.distributed is not None:
- attrs['distributed'] = parsed_args.distributed
+ if parsed_args.enable:
+ attrs['admin_state_up'] = True
+ if parsed_args.disable:
+ attrs['admin_state_up'] = False
+ # centralized is available only for SetRouter and not for CreateRouter
+ if 'centralized' in parsed_args and parsed_args.centralized:
+ attrs['distributed'] = False
+ if parsed_args.distributed:
+ attrs['distributed'] = True
if ('availability_zone_hints' in parsed_args
and parsed_args.availability_zone_hints is not None):
attrs['availability_zone_hints'] = parsed_args.availability_zone_hints
@@ -119,15 +124,13 @@ class CreateRouter(command.ShowOne):
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state_up',
action='store_true',
default=True,
help="Enable router (default)",
)
admin_group.add_argument(
'--disable',
- dest='admin_state_up',
- action='store_false',
+ action='store_true',
help="Disable router",
)
parser.add_argument(
@@ -283,29 +286,24 @@ class SetRouter(command.Command):
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
'--enable',
- dest='admin_state_up',
action='store_true',
default=None,
help='Enable router',
)
admin_group.add_argument(
'--disable',
- dest='admin_state_up',
- action='store_false',
+ action='store_true',
help='Disable router',
)
distribute_group = parser.add_mutually_exclusive_group()
distribute_group.add_argument(
'--distributed',
- dest='distributed',
action='store_true',
- default=None,
help="Set router to distributed mode (disabled router only)",
)
distribute_group.add_argument(
'--centralized',
- dest='distributed',
- action='store_false',
+ action='store_true',
help="Set router to centralized mode (disabled router only)",
)
routes_group = parser.add_mutually_exclusive_group()
@@ -323,7 +321,6 @@ class SetRouter(command.Command):
)
routes_group.add_argument(
'--clear-routes',
- dest='clear_routes',
action='store_true',
help="Clear routes associated with the router",
)
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 62699ffd..92498144 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -14,9 +14,152 @@
"""Security Group action implementations"""
import argparse
+import six
from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
from openstackclient.network import common
+from openstackclient.network import utils as network_utils
+
+
+def _format_network_security_group_rules(sg_rules):
+ # For readability and to align with formatting compute security group
+ # rules, trim keys with caller known (e.g. security group and tenant ID)
+ # or empty values.
+ for sg_rule in sg_rules:
+ empty_keys = [k for k, v in six.iteritems(sg_rule) if not v]
+ for key in empty_keys:
+ sg_rule.pop(key)
+ sg_rule.pop('security_group_id', None)
+ sg_rule.pop('tenant_id', None)
+ return utils.format_list_of_dicts(sg_rules)
+
+
+def _format_compute_security_group_rule(sg_rule):
+ info = network_utils.transform_compute_security_group_rule(sg_rule)
+ # Trim parent security group ID since caller has this information.
+ info.pop('parent_group_id', None)
+ # Trim keys with empty string values.
+ keys_to_trim = [
+ 'ip_protocol',
+ 'ip_range',
+ 'port_range',
+ 'remote_security_group',
+ ]
+ for key in keys_to_trim:
+ if key in info and not info[key]:
+ info.pop(key)
+ return utils.format_dict(info)
+
+
+def _format_compute_security_group_rules(sg_rules):
+ rules = []
+ for sg_rule in sg_rules:
+ rules.append(_format_compute_security_group_rule(sg_rule))
+ return utils.format_list(rules, separator='\n')
+
+
+_formatters_network = {
+ 'security_group_rules': _format_network_security_group_rules,
+}
+
+
+_formatters_compute = {
+ 'rules': _format_compute_security_group_rules,
+}
+
+
+def _get_columns(item):
+ # Build the display columns and a list of the property columns
+ # that need to be mapped (display column name, property name).
+ columns = list(item.keys())
+ property_column_mappings = []
+ if 'security_group_rules' in columns:
+ columns.append('rules')
+ columns.remove('security_group_rules')
+ property_column_mappings.append(('rules', 'security_group_rules'))
+ if 'tenant_id' in columns:
+ columns.append('project_id')
+ columns.remove('tenant_id')
+ property_column_mappings.append(('project_id', 'tenant_id'))
+ display_columns = sorted(columns)
+
+ # Build the property columns and apply any column mappings.
+ property_columns = sorted(columns)
+ for property_column_mapping in property_column_mappings:
+ property_index = property_columns.index(property_column_mapping[0])
+ property_columns[property_index] = property_column_mapping[1]
+ return tuple(display_columns), property_columns
+
+
+class CreateSecurityGroup(common.NetworkAndComputeShowOne):
+ """Create a new security group"""
+
+ def update_parser_common(self, parser):
+ parser.add_argument(
+ "name",
+ metavar="<name>",
+ help="New security group name",
+ )
+ parser.add_argument(
+ "--description",
+ metavar="<description>",
+ help="Security group description",
+ )
+ return parser
+
+ def update_parser_network(self, parser):
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help="Owner's project (name or ID)"
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def _get_description(self, parsed_args):
+ if parsed_args.description is not None:
+ return parsed_args.description
+ else:
+ return parsed_args.name
+
+ def take_action_network(self, client, parsed_args):
+ # Build the create attributes.
+ attrs = {}
+ attrs['name'] = parsed_args.name
+ attrs['description'] = self._get_description(parsed_args)
+ if parsed_args.project is not None:
+ identity_client = self.app.client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+
+ # Create the security group and display the results.
+ obj = client.create_security_group(**attrs)
+ display_columns, property_columns = _get_columns(obj)
+ data = utils.get_item_properties(
+ obj,
+ property_columns,
+ formatters=_formatters_network
+ )
+ return (display_columns, data)
+
+ def take_action_compute(self, client, parsed_args):
+ description = self._get_description(parsed_args)
+ obj = client.security_groups.create(
+ parsed_args.name,
+ description,
+ )
+ display_columns, property_columns = _get_columns(obj._info)
+ data = utils.get_dict_properties(
+ obj._info,
+ property_columns,
+ formatters=_formatters_compute
+ )
+ return (display_columns, data)
class DeleteSecurityGroup(common.NetworkAndComputeCommand):
@@ -143,3 +286,39 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
data.name,
data.description,
)
+
+
+class ShowSecurityGroup(common.NetworkAndComputeShowOne):
+ """Display security group details"""
+
+ def update_parser_common(self, parser):
+ parser.add_argument(
+ 'group',
+ metavar='<group>',
+ help='Security group to display (name or ID)',
+ )
+ return parser
+
+ def take_action_network(self, client, parsed_args):
+ obj = client.find_security_group(parsed_args.group,
+ ignore_missing=False)
+ display_columns, property_columns = _get_columns(obj)
+ data = utils.get_item_properties(
+ obj,
+ property_columns,
+ formatters=_formatters_network
+ )
+ return (display_columns, data)
+
+ def take_action_compute(self, client, parsed_args):
+ obj = utils.find_resource(
+ client.security_groups,
+ parsed_args.group,
+ )
+ display_columns, property_columns = _get_columns(obj._info)
+ data = utils.get_dict_properties(
+ obj._info,
+ property_columns,
+ formatters=_formatters_compute
+ )
+ return (display_columns, data)
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index a61e3233..f60995ab 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -16,51 +16,132 @@
import six
from openstackclient.common import exceptions
+from openstackclient.common import parseractions
from openstackclient.common import utils
from openstackclient.network import common
-
-
-def _xform_security_group_rule(sgroup):
- info = {}
- info.update(sgroup)
- from_port = info.pop('from_port')
- to_port = info.pop('to_port')
- if isinstance(from_port, int) and isinstance(to_port, int):
- port_range = {'port_range': "%u:%u" % (from_port, to_port)}
- elif from_port is None and to_port is None:
- port_range = {'port_range': ""}
- else:
- port_range = {'port_range': "%s:%s" % (from_port, to_port)}
- info.update(port_range)
- if 'cidr' in info['ip_range']:
- info['ip_range'] = info['ip_range']['cidr']
- else:
- info['ip_range'] = ''
- if info['ip_protocol'] is None:
- info['ip_protocol'] = ''
- elif info['ip_protocol'].lower() == 'icmp':
- info['port_range'] = ''
- group = info.pop('group')
- if 'name' in group:
- info['remote_security_group'] = group['name']
- else:
- info['remote_security_group'] = ''
- return info
+from openstackclient.network import utils as network_utils
def _format_security_group_rule_show(obj):
- data = _xform_security_group_rule(obj)
+ data = network_utils.transform_compute_security_group_rule(obj)
return zip(*sorted(six.iteritems(data)))
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
return tuple(sorted(columns))
+def _convert_to_lowercase(string):
+ return string.lower()
+
+
+class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
+ """Create a new security group rule"""
+
+ def update_parser_common(self, parser):
+ parser.add_argument(
+ 'group',
+ metavar='<group>',
+ help='Create rule in this security group (name or ID)',
+ )
+ # TODO(rtheis): Add support for additional protocols for network.
+ # Until then, continue enforcing the compute choices.
+ parser.add_argument(
+ "--proto",
+ metavar="<proto>",
+ default="tcp",
+ choices=['icmp', 'tcp', 'udp'],
+ type=_convert_to_lowercase,
+ help="IP protocol (icmp, tcp, udp; default: tcp)",
+ )
+ source_group = parser.add_mutually_exclusive_group()
+ source_group.add_argument(
+ "--src-ip",
+ metavar="<ip-address>",
+ default="0.0.0.0/0",
+ help="Source IP address block (may use CIDR notation; default: "
+ "0.0.0.0/0)",
+ )
+ source_group.add_argument(
+ "--src-group",
+ metavar="<group>",
+ help="Source security group (name or ID)",
+ )
+ parser.add_argument(
+ "--dst-port",
+ metavar="<port-range>",
+ default=(0, 0),
+ action=parseractions.RangeAction,
+ help="Destination port, may be a single port or port range: "
+ "137:139 (only required for IP protocols tcp and udp)",
+ )
+ return parser
+
+ def take_action_network(self, client, parsed_args):
+ # Get the security group ID to hold the rule.
+ security_group_id = client.find_security_group(
+ parsed_args.group,
+ ignore_missing=False
+ ).id
+
+ # Build the create attributes.
+ attrs = {}
+ # TODO(rtheis): Add --direction option. Until then, continue
+ # with the default of 'ingress'.
+ attrs['direction'] = 'ingress'
+ # TODO(rtheis): Add --ethertype option. Until then, continue
+ # with the default of 'IPv4'
+ attrs['ethertype'] = 'IPv4'
+ # TODO(rtheis): Add port range support (type and code) for icmp
+ # protocol. Until then, continue ignoring the port range.
+ if parsed_args.proto != 'icmp':
+ attrs['port_range_min'] = parsed_args.dst_port[0]
+ attrs['port_range_max'] = parsed_args.dst_port[1]
+ attrs['protocol'] = parsed_args.proto
+ if parsed_args.src_group is not None:
+ attrs['remote_group_id'] = client.find_security_group(
+ parsed_args.src_group,
+ ignore_missing=False
+ ).id
+ else:
+ attrs['remote_ip_prefix'] = parsed_args.src_ip
+ attrs['security_group_id'] = security_group_id
+
+ # Create and show the security group rule.
+ obj = client.create_security_group_rule(**attrs)
+ columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return (columns, data)
+
+ def take_action_compute(self, client, parsed_args):
+ group = utils.find_resource(
+ client.security_groups,
+ parsed_args.group,
+ )
+ if parsed_args.proto == 'icmp':
+ from_port, to_port = -1, -1
+ else:
+ from_port, to_port = parsed_args.dst_port
+ if parsed_args.src_group is not None:
+ parsed_args.src_group = utils.find_resource(
+ client.security_groups,
+ parsed_args.src_group,
+ ).id
+ obj = client.security_group_rules.create(
+ group.id,
+ parsed_args.proto,
+ from_port,
+ to_port,
+ parsed_args.src_ip,
+ parsed_args.src_group,
+ )
+ return _format_security_group_rule_show(obj._info)
+
+
class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
"""Delete a security group rule"""
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index b514a88f..10e5859a 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -12,9 +12,15 @@
#
"""Subnet action implementations"""
+import copy
+
+from json.encoder import JSONEncoder
from openstackclient.common import command
+from openstackclient.common import exceptions
+from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
def _format_allocation_pools(data):
@@ -23,21 +29,246 @@ def _format_allocation_pools(data):
return ','.join(pool_formatted)
+def _format_host_routes(data):
+ try:
+ return '\n'.join([JSONEncoder().encode(route) for route in data])
+ except (TypeError, KeyError):
+ return ''
+
+
_formatters = {
'allocation_pools': _format_allocation_pools,
'dns_nameservers': utils.format_list,
- 'host_routes': utils.format_list,
+ 'host_routes': _format_host_routes,
}
+def _get_common_parse_arguments(parser):
+ parser.add_argument(
+ '--allocation-pool',
+ metavar='start=<ip-address>,end=<ip-address>',
+ dest='allocation_pools',
+ action=parseractions.MultiKeyValueAction,
+ required_keys=['start', 'end'],
+ help='Allocation pool IP addresses for this subnet '
+ 'e.g.: start=192.168.199.2,end=192.168.199.254 '
+ '(This option can be repeated)',
+ )
+ parser.add_argument(
+ '--dns-nameserver',
+ metavar='<dns-nameserver>',
+ action='append',
+ dest='dns_nameservers',
+ help='DNS name server for this subnet '
+ '(This option can be repeated)',
+ )
+ parser.add_argument(
+ '--host-route',
+ metavar='destination=<subnet>,gateway=<ip-address>',
+ dest='host_routes',
+ action=parseractions.MultiKeyValueAction,
+ required_keys=['destination', 'gateway'],
+ help='Additional route for this subnet '
+ 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 '
+ 'destination: destination subnet (in CIDR notation) '
+ 'gateway: nexthop IP address '
+ '(This option can be repeated)',
+ )
+
+
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
return tuple(sorted(columns))
+def convert_entries_to_nexthop(entries):
+ # Change 'gateway' entry to 'nexthop'
+ changed_entries = copy.deepcopy(entries)
+ for entry in changed_entries:
+ entry['nexthop'] = entry['gateway']
+ del entry['gateway']
+
+ return changed_entries
+
+
+def convert_entries_to_gateway(entries):
+ # Change 'nexthop' entry to 'gateway'
+ changed_entries = copy.deepcopy(entries)
+ for entry in changed_entries:
+ entry['gateway'] = entry['nexthop']
+ del entry['nexthop']
+
+ return changed_entries
+
+
+def _get_attrs(client_manager, parsed_args, is_create=True):
+ attrs = {}
+ if 'name' in parsed_args and parsed_args.name is not None:
+ attrs['name'] = str(parsed_args.name)
+
+ if is_create:
+ if 'project' in parsed_args and parsed_args.project is not None:
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+ client = client_manager.network
+ attrs['network_id'] = client.find_network(parsed_args.network,
+ ignore_missing=False).id
+ if parsed_args.subnet_pool is not None:
+ subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool,
+ ignore_missing=False)
+ attrs['subnetpool_id'] = subnet_pool.id
+ if parsed_args.use_default_subnet_pool:
+ attrs['use_default_subnetpool'] = True
+ if parsed_args.prefix_length is not None:
+ attrs['prefixlen'] = parsed_args.prefix_length
+ if parsed_args.subnet_range is not None:
+ attrs['cidr'] = parsed_args.subnet_range
+ if parsed_args.ip_version is not None:
+ attrs['ip_version'] = parsed_args.ip_version
+ if parsed_args.ipv6_ra_mode is not None:
+ attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
+ if parsed_args.ipv6_address_mode is not None:
+ attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
+
+ if 'gateway' in parsed_args and parsed_args.gateway is not None:
+ gateway = parsed_args.gateway.lower()
+
+ if not is_create and gateway == 'auto':
+ raise exceptions.CommandError("Auto option is not available"
+ " for Subnet Set. Valid options are"
+ " <ip-address> or none")
+ elif gateway != 'auto':
+ if gateway == 'none':
+ attrs['gateway_ip'] = None
+ else:
+ attrs['gateway_ip'] = gateway
+ if ('allocation_pools' in parsed_args and
+ parsed_args.allocation_pools is not None):
+ attrs['allocation_pools'] = parsed_args.allocation_pools
+ if parsed_args.dhcp:
+ attrs['enable_dhcp'] = True
+ elif parsed_args.no_dhcp:
+ attrs['enable_dhcp'] = False
+ if ('dns_nameservers' in parsed_args and
+ parsed_args.dns_nameservers is not None):
+ attrs['dns_nameservers'] = parsed_args.dns_nameservers
+ if 'host_routes' in parsed_args and parsed_args.host_routes is not None:
+ # Change 'gateway' entry to 'nexthop' to match the API
+ attrs['host_routes'] = convert_entries_to_nexthop(
+ parsed_args.host_routes)
+ return attrs
+
+
+class CreateSubnet(command.ShowOne):
+ """Create a subnet"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateSubnet, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ help='New subnet name',
+ )
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help="Owner's project (name or ID)",
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ subnet_pool_group = parser.add_mutually_exclusive_group()
+ subnet_pool_group.add_argument(
+ '--subnet-pool',
+ metavar='<subnet-pool>',
+ help='Subnet pool from which this subnet will obtain a CIDR '
+ '(Name or ID)',
+ )
+ subnet_pool_group.add_argument(
+ '--use-default-subnet-pool',
+ action='store_true',
+ help='Use default subnet pool for --ip-version',
+ )
+ parser.add_argument(
+ '--prefix-length',
+ metavar='<prefix-length>',
+ help='Prefix length for subnet allocation from subnetpool',
+ )
+ parser.add_argument(
+ '--subnet-range',
+ metavar='<subnet-range>',
+ help='Subnet range in CIDR notation '
+ '(required if --subnet-pool is not specified, '
+ 'optional otherwise)',
+ )
+ dhcp_enable_group = parser.add_mutually_exclusive_group()
+ dhcp_enable_group.add_argument(
+ '--dhcp',
+ action='store_true',
+ default=True,
+ help='Enable DHCP (default)',
+ )
+ dhcp_enable_group.add_argument(
+ '--no-dhcp',
+ action='store_true',
+ help='Disable DHCP',
+ )
+ parser.add_argument(
+ '--gateway',
+ metavar='<gateway>',
+ default='auto',
+ help="Specify a gateway for the subnet. The three options are: "
+ " <ip-address>: Specific IP address to use as the gateway "
+ " 'auto': Gateway address should automatically be "
+ " chosen from within the subnet itself "
+ " 'none': This subnet will not use a gateway "
+ "e.g.: --gateway 192.168.9.1, --gateway auto, --gateway none"
+ "(default is 'auto')",
+ )
+ parser.add_argument(
+ '--ip-version',
+ type=int,
+ default=4,
+ choices=[4, 6],
+ help='IP version (default is 4). Note that when subnet pool is '
+ 'specified, IP version is determined from the subnet pool '
+ 'and this option is ignored.',
+ )
+ parser.add_argument(
+ '--ipv6-ra-mode',
+ choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
+ help='IPv6 RA (Router Advertisement) mode, '
+ 'valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]',
+ )
+ parser.add_argument(
+ '--ipv6-address-mode',
+ choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
+ help='IPv6 address mode, '
+ 'valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]',
+ )
+ parser.add_argument(
+ '--network',
+ required=True,
+ metavar='<network>',
+ help='Network this subnet belongs to (name or ID)',
+ )
+ _get_common_parse_arguments(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ obj = client.create_subnet(**attrs)
+ columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ return (columns, data)
+
+
class DeleteSubnet(command.Command):
"""Delete subnet"""
@@ -46,7 +277,7 @@ class DeleteSubnet(command.Command):
parser.add_argument(
'subnet',
metavar="<subnet>",
- help="Subnet to delete (name or ID)"
+ help="Subnet to delete (name or ID)",
)
return parser
@@ -89,6 +320,56 @@ class ListSubnet(command.Lister):
) for s in data))
+class SetSubnet(command.Command):
+ """Set subnet properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetSubnet, self).get_parser(prog_name)
+ parser.add_argument(
+ 'subnet',
+ metavar="<subnet>",
+ help=("Subnet to modify (name or ID)")
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help='Updated name of the subnet',
+ )
+ dhcp_enable_group = parser.add_mutually_exclusive_group()
+ dhcp_enable_group.add_argument(
+ '--dhcp',
+ action='store_true',
+ default=None,
+ help='Enable DHCP',
+ )
+ dhcp_enable_group.add_argument(
+ '--no-dhcp',
+ action='store_true',
+ help='Disable DHCP',
+ )
+ parser.add_argument(
+ '--gateway',
+ metavar='<gateway>',
+ help="Specify a gateway for the subnet. The options are: "
+ " <ip-address>: Specific IP address to use as the gateway "
+ " 'none': This subnet will not use a gateway "
+ "e.g.: --gateway 192.168.9.1, --gateway none"
+ )
+ _get_common_parse_arguments(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_subnet(parsed_args.subnet, ignore_missing=False)
+ attrs = _get_attrs(self.app.client_manager, parsed_args,
+ is_create=False)
+ if not attrs:
+ msg = "Nothing specified to be set"
+ raise exceptions.CommandError(msg)
+ client.update_subnet(obj, **attrs)
+ return
+
+
class ShowSubnet(command.ShowOne):
"""Show subnet details"""
@@ -97,7 +378,7 @@ class ShowSubnet(command.ShowOne):
parser.add_argument(
'subnet',
metavar="<subnet>",
- help="Subnet to show (name or ID)"
+ help="Subnet to show (name or ID)",
)
return parser
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 5bb45c12..6b6fc090 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -14,11 +14,14 @@
"""Subnet pool action implementations"""
from openstackclient.common import command
+from openstackclient.common import exceptions
+from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
def _get_columns(item):
- columns = item.keys()
+ columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
columns.append('project_id')
@@ -30,6 +33,93 @@ _formatters = {
}
+def _get_attrs(client_manager, parsed_args):
+ attrs = {}
+ if parsed_args.name is not None:
+ attrs['name'] = str(parsed_args.name)
+ if parsed_args.prefixes is not None:
+ attrs['prefixes'] = parsed_args.prefixes
+ if parsed_args.default_prefix_length is not None:
+ attrs['default_prefixlen'] = parsed_args.default_prefix_length
+ if parsed_args.min_prefix_length is not None:
+ attrs['min_prefixlen'] = parsed_args.min_prefix_length
+ if parsed_args.max_prefix_length is not None:
+ attrs['max_prefixlen'] = parsed_args.max_prefix_length
+
+ # "subnet pool set" command doesn't support setting project.
+ if 'project' in parsed_args and parsed_args.project is not None:
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+
+ return attrs
+
+
+def _add_prefix_options(parser):
+ parser.add_argument(
+ '--pool-prefix',
+ metavar='<pool-prefix>',
+ dest='prefixes',
+ action='append',
+ help='Set subnet pool prefixes (in CIDR notation). '
+ 'Repeat this option to set multiple prefixes.',
+ )
+ parser.add_argument(
+ '--default-prefix-length',
+ metavar='<default-prefix-length>',
+ action=parseractions.NonNegativeAction,
+ help='Set subnet pool default prefix length',
+ )
+ parser.add_argument(
+ '--min-prefix-length',
+ metavar='<min-prefix-length>',
+ action=parseractions.NonNegativeAction,
+ help='Set subnet pool minimum prefix length',
+ )
+ parser.add_argument(
+ '--max-prefix-length',
+ metavar='<max-prefix-length>',
+ action=parseractions.NonNegativeAction,
+ help='Set subnet pool maximum prefix length',
+ )
+
+
+class CreateSubnetPool(command.ShowOne):
+ """Create subnet pool"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateSubnetPool, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help='Name of the new subnet pool'
+ )
+ _add_prefix_options(parser)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help="Owner's project (name or ID)",
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ # NeutronServer expects prefixes to be a List
+ if "prefixes" not in attrs:
+ attrs['prefixes'] = []
+ obj = client.create_subnet_pool(**attrs)
+ columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ return (columns, data)
+
+
class DeleteSubnetPool(command.Command):
"""Delete subnet pool"""
@@ -37,8 +127,8 @@ class DeleteSubnetPool(command.Command):
parser = super(DeleteSubnetPool, self).get_parser(prog_name)
parser.add_argument(
'subnet_pool',
- metavar="<subnet-pool>",
- help=("Subnet pool to delete (name or ID)")
+ metavar='<subnet-pool>',
+ help='Subnet pool to delete (name or ID)'
)
return parser
@@ -98,6 +188,42 @@ class ListSubnetPool(command.Lister):
) for s in data))
+class SetSubnetPool(command.Command):
+ """Set subnet pool properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetSubnetPool, self).get_parser(prog_name)
+ parser.add_argument(
+ 'subnet_pool',
+ metavar='<subnet-pool>',
+ help='Subnet pool to modify (name or ID)'
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help='Set subnet pool name',
+ )
+ _add_prefix_options(parser)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_subnet_pool(parsed_args.subnet_pool,
+ ignore_missing=False)
+
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ if attrs == {}:
+ msg = "Nothing specified to be set"
+ raise exceptions.CommandError(msg)
+
+ # Existing prefixes must be a subset of the new prefixes.
+ if 'prefixes' in attrs:
+ attrs['prefixes'].extend(obj.prefixes)
+
+ client.update_subnet_pool(obj, **attrs)
+
+
class ShowSubnetPool(command.ShowOne):
"""Display subnet pool details"""
@@ -105,8 +231,8 @@ class ShowSubnetPool(command.ShowOne):
parser = super(ShowSubnetPool, self).get_parser(prog_name)
parser.add_argument(
'subnet_pool',
- metavar="<subnet-pool>",
- help=("Subnet pool to display (name or ID)")
+ metavar='<subnet-pool>',
+ help='Subnet pool to display (name or ID)'
)
return parser
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 53e9be08..b7bc7b1a 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -189,6 +189,18 @@ class OpenStackShell(app.App):
dest='cacert',
default=utils.env('OS_CACERT'),
help='CA certificate bundle file (Env: OS_CACERT)')
+ parser.add_argument(
+ '--os-cert',
+ metavar='<certificate-file>',
+ dest='cert',
+ default=utils.env('OS_CERT'),
+ help='Client certificate bundle file (Env: OS_CERT)')
+ parser.add_argument(
+ '--os-key',
+ metavar='<key-file>',
+ dest='key',
+ default=utils.env('OS_KEY'),
+ help='Client certificate key file (Env: OS_KEY)')
verify_group = parser.add_mutually_exclusive_group()
verify_group.add_argument(
'--verify',
@@ -355,7 +367,7 @@ class OpenStackShell(app.App):
self.log.warning(
"%s version %s is not in supported versions %s"
% (api, version_opt,
- ', '.join(mod.API_VERSIONS.keys())))
+ ', '.join(list(mod.API_VERSIONS.keys()))))
# Command groups deal only with major versions
version = '.v' + version_opt.replace('.', '_').split('_')[0]
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index 2bd9e783..6fc5b41e 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -58,6 +58,8 @@ class FakeOptions(object):
self.interface = None
self.url = None
self.auth = {}
+ self.cert = None
+ self.key = None
self.default_domain = 'default'
self.__dict__.update(kwargs)
@@ -268,6 +270,21 @@ class TestClientManager(utils.TestCase):
self.assertEqual('cafile', client_manager._cacert)
self.assertTrue(client_manager.is_network_endpoint_enabled())
+ def test_client_manager_password_no_cert(self):
+ client_manager = clientmanager.ClientManager(
+ cli_options=FakeOptions())
+ self.assertIsNone(client_manager._cert)
+
+ def test_client_manager_password_client_cert(self):
+ client_manager = clientmanager.ClientManager(
+ cli_options=FakeOptions(cert='cert'))
+ self.assertEqual('cert', client_manager._cert)
+
+ def test_client_manager_password_client_cert_and_key(self):
+ client_manager = clientmanager.ClientManager(
+ cli_options=FakeOptions(cert='cert', key='key'))
+ self.assertEqual(('cert', 'key'), client_manager._cert)
+
def _select_auth_plugin(self, auth_params, api_version, auth_plugin_name):
auth_params['auth_type'] = auth_plugin_name
auth_params['identity_api_version'] = api_version
diff --git a/openstackclient/tests/common/test_extension.py b/openstackclient/tests/common/test_extension.py
index 66532827..0736a3e5 100644
--- a/openstackclient/tests/common/test_extension.py
+++ b/openstackclient/tests/common/test_extension.py
@@ -12,13 +12,16 @@
#
import copy
+import mock
from openstackclient.common import extension
from openstackclient.tests import fakes
from openstackclient.tests import utils
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.network.v2 import fakes as network_fakes
+from openstackclient.tests.volume.v2 import fakes as volume_fakes
class TestExtension(utils.TestCommand):
@@ -34,6 +37,16 @@ class TestExtension(utils.TestCommand):
self.app.client_manager.identity.extensions)
self.identity_extensions_mock.reset_mock()
+ self.app.client_manager.compute = compute_fakes.FakeComputev2Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+ self.app.client_manager.volume = volume_fakes.FakeVolumeClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
network_client = network_fakes.FakeNetworkV2Client()
self.app.client_manager.network = network_client
self.network_extensions_mock = network_client.extensions
@@ -43,6 +56,8 @@ class TestExtension(utils.TestCommand):
class TestExtensionList(TestExtension):
columns = ('Name', 'Alias', 'Description')
+ long_columns = ('Name', 'Namespace', 'Description', 'Alias', 'Updated',
+ 'Links')
def setUp(self):
super(TestExtensionList, self).setUp()
@@ -55,12 +70,33 @@ class TestExtensionList(TestExtension):
),
]
+ self.app.client_manager.compute.list_extensions = mock.Mock()
+ self.compute_extensions_mock = (
+ self.app.client_manager.compute.list_extensions)
+ self.compute_extensions_mock.show_all.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(compute_fakes.EXTENSION),
+ loaded=True,
+ ),
+ ]
+
+ self.app.client_manager.volume.list_extensions = mock.Mock()
+ self.volume_extensions_mock = (
+ self.app.client_manager.volume.list_extensions)
+ self.volume_extensions_mock.show_all.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(volume_fakes.EXTENSION),
+ loaded=True,
+ ),
+ ]
+
# Get the command object to test
self.cmd = extension.ListExtension(self.app, None)
- def test_extension_list_no_options(self):
- arglist = []
- verifylist = []
+ def _test_extension_list_helper(self, arglist, verifylist,
+ expected_data, long=False):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
@@ -68,10 +104,15 @@ class TestExtensionList(TestExtension):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- # no args should output from all services
- self.identity_extensions_mock.list.assert_called_with()
+ if long:
+ self.assertEqual(self.long_columns, columns)
+ else:
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(expected_data, tuple(data))
- self.assertEqual(self.columns, columns)
+ def test_extension_list_no_options(self):
+ arglist = []
+ verifylist = []
datalist = (
(
identity_fakes.extension_name,
@@ -79,12 +120,26 @@ class TestExtensionList(TestExtension):
identity_fakes.extension_description,
),
(
+ compute_fakes.extension_name,
+ compute_fakes.extension_alias,
+ compute_fakes.extension_description,
+ ),
+ (
+ volume_fakes.extension_name,
+ volume_fakes.extension_alias,
+ volume_fakes.extension_description,
+ ),
+ (
network_fakes.extension_name,
network_fakes.extension_alias,
network_fakes.extension_description,
),
)
- self.assertEqual(datalist, tuple(data))
+ self._test_extension_list_helper(arglist, verifylist, datalist)
+ self.identity_extensions_mock.list.assert_called_with()
+ self.compute_extensions_mock.show_all.assert_called_with()
+ self.volume_extensions_mock.show_all.assert_called_with()
+ self.network_extensions_mock.assert_called_with()
def test_extension_list_long(self):
arglist = [
@@ -93,19 +148,6 @@ class TestExtensionList(TestExtension):
verifylist = [
('long', True),
]
- 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)
-
- # no args should output from all services
- self.identity_extensions_mock.list.assert_called_with()
-
- collist = ('Name', 'Namespace', 'Description', 'Alias', 'Updated',
- 'Links')
- self.assertEqual(collist, columns)
datalist = (
(
identity_fakes.extension_name,
@@ -116,6 +158,22 @@ class TestExtensionList(TestExtension):
identity_fakes.extension_links,
),
(
+ compute_fakes.extension_name,
+ compute_fakes.extension_namespace,
+ compute_fakes.extension_description,
+ compute_fakes.extension_alias,
+ compute_fakes.extension_updated,
+ compute_fakes.extension_links,
+ ),
+ (
+ volume_fakes.extension_name,
+ volume_fakes.extension_namespace,
+ volume_fakes.extension_description,
+ volume_fakes.extension_alias,
+ volume_fakes.extension_updated,
+ volume_fakes.extension_links,
+ ),
+ (
network_fakes.extension_name,
network_fakes.extension_namespace,
network_fakes.extension_description,
@@ -124,7 +182,11 @@ class TestExtensionList(TestExtension):
network_fakes.extension_links,
),
)
- self.assertEqual(datalist, tuple(data))
+ self._test_extension_list_helper(arglist, verifylist, datalist, True)
+ self.identity_extensions_mock.list.assert_called_with()
+ self.compute_extensions_mock.show_all.assert_called_with()
+ self.volume_extensions_mock.show_all.assert_called_with()
+ self.network_extensions_mock.assert_called_with()
def test_extension_list_identity(self):
arglist = [
@@ -133,22 +195,13 @@ class TestExtensionList(TestExtension):
verifylist = [
('identity', True),
]
- 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.identity_extensions_mock.list.assert_called_with()
-
- self.assertEqual(self.columns, columns)
datalist = ((
identity_fakes.extension_name,
identity_fakes.extension_alias,
identity_fakes.extension_description,
), )
- self.assertEqual(datalist, tuple(data))
+ self._test_extension_list_helper(arglist, verifylist, datalist)
+ self.identity_extensions_mock.list.assert_called_with()
def test_extension_list_network(self):
arglist = [
@@ -157,13 +210,6 @@ class TestExtensionList(TestExtension):
verifylist = [
('network', True),
]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.network_extensions_mock.assert_called_with()
-
- self.assertEqual(self.columns, columns)
datalist = (
(
network_fakes.extension_name,
@@ -171,4 +217,35 @@ class TestExtensionList(TestExtension):
network_fakes.extension_description,
),
)
- self.assertEqual(datalist, tuple(data))
+ self._test_extension_list_helper(arglist, verifylist, datalist)
+ self.network_extensions_mock.assert_called_with()
+
+ def test_extension_list_compute(self):
+ arglist = [
+ '--compute',
+ ]
+ verifylist = [
+ ('compute', True),
+ ]
+ datalist = ((
+ compute_fakes.extension_name,
+ compute_fakes.extension_alias,
+ compute_fakes.extension_description,
+ ), )
+ self._test_extension_list_helper(arglist, verifylist, datalist)
+ self.compute_extensions_mock.show_all.assert_called_with()
+
+ def test_extension_list_volume(self):
+ arglist = [
+ '--volume',
+ ]
+ verifylist = [
+ ('volume', True),
+ ]
+ datalist = ((
+ volume_fakes.extension_name,
+ volume_fakes.extension_alias,
+ volume_fakes.extension_description,
+ ), )
+ self._test_extension_list_helper(arglist, verifylist, datalist)
+ self.volume_extensions_mock.show_all.assert_called_with()
diff --git a/openstackclient/tests/common/test_module.py b/openstackclient/tests/common/test_module.py
index 2821da9e..7d08dae7 100644
--- a/openstackclient/tests/common/test_module.py
+++ b/openstackclient/tests/common/test_module.py
@@ -48,10 +48,11 @@ class TestCommandList(utils.TestCommand):
super(TestCommandList, self).setUp()
self.app.command_manager = mock.Mock()
- self.app.command_manager.get_command_groups.return_value = ['test']
+ self.app.command_manager.get_command_groups.return_value = [
+ 'openstack.common'
+ ]
self.app.command_manager.get_command_names.return_value = [
- 'one',
- 'cmd two',
+ 'limits show\nextension list'
]
# Get the command object to test
@@ -67,12 +68,15 @@ class TestCommandList(utils.TestCommand):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
+ # TODO(bapalm): Adjust this when cliff properly supports
+ # handling the detection rather than using the hard-code below.
collist = ('Command Group', 'Commands')
self.assertEqual(collist, columns)
datalist = ((
- 'test',
- ['one', 'cmd two'],
- ), )
+ 'openstack.common',
+ 'limits show\nextension list'
+ ),)
+
self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/common/test_parseractions.py b/openstackclient/tests/common/test_parseractions.py
index a4ee07bf..5c5ca3d3 100644
--- a/openstackclient/tests/common/test_parseractions.py
+++ b/openstackclient/tests/common/test_parseractions.py
@@ -91,11 +91,7 @@ class TestMultiKeyValueAction(utils.TestCase):
{'req1': 'aaa', 'req2': 'bbb'},
{'req1': '', 'req2': ''},
]
- # Need to sort the lists before comparing them
- key = lambda x: x['req1']
- expect.sort(key=key)
- actual.sort(key=key)
- self.assertListEqual(expect, actual)
+ self.assertItemsEqual(expect, actual)
def test_empty_required_optional(self):
self.parser.add_argument(
@@ -119,11 +115,7 @@ class TestMultiKeyValueAction(utils.TestCase):
{'req1': 'aaa', 'req2': 'bbb'},
{'req1': '', 'req2': ''},
]
- # Need to sort the lists before comparing them
- key = lambda x: x['req1']
- expect.sort(key=key)
- actual.sort(key=key)
- self.assertListEqual(expect, actual)
+ self.assertItemsEqual(expect, actual)
def test_error_values_with_comma(self):
self.assertRaises(
diff --git a/openstackclient/tests/common/test_utils.py b/openstackclient/tests/common/test_utils.py
index 95bce458..2248d043 100644
--- a/openstackclient/tests/common/test_utils.py
+++ b/openstackclient/tests/common/test_utils.py
@@ -306,6 +306,18 @@ class TestFindResource(test_utils.TestCase):
self.manager.get.assert_called_with(self.name)
self.manager.find.assert_called_with(name=self.name)
+ def test_find_resource_list_forbidden(self):
+ self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
+ self.manager.find = mock.Mock(side_effect=Exception('Boom!'))
+ self.manager.list = mock.Mock(
+ side_effect=exceptions.Forbidden(403)
+ )
+ self.assertRaises(exceptions.Forbidden,
+ utils.find_resource,
+ self.manager,
+ self.name)
+ self.manager.list.assert_called_with()
+
def test_find_resource_find_no_unique(self):
self.manager.get = mock.Mock(side_effect=Exception('Boom!'))
self.manager.find = mock.Mock(side_effect=NoUniqueMatch())
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index 32d257f1..6c67c470 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -79,13 +79,47 @@ QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA))
service_host = 'host_test'
service_binary = 'compute_test'
service_status = 'enabled'
+service_disabled_reason = 'earthquake'
SERVICE = {
'host': service_host,
'binary': service_binary,
'status': service_status,
+ 'disabled_reason': service_disabled_reason,
}
+class FakeAggregate(object):
+ """Fake one aggregate."""
+
+ @staticmethod
+ def create_one_aggregate(attrs=None):
+ """Create a fake aggregate.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id and other attributes
+ """
+ if attrs is None:
+ attrs = {}
+
+ # Set default attribute
+ aggregate_info = {
+ "name": "aggregate-name-" + uuid.uuid4().hex,
+ "availability_zone": "ag_zone",
+ "hosts": [],
+ "id": "aggregate-id-" + uuid.uuid4().hex,
+ "metadata": {
+ "availability_zone": "ag_zone",
+ }
+ }
+ aggregate_info.update(attrs)
+ aggregate = fakes.FakeResource(
+ info=copy.deepcopy(aggregate_info),
+ loaded=True)
+ return aggregate
+
+
class FakeComputev2Client(object):
def __init__(self, **kwargs):
@@ -140,6 +174,9 @@ class FakeComputev2Client(object):
self.keypairs = mock.Mock()
self.keypairs.resource_class = fakes.FakeResource(None, {})
+ self.hosts = mock.Mock()
+ self.hosts.resource_class = fakes.FakeResource(None, {})
+
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@@ -243,7 +280,7 @@ class FakeHypervisor(object):
return hypervisors
-class FakehypervisorStats(object):
+class FakeHypervisorStats(object):
"""Fake one or more hypervisor stats."""
@staticmethod
@@ -296,7 +333,7 @@ class FakehypervisorStats(object):
hypervisors = []
for i in range(0, count):
hypervisors.append(
- FakehypervisorStats.create_one_hypervisor_stats(attrs))
+ FakeHypervisorStats.create_one_hypervisor_stats(attrs))
return hypervisors
@@ -333,7 +370,9 @@ class FakeSecurityGroup(object):
security_group_attrs.update(attrs)
# Set default methods.
- security_group_methods = {}
+ security_group_methods = {
+ 'keys': ['id', 'name', 'description', 'tenant_id', 'rules'],
+ }
# Overwrite default methods.
security_group_methods.update(methods)
@@ -369,7 +408,7 @@ class FakeSecurityGroupRule(object):
"""Fake one or more security group rules."""
@staticmethod
- def create_one_security_group_rule(attrs={}, methods={}):
+ def create_one_security_group_rule(attrs=None, methods=None):
"""Create a fake security group rule.
:param Dictionary attrs:
@@ -379,15 +418,20 @@ class FakeSecurityGroupRule(object):
:return:
A FakeResource object, with id, etc.
"""
+ if attrs is None:
+ attrs = {}
+ if methods is None:
+ methods = {}
+
# Set default attributes.
security_group_rule_attrs = {
- 'from_port': -1,
+ 'from_port': 0,
'group': {},
'id': 'security-group-rule-id-' + uuid.uuid4().hex,
- 'ip_protocol': 'icmp',
+ 'ip_protocol': 'tcp',
'ip_range': {'cidr': '0.0.0.0/0'},
'parent_group_id': 'security-group-id-' + uuid.uuid4().hex,
- 'to_port': -1,
+ 'to_port': 0,
}
# Overwrite default attributes.
@@ -406,7 +450,7 @@ class FakeSecurityGroupRule(object):
return security_group_rule
@staticmethod
- def create_security_group_rules(attrs={}, methods={}, count=2):
+ def create_security_group_rules(attrs=None, methods=None, count=2):
"""Create multiple fake security group rules.
:param Dictionary attrs:
@@ -501,42 +545,21 @@ class FakeServer(object):
return mock.MagicMock(side_effect=servers)
-class FakeFlavorResource(fakes.FakeResource):
- """Fake flavor object's methods to help test.
-
- The flavor object has three methods to get, set, unset its properties.
- Need to fake them, otherwise the functions to be tested won't run properly.
- """
-
- def __init__(self, manager=None, info={}, loaded=False, methods={}):
- super(FakeFlavorResource, self).__init__(manager, info,
- loaded, methods)
- # Fake properties.
- self._keys = {'property': 'value'}
-
- def set_keys(self, args):
- self._keys.update(args)
-
- def unset_keys(self, keys):
- for key in keys:
- self._keys.pop(key, None)
-
- def get_keys(self):
- return self._keys
-
-
class FakeFlavor(object):
"""Fake one or more flavors."""
@staticmethod
- def create_one_flavor(attrs={}):
+ def create_one_flavor(attrs=None):
"""Create a fake flavor.
:param Dictionary attrs:
A dictionary with all attributes
:return:
- A FakeFlavorResource object, with id, name, ram, vcpus, properties
+ A FakeResource object, with id, name, ram, vcpus, properties
"""
+ if attrs is None:
+ attrs = {}
+
# Set default attributes.
flavor_info = {
'id': 'flavor-id-' + uuid.uuid4().hex,
@@ -554,7 +577,15 @@ class FakeFlavor(object):
# Overwrite default attributes.
flavor_info.update(attrs)
- flavor = FakeFlavorResource(info=copy.deepcopy(flavor_info),
+ # Set default methods.
+ flavor_methods = {
+ 'set_keys': None,
+ 'unset_keys': None,
+ 'get_keys': {'property': 'value'},
+ }
+
+ flavor = fakes.FakeResource(info=copy.deepcopy(flavor_info),
+ methods=flavor_methods,
loaded=True)
# Set attributes with special mappings in nova client.
@@ -573,7 +604,7 @@ class FakeFlavor(object):
:param int count:
The number of flavors to fake
:return:
- A list of FakeFlavorResource objects faking the flavors
+ A list of FakeResource objects faking the flavors
"""
flavors = []
for i in range(0, count):
@@ -589,7 +620,7 @@ class FakeFlavor(object):
list. Otherwise create one.
:param List flavors:
- A list of FakeFlavorResource objects faking flavors
+ A list of FakeResource objects faking flavors
:param int count:
The number of flavors to fake
:return:
@@ -636,14 +667,14 @@ class FakeKeypair(object):
@staticmethod
def create_keypairs(attrs=None, count=2):
- """Create multiple fake flavors.
+ """Create multiple fake keypairs.
:param Dictionary attrs:
A dictionary with all attributes
:param int count:
- The number of flavors to fake
+ The number of keypairs to fake
:return:
- A list of FakeFlavorResource objects faking the flavors
+ A list of FakeResource objects faking the keypairs
"""
keypairs = []
@@ -878,3 +909,56 @@ class FakeNetwork(object):
networks.append(FakeNetwork.create_one_network(attrs, methods))
return networks
+
+
+class FakeHost(object):
+ """Fake one host."""
+
+ @staticmethod
+ def create_one_host(attrs=None):
+ """Create a fake host.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id and other attributes
+ """
+ if attrs is None:
+ attrs = {}
+
+ # Set default attributes.
+ host_info = {
+ "id": 1,
+ "service_id": 1,
+ "host": "host1",
+ "uuid": 'host-id-' + uuid.uuid4().hex,
+ "vcpus": 10,
+ "memory_mb": 100,
+ "local_gb": 100,
+ "vcpus_used": 5,
+ "memory_mb_used": 50,
+ "local_gb_used": 10,
+ "hypervisor_type": "xen",
+ "hypervisor_version": 1,
+ "hypervisor_hostname": "devstack1",
+ "free_ram_mb": 50,
+ "free_disk_gb": 50,
+ "current_workload": 10,
+ "running_vms": 1,
+ "cpu_info": "",
+ "disk_available_least": 1,
+ "host_ip": "10.10.10.10",
+ "supported_instances": "",
+ "metrics": "",
+ "pci_stats": "",
+ "extra_resources": "",
+ "stats": "",
+ "numa_topology": "",
+ "ram_allocation_ratio": 1.0,
+ "cpu_allocation_ratio": 1.0
+ }
+ host_info.update(attrs)
+ host = fakes.FakeResource(
+ info=copy.deepcopy(host_info),
+ loaded=True)
+ return host
diff --git a/openstackclient/tests/compute/v2/test_aggregate.py b/openstackclient/tests/compute/v2/test_aggregate.py
new file mode 100644
index 00000000..58dd7755
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_aggregate.py
@@ -0,0 +1,392 @@
+# Copyright 2016 Huawei, Inc. All rights reserved.
+#
+# 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.compute.v2 import aggregate
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import utils as tests_utils
+
+
+class TestAggregate(compute_fakes.TestComputev2):
+
+ fake_ag = compute_fakes.FakeAggregate.create_one_aggregate()
+
+ columns = (
+ 'availability_zone',
+ 'hosts',
+ 'id',
+ 'metadata',
+ 'name',
+ )
+
+ data = (
+ fake_ag.availability_zone,
+ fake_ag.hosts,
+ fake_ag.id,
+ fake_ag.metadata,
+ fake_ag.name,
+ )
+
+ def setUp(self):
+ super(TestAggregate, self).setUp()
+
+ # Get a shortcut to the AggregateManager Mock
+ self.aggregate_mock = self.app.client_manager.compute.aggregates
+ self.aggregate_mock.reset_mock()
+
+
+class TestAggregateAddHost(TestAggregate):
+
+ def setUp(self):
+ super(TestAggregateAddHost, self).setUp()
+
+ self.aggregate_mock.get.return_value = self.fake_ag
+ self.aggregate_mock.add_host.return_value = self.fake_ag
+ self.cmd = aggregate.AddAggregateHost(self.app, None)
+
+ def test_aggregate_add_host(self):
+ arglist = [
+ 'ag1',
+ 'host1',
+ ]
+ verifylist = [
+ ('aggregate', 'ag1'),
+ ('host', 'host1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.aggregate_mock.add_host.assert_called_once_with(self.fake_ag,
+ parsed_args.host)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestAggregateCreate(TestAggregate):
+
+ def setUp(self):
+ super(TestAggregateCreate, self).setUp()
+
+ self.aggregate_mock.create.return_value = self.fake_ag
+ self.aggregate_mock.set_metadata.return_value = self.fake_ag
+ self.cmd = aggregate.CreateAggregate(self.app, None)
+
+ def test_aggregate_create(self):
+ arglist = [
+ 'ag1',
+ ]
+ verifylist = [
+ ('name', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
+ None)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_aggregate_create_with_zone(self):
+ arglist = [
+ '--zone', 'zone1',
+ 'ag1',
+ ]
+ verifylist = [
+ ('zone', 'zone1'),
+ ('name', 'ag1'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
+ parsed_args.zone)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_aggregate_create_with_property(self):
+ arglist = [
+ '--property', 'key1=value1',
+ '--property', 'key2=value2',
+ 'ag1',
+ ]
+ verifylist = [
+ ('property', {'key1': 'value1', 'key2': 'value2'}),
+ ('name', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
+ None)
+ self.aggregate_mock.set_metadata.assert_called_once_with(
+ self.fake_ag, parsed_args.property)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestAggregateDelete(TestAggregate):
+
+ def setUp(self):
+ super(TestAggregateDelete, self).setUp()
+
+ self.aggregate_mock.get.return_value = self.fake_ag
+ self.cmd = aggregate.DeleteAggregate(self.app, None)
+
+ def test_aggregate_delete(self):
+ arglist = [
+ 'ag1',
+ ]
+ verifylist = [
+ ('aggregate', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.aggregate_mock.delete.assert_called_once_with(self.fake_ag.id)
+ self.assertIsNone(result)
+
+
+class TestAggregateList(TestAggregate):
+
+ list_columns = (
+ "ID",
+ "Name",
+ "Availability Zone",
+ )
+
+ list_columns_long = (
+ "ID",
+ "Name",
+ "Availability Zone",
+ "Properties",
+ )
+
+ list_data = ((
+ TestAggregate.fake_ag.id,
+ TestAggregate.fake_ag.name,
+ TestAggregate.fake_ag.availability_zone,
+ ), )
+
+ list_data_long = ((
+ TestAggregate.fake_ag.id,
+ TestAggregate.fake_ag.name,
+ TestAggregate.fake_ag.availability_zone,
+ {},
+ ), )
+
+ def setUp(self):
+ super(TestAggregateList, self).setUp()
+
+ self.aggregate_mock.list.return_value = [self.fake_ag]
+ self.cmd = aggregate.ListAggregate(self.app, None)
+
+ def test_aggregate_list(self):
+
+ parsed_args = self.check_parser(self.cmd, [], [])
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.list_columns, columns)
+ self.assertEqual(self.list_data, tuple(data))
+
+ def test_aggregate_list_with_long(self):
+ arglist = [
+ '--long',
+ ]
+ vertifylist = [
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, vertifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.list_columns_long, columns)
+ self.assertEqual(self.list_data_long, tuple(data))
+
+
+class TestAggregateRemoveHost(TestAggregate):
+
+ def setUp(self):
+ super(TestAggregateRemoveHost, self).setUp()
+
+ self.aggregate_mock.get.return_value = self.fake_ag
+ self.aggregate_mock.remove_host.return_value = self.fake_ag
+ self.cmd = aggregate.RemoveAggregateHost(self.app, None)
+
+ def test_aggregate_add_host(self):
+ arglist = [
+ 'ag1',
+ 'host1',
+ ]
+ verifylist = [
+ ('aggregate', 'ag1'),
+ ('host', 'host1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.aggregate_mock.remove_host.assert_called_once_with(
+ self.fake_ag, parsed_args.host)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestAggregateSet(TestAggregate):
+
+ def setUp(self):
+ super(TestAggregateSet, self).setUp()
+
+ self.aggregate_mock.get.return_value = self.fake_ag
+ self.cmd = aggregate.SetAggregate(self.app, None)
+
+ def test_aggregate_set_no_option(self):
+ arglist = [
+ 'ag1',
+ ]
+ verifylist = [
+ ('aggregate', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.assertNotCalled(self.aggregate_mock.update)
+ self.assertNotCalled(self.aggregate_mock.set_metadata)
+ self.assertIsNone(result)
+
+ def test_aggregate_set_with_name(self):
+ arglist = [
+ '--name', 'new_name',
+ 'ag1',
+ ]
+ verifylist = [
+ ('name', 'new_name'),
+ ('aggregate', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.aggregate_mock.update.assert_called_once_with(
+ self.fake_ag, {'name': parsed_args.name})
+ self.assertNotCalled(self.aggregate_mock.set_metadata)
+ self.assertIsNone(result)
+
+ def test_aggregate_set_with_zone(self):
+ arglist = [
+ '--zone', 'new_zone',
+ 'ag1',
+ ]
+ verifylist = [
+ ('zone', 'new_zone'),
+ ('aggregate', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.aggregate_mock.update.assert_called_once_with(
+ self.fake_ag, {'availability_zone': parsed_args.zone})
+ self.assertNotCalled(self.aggregate_mock.set_metadata)
+ self.assertIsNone(result)
+
+ def test_aggregate_set_with_property(self):
+ arglist = [
+ '--property', 'key1=value1',
+ '--property', 'key2=value2',
+ 'ag1',
+ ]
+ verifylist = [
+ ('property', {'key1': 'value1', 'key2': 'value2'}),
+ ('aggregate', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+ self.assertNotCalled(self.aggregate_mock.update)
+ self.aggregate_mock.set_metadata.assert_called_once_with(
+ self.fake_ag, parsed_args.property)
+ self.assertIsNone(result)
+
+
+class TestAggregateShow(TestAggregate):
+
+ columns = (
+ 'availability_zone',
+ 'hosts',
+ 'id',
+ 'name',
+ 'properties',
+ )
+
+ data = (
+ TestAggregate.fake_ag.availability_zone,
+ TestAggregate.fake_ag.hosts,
+ TestAggregate.fake_ag.id,
+ TestAggregate.fake_ag.name,
+ '',
+ )
+
+ def setUp(self):
+ super(TestAggregateShow, self).setUp()
+
+ self.aggregate_mock.get.return_value = self.fake_ag
+ self.cmd = aggregate.ShowAggregate(self.app, None)
+
+ def test_aggregate_show(self):
+ arglist = [
+ 'ag1',
+ ]
+ verifylist = [
+ ('aggregate', 'ag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestAggregateUnset(TestAggregate):
+
+ def setUp(self):
+ super(TestAggregateUnset, self).setUp()
+
+ self.aggregate_mock.get.return_value = self.fake_ag
+ self.cmd = aggregate.UnsetAggregate(self.app, None)
+
+ def test_aggregate_unset(self):
+ arglist = [
+ '--property', 'unset_key',
+ 'ag1',
+ ]
+ verifylist = [
+ ('property', ['unset_key']),
+ ('aggregate', 'ag1'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.aggregate_mock.set_metadata.assert_called_once_with(
+ self.fake_ag, {'unset_key': None})
+ self.assertIsNone(result)
+
+ def test_aggregate_unset_no_property(self):
+ arglist = [
+ 'ag1',
+ ]
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
diff --git a/openstackclient/tests/compute/v2/test_host.py b/openstackclient/tests/compute/v2/test_host.py
new file mode 100644
index 00000000..de537577
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_host.py
@@ -0,0 +1,75 @@
+# Copyright 2016 IBM 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.
+#
+
+from openstackclient.compute.v2 import host
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+
+
+class TestHost(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestHost, self).setUp()
+
+ # Get a shortcut to the FlavorManager Mock
+ self.host_mock = self.app.client_manager.compute.hosts
+ self.host_mock.reset_mock()
+
+
+class TestHostSet(TestHost):
+
+ def setUp(self):
+ super(TestHostSet, self).setUp()
+
+ self.host = compute_fakes.FakeHost.create_one_host()
+ self.host_mock.get.return_value = self.host
+ self.host_mock.update.return_value = None
+
+ self.cmd = host.SetHost(self.app, None)
+
+ def test_host_set_no_option(self):
+ arglist = [
+ str(self.host.id)
+ ]
+ verifylist = [
+ ('host', str(self.host.id))
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.assertIsNone(result)
+
+ body = {}
+ self.host_mock.update.assert_called_with(self.host.id, body)
+
+ def test_host_set(self):
+ arglist = [
+ '--enable',
+ '--disable-maintenance',
+ str(self.host.id)
+ ]
+ verifylist = [
+ ('enable', True),
+ ('enable_maintenance', False),
+ ('host', str(self.host.id))
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.assertIsNone(result)
+
+ body = {'status': True, 'maintenance_mode': False}
+ self.host_mock.update.assert_called_with(self.host.id, body)
diff --git a/openstackclient/tests/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/compute/v2/test_hypervisor_stats.py
index 39e303a8..ca5ce29b 100644
--- a/openstackclient/tests/compute/v2/test_hypervisor_stats.py
+++ b/openstackclient/tests/compute/v2/test_hypervisor_stats.py
@@ -33,7 +33,7 @@ class TestHypervisorStatsShow(TestHypervisorStats):
super(TestHypervisorStatsShow, self).setUp()
self.hypervisor_stats = \
- compute_fakes.FakehypervisorStats.create_one_hypervisor_stats()
+ compute_fakes.FakeHypervisorStats.create_one_hypervisor_stats()
self.hypervisors_mock.statistics.return_value =\
self.hypervisor_stats
diff --git a/openstackclient/tests/compute/v2/test_security_group.py b/openstackclient/tests/compute/v2/test_security_group.py
deleted file mode 100644
index 01e27b82..00000000
--- a/openstackclient/tests/compute/v2/test_security_group.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# 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 copy
-
-from openstackclient.compute.v2 import security_group
-from openstackclient.tests.compute.v2 import fakes as compute_fakes
-from openstackclient.tests import fakes
-from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
-
-
-security_group_id = '11'
-security_group_name = 'wide-open'
-security_group_description = 'nothing but net'
-
-SECURITY_GROUP = {
- 'id': security_group_id,
- 'name': security_group_name,
- 'description': security_group_description,
- 'tenant_id': identity_fakes.project_id,
-}
-
-
-class FakeSecurityGroupResource(fakes.FakeResource):
-
- def get_keys(self):
- return {'property': 'value'}
-
-
-class TestSecurityGroup(compute_fakes.TestComputev2):
-
- def setUp(self):
- super(TestSecurityGroup, self).setUp()
-
- # Get a shortcut compute client security_groups mock
- self.secgroups_mock = self.app.client_manager.compute.security_groups
- self.secgroups_mock.reset_mock()
-
- # Get a shortcut identity client projects mock
- self.projects_mock = self.app.client_manager.identity.projects
- self.projects_mock.reset_mock()
-
-
-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()
-
- self.secgroups_mock.create.return_value = FakeSecurityGroupResource(
- None,
- copy.deepcopy(SECURITY_GROUP),
- loaded=True,
- )
-
- # Get the command object to test
- self.cmd = security_group.CreateSecurityGroup(self.app, None)
-
- def test_security_group_create_no_options(self):
- arglist = [
- security_group_name,
- ]
- verifylist = [
- ('name', security_group_name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.secgroups_mock.create.assert_called_with(
- security_group_name,
- security_group_name,
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
- def test_security_group_create_description(self):
- arglist = [
- security_group_name,
- '--description', security_group_description,
- ]
- verifylist = [
- ('name', security_group_name),
- ('description', security_group_description),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.secgroups_mock.create.assert_called_with(
- security_group_name,
- security_group_description,
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/compute/v2/test_security_group_rule.py b/openstackclient/tests/compute/v2/test_security_group_rule.py
index 9a8003f3..42bf2c26 100644
--- a/openstackclient/tests/compute/v2/test_security_group_rule.py
+++ b/openstackclient/tests/compute/v2/test_security_group_rule.py
@@ -17,7 +17,6 @@ from openstackclient.compute.v2 import security_group
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
-from openstackclient.tests import utils
security_group_id = '11'
@@ -111,271 +110,6 @@ class TestSecurityGroupRule(compute_fakes.TestComputev2):
self.sg_rules_mock.reset_mock()
-class TestSecurityGroupRuleCreate(TestSecurityGroupRule):
-
- columns = (
- 'id',
- 'ip_protocol',
- 'ip_range',
- 'parent_group_id',
- 'port_range',
- 'remote_security_group',
- )
-
- def setUp(self):
- super(TestSecurityGroupRuleCreate, self).setUp()
-
- self.secgroups_mock.get.return_value = FakeSecurityGroupRuleResource(
- None,
- copy.deepcopy(SECURITY_GROUP),
- loaded=True,
- )
-
- # Get the command object to test
- self.cmd = security_group.CreateSecurityGroupRule(self.app, None)
-
- def test_security_group_rule_create_no_options(self):
- self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
- None,
- copy.deepcopy(SECURITY_GROUP_RULE),
- loaded=True,
- )
-
- arglist = [
- security_group_name,
- ]
- verifylist = [
- ('group', security_group_name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.sg_rules_mock.create.assert_called_with(
- security_group_id,
- 'tcp',
- 0,
- 0,
- security_group_rule_cidr,
- None,
- )
-
- self.assertEqual(self.columns, columns)
- datalist = (
- security_group_rule_id,
- 'tcp',
- security_group_rule_cidr,
- security_group_id,
- '0:0',
- '',
- )
- self.assertEqual(datalist, data)
-
- def test_security_group_rule_create_ftp(self):
- sg_rule = copy.deepcopy(SECURITY_GROUP_RULE)
- sg_rule['from_port'] = 20
- sg_rule['to_port'] = 21
- self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
- None,
- sg_rule,
- loaded=True,
- )
-
- arglist = [
- security_group_name,
- '--dst-port', '20:21',
- ]
- verifylist = [
- ('group', security_group_name),
- ('dst_port', (20, 21)),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.sg_rules_mock.create.assert_called_with(
- security_group_id,
- 'tcp',
- 20,
- 21,
- security_group_rule_cidr,
- None,
- )
-
- self.assertEqual(self.columns, columns)
- datalist = (
- security_group_rule_id,
- 'tcp',
- security_group_rule_cidr,
- security_group_id,
- '20:21',
- '',
- )
- self.assertEqual(datalist, data)
-
- def test_security_group_rule_create_ssh(self):
- sg_rule = copy.deepcopy(SECURITY_GROUP_RULE)
- sg_rule['from_port'] = 22
- sg_rule['to_port'] = 22
- sg_rule['ip_range'] = {}
- sg_rule['group'] = {'name': security_group_name}
- self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
- None,
- sg_rule,
- loaded=True,
- )
-
- arglist = [
- security_group_name,
- '--dst-port', '22',
- '--src-group', security_group_id,
- ]
- verifylist = [
- ('group', security_group_name),
- ('dst_port', (22, 22)),
- ('src_group', security_group_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.sg_rules_mock.create.assert_called_with(
- security_group_id,
- 'tcp',
- 22,
- 22,
- security_group_rule_cidr,
- security_group_id,
- )
-
- self.assertEqual(self.columns, columns)
- datalist = (
- security_group_rule_id,
- 'tcp',
- '',
- security_group_id,
- '22:22',
- security_group_name,
- )
- self.assertEqual(datalist, data)
-
- def test_security_group_rule_create_udp(self):
- sg_rule = copy.deepcopy(SECURITY_GROUP_RULE)
- sg_rule['ip_protocol'] = 'udp'
- self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
- None,
- sg_rule,
- loaded=True,
- )
-
- arglist = [
- security_group_name,
- '--proto', 'udp',
- ]
- verifylist = [
- ('group', security_group_name),
- ('proto', 'udp'),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.sg_rules_mock.create.assert_called_with(
- security_group_id,
- 'udp',
- 0,
- 0,
- security_group_rule_cidr,
- None,
- )
-
- self.assertEqual(self.columns, columns)
- datalist = (
- security_group_rule_id,
- 'udp',
- security_group_rule_cidr,
- security_group_id,
- '0:0',
- '',
- )
- self.assertEqual(datalist, data)
-
- def test_security_group_rule_create_icmp(self):
- sg_rule_cidr = '10.0.2.0/24'
- sg_rule = copy.deepcopy(SECURITY_GROUP_RULE_ICMP)
- sg_rule['ip_range'] = {'cidr': sg_rule_cidr}
- self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
- None,
- sg_rule,
- loaded=True,
- )
-
- arglist = [
- security_group_name,
- '--proto', 'ICMP',
- '--src-ip', sg_rule_cidr,
- ]
- verifylist = [
- ('group', security_group_name),
- ('proto', 'ICMP'),
- ('src_ip', sg_rule_cidr)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # SecurityGroupManager.create(name, description)
- self.sg_rules_mock.create.assert_called_with(
- security_group_id,
- 'ICMP',
- -1,
- -1,
- sg_rule_cidr,
- None,
- )
-
- self.assertEqual(self.columns, columns)
- datalist = (
- security_group_rule_id,
- 'icmp',
- sg_rule_cidr,
- security_group_id,
- '',
- '',
- )
- self.assertEqual(datalist, data)
-
- def test_security_group_rule_create_src_invalid(self):
- arglist = [
- security_group_name,
- '--proto', 'ICMP',
- '--src-ip', security_group_rule_cidr,
- '--src-group', security_group_id,
- ]
-
- self.assertRaises(utils.ParserException,
- self.check_parser, self.cmd, arglist, [])
-
-
class TestSecurityGroupRuleList(TestSecurityGroupRule):
def setUp(self):
diff --git a/openstackclient/tests/compute/v2/test_service.py b/openstackclient/tests/compute/v2/test_service.py
index 2f8b2e7d..db097204 100644
--- a/openstackclient/tests/compute/v2/test_service.py
+++ b/openstackclient/tests/compute/v2/test_service.py
@@ -14,6 +14,7 @@
#
import copy
+import mock
from openstackclient.compute.v2 import service
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -85,13 +86,37 @@ class TestServiceList(TestService):
# 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.
- self.cmd.take_action(parsed_args)
+ columns, data = self.cmd.take_action(parsed_args)
self.service_mock.list.assert_called_with(
compute_fakes.service_host,
compute_fakes.service_binary,
)
+ self.assertNotIn("Disabled Reason", columns)
+ self.assertNotIn(compute_fakes.service_disabled_reason, list(data)[0])
+
+ def test_service_list_with_long_option(self):
+ arglist = [
+ '--host', compute_fakes.service_host,
+ '--service', compute_fakes.service_binary,
+ '--long'
+ ]
+ verifylist = [
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ('long', True)
+ ]
+ 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.assertIn("Disabled Reason", columns)
+ self.assertIn(compute_fakes.service_disabled_reason, list(data)[0])
+
class TestServiceSet(TestService):
@@ -114,14 +139,14 @@ class TestServiceSet(TestService):
def test_service_set_enable(self):
arglist = [
+ '--enable',
compute_fakes.service_host,
compute_fakes.service_binary,
- '--enable',
]
verifylist = [
+ ('enabled', True),
('host', compute_fakes.service_host),
('service', compute_fakes.service_binary),
- ('enabled', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -135,14 +160,14 @@ class TestServiceSet(TestService):
def test_service_set_disable(self):
arglist = [
+ '--disable',
compute_fakes.service_host,
compute_fakes.service_binary,
- '--disable',
]
verifylist = [
+ ('enabled', False),
('host', compute_fakes.service_host),
('service', compute_fakes.service_binary),
- ('enabled', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -153,3 +178,83 @@ class TestServiceSet(TestService):
compute_fakes.service_binary,
)
self.assertIsNone(result)
+
+ def test_service_set_disable_with_reason(self):
+ reason = 'earthquake'
+ arglist = [
+ '--disable',
+ '--disable-reason', reason,
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ ]
+ verifylist = [
+ ('enabled', False),
+ ('disable_reason', reason),
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.service_mock.disable_log_reason.assert_called_with(
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ reason
+ )
+ self.assertIsNone(result)
+
+ def test_service_set_only_with_disable_reason(self):
+ reason = 'earthquake'
+ arglist = [
+ '--disable-reason', reason,
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('disable_reason', reason),
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'info') as mock_log:
+ result = self.cmd.take_action(parsed_args)
+
+ msg = "argument --disable-reason has been ignored"
+ mock_log.assert_called_once_with(msg)
+
+ self.service_mock.enable.assert_called_with(
+ compute_fakes.service_host,
+ compute_fakes.service_binary
+ )
+ self.assertIsNone(result)
+
+ def test_service_set_enable_with_disable_reason(self):
+ reason = 'earthquake'
+ arglist = [
+ '--enable',
+ '--disable-reason', reason,
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('disable_reason', reason),
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'info') as mock_log:
+ result = self.cmd.take_action(parsed_args)
+
+ msg = "argument --disable-reason has been ignored"
+ mock_log.assert_called_once_with(msg)
+
+ self.service_mock.enable.assert_called_with(
+ compute_fakes.service_host,
+ compute_fakes.service_binary
+ )
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/identity/v2_0/test_endpoint.py b/openstackclient/tests/identity/v2_0/test_endpoint.py
index 088fdcd1..45ece45a 100644
--- a/openstackclient/tests/identity/v2_0/test_endpoint.py
+++ b/openstackclient/tests/identity/v2_0/test_endpoint.py
@@ -132,11 +132,12 @@ class TestEndpointDelete(TestEndpoint):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.endpoints_mock.delete.assert_called_with(
identity_fakes.endpoint_id,
)
+ self.assertIsNone(result)
class TestEndpointList(TestEndpoint):
diff --git a/openstackclient/tests/identity/v2_0/test_project.py b/openstackclient/tests/identity/v2_0/test_project.py
index 98570297..38684aaf 100644
--- a/openstackclient/tests/identity/v2_0/test_project.py
+++ b/openstackclient/tests/identity/v2_0/test_project.py
@@ -613,10 +613,9 @@ class TestProjectUnset(TestProject):
verifylist = [
('property', ['fee', 'fo']),
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'description': identity_fakes.project_description,
@@ -631,3 +630,4 @@ class TestProjectUnset(TestProject):
identity_fakes.project_id,
**kwargs
)
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/identity/v2_0/test_role.py b/openstackclient/tests/identity/v2_0/test_role.py
index 03b7f924..3c4b79a4 100644
--- a/openstackclient/tests/identity/v2_0/test_role.py
+++ b/openstackclient/tests/identity/v2_0/test_role.py
@@ -240,11 +240,12 @@ class TestRoleDelete(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.roles_mock.delete.assert_called_with(
identity_fakes.role_id,
)
+ self.assertIsNone(result)
class TestRoleList(TestRole):
@@ -459,7 +460,7 @@ class TestRoleRemove(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# RoleManager.remove_user_role(user, role, tenant=None)
self.roles_mock.remove_user_role.assert_called_with(
@@ -467,6 +468,7 @@ class TestRoleRemove(TestRole):
identity_fakes.role_id,
identity_fakes.project_id,
)
+ self.assertIsNone(result)
class TestRoleShow(TestRole):
diff --git a/openstackclient/tests/identity/v2_0/test_service.py b/openstackclient/tests/identity/v2_0/test_service.py
index 606b1433..dc0fbcd1 100644
--- a/openstackclient/tests/identity/v2_0/test_service.py
+++ b/openstackclient/tests/identity/v2_0/test_service.py
@@ -194,11 +194,12 @@ class TestServiceDelete(TestService):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.services_mock.delete.assert_called_with(
identity_fakes.service_id,
)
+ self.assertIsNone(result)
class TestServiceList(TestService):
diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py
index c90477f9..613139dd 100644
--- a/openstackclient/tests/identity/v2_0/test_token.py
+++ b/openstackclient/tests/identity/v2_0/test_token.py
@@ -99,6 +99,7 @@ class TestTokenRevoke(TestToken):
verifylist = [('token', self.TOKEN)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.tokens_mock.delete.assert_called_with(self.TOKEN)
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/identity/v2_0/test_user.py b/openstackclient/tests/identity/v2_0/test_user.py
index 9afe4ad1..921e215d 100644
--- a/openstackclient/tests/identity/v2_0/test_user.py
+++ b/openstackclient/tests/identity/v2_0/test_user.py
@@ -414,11 +414,12 @@ class TestUserDelete(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.users_mock.delete.assert_called_with(
identity_fakes.user_id,
)
+ self.assertIsNone(result)
class TestUserList(TestUser):
@@ -578,7 +579,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -590,6 +591,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_password(self):
arglist = [
@@ -608,13 +610,14 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# UserManager.update_password(user, password)
self.users_mock.update_password.assert_called_with(
identity_fakes.user_id,
'secret',
)
+ self.assertIsNone(result)
def test_user_set_password_prompt(self):
arglist = [
@@ -636,13 +639,14 @@ class TestUserSet(TestUser):
mocker = mock.Mock()
mocker.return_value = 'abc123'
with mock.patch("openstackclient.common.utils.get_password", mocker):
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# UserManager.update_password(user, password)
self.users_mock.update_password.assert_called_with(
identity_fakes.user_id,
'abc123',
)
+ self.assertIsNone(result)
def test_user_set_email(self):
arglist = [
@@ -660,7 +664,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -672,6 +676,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_project(self):
arglist = [
@@ -689,13 +694,14 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# UserManager.update_tenant(user, tenant)
self.users_mock.update_tenant.assert_called_with(
identity_fakes.user_id,
identity_fakes.project_id,
)
+ self.assertIsNone(result)
def test_user_set_enable(self):
arglist = [
@@ -713,7 +719,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -724,6 +730,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_disable(self):
arglist = [
@@ -741,7 +748,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -752,6 +759,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
class TestUserShow(TestUser):
diff --git a/openstackclient/tests/identity/v3/test_consumer.py b/openstackclient/tests/identity/v3/test_consumer.py
index 4d15d5d8..4a8cf087 100644
--- a/openstackclient/tests/identity/v3/test_consumer.py
+++ b/openstackclient/tests/identity/v3/test_consumer.py
@@ -136,45 +136,6 @@ class TestConsumerList(TestOAuth1):
self.assertEqual(datalist, tuple(data))
-class TestConsumerShow(TestOAuth1):
-
- def setUp(self):
- super(TestConsumerShow, self).setUp()
-
- consumer_no_secret = copy.deepcopy(identity_fakes.OAUTH_CONSUMER)
- del consumer_no_secret['secret']
- self.consumers_mock.get.return_value = fakes.FakeResource(
- None,
- consumer_no_secret,
- loaded=True,
- )
-
- # Get the command object to test
- self.cmd = consumer.ShowConsumer(self.app, None)
-
- def test_consumer_show(self):
- arglist = [
- identity_fakes.consumer_id,
- ]
- verifylist = [
- ('consumer', identity_fakes.consumer_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- self.consumers_mock.get.assert_called_with(
- identity_fakes.consumer_id,
- )
-
- collist = ('description', 'id')
- self.assertEqual(collist, columns)
- datalist = (
- identity_fakes.consumer_description,
- identity_fakes.consumer_id,
- )
- self.assertEqual(datalist, data)
-
-
class TestConsumerSet(TestOAuth1):
def setUp(self):
@@ -217,3 +178,42 @@ class TestConsumerSet(TestOAuth1):
**kwargs
)
self.assertIsNone(result)
+
+
+class TestConsumerShow(TestOAuth1):
+
+ def setUp(self):
+ super(TestConsumerShow, self).setUp()
+
+ consumer_no_secret = copy.deepcopy(identity_fakes.OAUTH_CONSUMER)
+ del consumer_no_secret['secret']
+ self.consumers_mock.get.return_value = fakes.FakeResource(
+ None,
+ consumer_no_secret,
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = consumer.ShowConsumer(self.app, None)
+
+ def test_consumer_show(self):
+ arglist = [
+ identity_fakes.consumer_id,
+ ]
+ verifylist = [
+ ('consumer', identity_fakes.consumer_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.consumers_mock.get.assert_called_with(
+ identity_fakes.consumer_id,
+ )
+
+ collist = ('description', 'id')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.consumer_description,
+ identity_fakes.consumer_id,
+ )
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_credential.py b/openstackclient/tests/identity/v3/test_credential.py
index afff7b6c..d8866124 100644
--- a/openstackclient/tests/identity/v3/test_credential.py
+++ b/openstackclient/tests/identity/v3/test_credential.py
@@ -96,9 +96,11 @@ class TestCredentialSet(TestCredential):
'--data', self.json_data,
identity_fakes.credential_id,
]
-
parsed_args = self.check_parser(self.cmd, arglist, [])
- self.cmd.take_action(parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
def test_credential_set_valid_with_project(self):
arglist = [
@@ -108,6 +110,8 @@ class TestCredentialSet(TestCredential):
'--project', identity_fakes.project_name,
identity_fakes.credential_id,
]
-
parsed_args = self.check_parser(self.cmd, arglist, [])
- self.cmd.take_action(parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py
index ddad6ffb..465e79ba 100644
--- a/openstackclient/tests/identity/v3/test_identity_provider.py
+++ b/openstackclient/tests/identity/v3/test_identity_provider.py
@@ -258,10 +258,13 @@ class TestIdentityProviderDelete(TestIdentityProvider):
('identity_provider', identity_fakes.idp_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+
self.identity_providers_mock.delete.assert_called_with(
identity_fakes.idp_id,
)
+ self.assertIsNone(result)
class TestIdentityProviderList(TestIdentityProvider):
@@ -307,46 +310,6 @@ class TestIdentityProviderList(TestIdentityProvider):
self.assertEqual(datalist, tuple(data))
-class TestIdentityProviderShow(TestIdentityProvider):
-
- def setUp(self):
- super(TestIdentityProviderShow, self).setUp()
-
- ret = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.IDENTITY_PROVIDER),
- loaded=True,
- )
- self.identity_providers_mock.get.return_value = ret
- # Get the command object to test
- self.cmd = identity_provider.ShowIdentityProvider(self.app, None)
-
- def test_identity_provider_show(self):
- arglist = [
- identity_fakes.idp_id,
- ]
- verifylist = [
- ('identity_provider', identity_fakes.idp_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.identity_providers_mock.get.assert_called_with(
- identity_fakes.idp_id,
- )
-
- collist = ('description', 'enabled', 'id', 'remote_ids')
- self.assertEqual(collist, columns)
- datalist = (
- identity_fakes.idp_description,
- True,
- identity_fakes.idp_id,
- identity_fakes.formatted_idp_remote_ids
- )
- self.assertEqual(datalist, data)
-
-
class TestIdentityProviderSet(TestIdentityProvider):
columns = (
@@ -624,3 +587,43 @@ class TestIdentityProviderSet(TestIdentityProvider):
# neither --enable nor --disable was specified
self.assertIsNone(columns)
self.assertIsNone(data)
+
+
+class TestIdentityProviderShow(TestIdentityProvider):
+
+ def setUp(self):
+ super(TestIdentityProviderShow, self).setUp()
+
+ ret = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.IDENTITY_PROVIDER),
+ loaded=True,
+ )
+ self.identity_providers_mock.get.return_value = ret
+ # Get the command object to test
+ self.cmd = identity_provider.ShowIdentityProvider(self.app, None)
+
+ def test_identity_provider_show(self):
+ arglist = [
+ identity_fakes.idp_id,
+ ]
+ verifylist = [
+ ('identity_provider', identity_fakes.idp_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.identity_providers_mock.get.assert_called_with(
+ identity_fakes.idp_id,
+ )
+
+ collist = ('description', 'enabled', 'id', 'remote_ids')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.idp_description,
+ True,
+ identity_fakes.idp_id,
+ identity_fakes.formatted_idp_remote_ids
+ )
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_mappings.py b/openstackclient/tests/identity/v3/test_mappings.py
index e811c0b6..b9e3b1d5 100644
--- a/openstackclient/tests/identity/v3/test_mappings.py
+++ b/openstackclient/tests/identity/v3/test_mappings.py
@@ -92,11 +92,13 @@ class TestMappingDelete(TestMapping):
verifylist = [
('mapping', identity_fakes.mapping_id)
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+
self.mapping_mock.delete.assert_called_with(
identity_fakes.mapping_id)
+ self.assertIsNone(result)
class TestMappingList(TestMapping):
@@ -143,41 +145,6 @@ class TestMappingList(TestMapping):
self.assertEqual(datalist, data)
-class TestMappingShow(TestMapping):
-
- def setUp(self):
- super(TestMappingShow, self).setUp()
-
- self.mapping_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.MAPPING_RESPONSE),
- loaded=True
- )
-
- self.cmd = mapping.ShowMapping(self.app, None)
-
- def test_mapping_show(self):
- arglist = [
- identity_fakes.mapping_id
- ]
- verifylist = [
- ('mapping', identity_fakes.mapping_id)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.mapping_mock.get.assert_called_with(
- identity_fakes.mapping_id)
-
- collist = ('id', 'rules')
- self.assertEqual(collist, columns)
-
- datalist = (identity_fakes.mapping_id,
- identity_fakes.MAPPING_RULES)
- self.assertEqual(datalist, data)
-
-
class TestMappingSet(TestMapping):
def setUp(self):
@@ -234,10 +201,44 @@ class TestMappingSet(TestMapping):
('mapping', identity_fakes.mapping_id),
('rules', identity_fakes.mapping_rules_file_path)
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args)
+
+
+class TestMappingShow(TestMapping):
+
+ def setUp(self):
+ super(TestMappingShow, self).setUp()
+
+ self.mapping_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.MAPPING_RESPONSE),
+ loaded=True
+ )
+
+ self.cmd = mapping.ShowMapping(self.app, None)
+
+ def test_mapping_show(self):
+ arglist = [
+ identity_fakes.mapping_id
+ ]
+ verifylist = [
+ ('mapping', identity_fakes.mapping_id)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.mapping_mock.get.assert_called_with(
+ identity_fakes.mapping_id)
+
+ collist = ('id', 'rules')
+ self.assertEqual(collist, columns)
+
+ datalist = (identity_fakes.mapping_id,
+ identity_fakes.MAPPING_RULES)
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_oauth.py b/openstackclient/tests/identity/v3/test_oauth.py
index dba6d034..d3cf3655 100644
--- a/openstackclient/tests/identity/v3/test_oauth.py
+++ b/openstackclient/tests/identity/v3/test_oauth.py
@@ -32,52 +32,52 @@ class TestOAuth1(identity_fakes.TestOAuth1):
self.roles_mock.reset_mock()
-class TestRequestTokenCreate(TestOAuth1):
+class TestAccessTokenCreate(TestOAuth1):
def setUp(self):
- super(TestRequestTokenCreate, self).setUp()
-
- self.request_tokens_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.OAUTH_REQUEST_TOKEN),
- loaded=True,
- )
+ super(TestAccessTokenCreate, self).setUp()
- self.projects_mock.get.return_value = fakes.FakeResource(
+ self.access_tokens_mock.create.return_value = fakes.FakeResource(
None,
- copy.deepcopy(identity_fakes.PROJECT),
+ copy.deepcopy(identity_fakes.OAUTH_ACCESS_TOKEN),
loaded=True,
)
- self.cmd = token.CreateRequestToken(self.app, None)
+ self.cmd = token.CreateAccessToken(self.app, None)
- def test_create_request_tokens(self):
+ def test_create_access_tokens(self):
arglist = [
'--consumer-key', identity_fakes.consumer_id,
'--consumer-secret', identity_fakes.consumer_secret,
- '--project', identity_fakes.project_id,
+ '--request-key', identity_fakes.request_token_id,
+ '--request-secret', identity_fakes.request_token_secret,
+ '--verifier', identity_fakes.oauth_verifier_pin,
]
verifylist = [
('consumer_key', identity_fakes.consumer_id),
('consumer_secret', identity_fakes.consumer_secret),
- ('project', identity_fakes.project_id),
+ ('request_key', identity_fakes.request_token_id),
+ ('request_secret', identity_fakes.request_token_secret),
+ ('verifier', identity_fakes.oauth_verifier_pin),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.request_tokens_mock.create.assert_called_with(
+ self.access_tokens_mock.create.assert_called_with(
identity_fakes.consumer_id,
identity_fakes.consumer_secret,
- identity_fakes.project_id,
+ identity_fakes.request_token_id,
+ identity_fakes.request_token_secret,
+ identity_fakes.oauth_verifier_pin,
)
collist = ('expires', 'id', 'key', 'secret')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.request_token_expires,
- identity_fakes.request_token_id,
- identity_fakes.request_token_id,
- identity_fakes.request_token_secret,
+ identity_fakes.access_token_expires,
+ identity_fakes.access_token_id,
+ identity_fakes.access_token_id,
+ identity_fakes.access_token_secret,
)
self.assertEqual(datalist, data)
@@ -123,51 +123,51 @@ class TestRequestTokenAuthorize(TestOAuth1):
self.assertEqual(datalist, data)
-class TestAccessTokenCreate(TestOAuth1):
+class TestRequestTokenCreate(TestOAuth1):
def setUp(self):
- super(TestAccessTokenCreate, self).setUp()
+ super(TestRequestTokenCreate, self).setUp()
- self.access_tokens_mock.create.return_value = fakes.FakeResource(
+ self.request_tokens_mock.create.return_value = fakes.FakeResource(
None,
- copy.deepcopy(identity_fakes.OAUTH_ACCESS_TOKEN),
+ copy.deepcopy(identity_fakes.OAUTH_REQUEST_TOKEN),
loaded=True,
)
- self.cmd = token.CreateAccessToken(self.app, None)
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ )
- def test_create_access_tokens(self):
+ self.cmd = token.CreateRequestToken(self.app, None)
+
+ def test_create_request_tokens(self):
arglist = [
'--consumer-key', identity_fakes.consumer_id,
'--consumer-secret', identity_fakes.consumer_secret,
- '--request-key', identity_fakes.request_token_id,
- '--request-secret', identity_fakes.request_token_secret,
- '--verifier', identity_fakes.oauth_verifier_pin,
+ '--project', identity_fakes.project_id,
]
verifylist = [
('consumer_key', identity_fakes.consumer_id),
('consumer_secret', identity_fakes.consumer_secret),
- ('request_key', identity_fakes.request_token_id),
- ('request_secret', identity_fakes.request_token_secret),
- ('verifier', identity_fakes.oauth_verifier_pin),
+ ('project', identity_fakes.project_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.access_tokens_mock.create.assert_called_with(
+ self.request_tokens_mock.create.assert_called_with(
identity_fakes.consumer_id,
identity_fakes.consumer_secret,
- identity_fakes.request_token_id,
- identity_fakes.request_token_secret,
- identity_fakes.oauth_verifier_pin,
+ identity_fakes.project_id,
)
collist = ('expires', 'id', 'key', 'secret')
self.assertEqual(collist, columns)
datalist = (
- identity_fakes.access_token_expires,
- identity_fakes.access_token_id,
- identity_fakes.access_token_id,
- identity_fakes.access_token_secret,
+ identity_fakes.request_token_expires,
+ identity_fakes.request_token_id,
+ identity_fakes.request_token_id,
+ identity_fakes.request_token_secret,
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_protocol.py b/openstackclient/tests/identity/v3/test_protocol.py
index 3c9c3f0a..238b0ff8 100644
--- a/openstackclient/tests/identity/v3/test_protocol.py
+++ b/openstackclient/tests/identity/v3/test_protocol.py
@@ -92,9 +92,12 @@ class TestProtocolDelete(TestProtocol):
('identity_provider', identity_fakes.idp_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+
self.protocols_mock.delete.assert_called_with(
identity_fakes.idp_id, identity_fakes.protocol_id)
+ self.assertIsNone(result)
class TestProtocolList(TestProtocol):
diff --git a/openstackclient/tests/identity/v3/test_role.py b/openstackclient/tests/identity/v3/test_role.py
index 19410deb..d2398e5d 100644
--- a/openstackclient/tests/identity/v3/test_role.py
+++ b/openstackclient/tests/identity/v3/test_role.py
@@ -306,11 +306,12 @@ class TestRoleDelete(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.roles_mock.delete.assert_called_with(
identity_fakes.role_id,
)
+ self.assertIsNone(result)
class TestRoleList(TestRole):
@@ -640,7 +641,7 @@ class TestRoleRemove(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -653,6 +654,7 @@ class TestRoleRemove(TestRole):
identity_fakes.role_id,
**kwargs
)
+ self.assertIsNone(result)
def test_role_remove_user_project(self):
arglist = [
@@ -672,7 +674,7 @@ class TestRoleRemove(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -685,6 +687,7 @@ class TestRoleRemove(TestRole):
identity_fakes.role_id,
**kwargs
)
+ self.assertIsNone(result)
def test_role_remove_group_domain(self):
arglist = [
@@ -705,7 +708,7 @@ class TestRoleRemove(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -718,6 +721,7 @@ class TestRoleRemove(TestRole):
identity_fakes.role_id,
**kwargs
)
+ self.assertIsNone(result)
def test_role_remove_group_project(self):
arglist = [
@@ -737,7 +741,7 @@ class TestRoleRemove(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -750,6 +754,7 @@ class TestRoleRemove(TestRole):
identity_fakes.role_id,
**kwargs
)
+ self.assertIsNone(result)
class TestRoleSet(TestRole):
@@ -778,7 +783,7 @@ class TestRoleSet(TestRole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -789,6 +794,7 @@ class TestRoleSet(TestRole):
identity_fakes.role_id,
**kwargs
)
+ self.assertIsNone(result)
class TestRoleShow(TestRole):
diff --git a/openstackclient/tests/identity/v3/test_service_provider.py b/openstackclient/tests/identity/v3/test_service_provider.py
index 39f779d0..80d60c5a 100644
--- a/openstackclient/tests/identity/v3/test_service_provider.py
+++ b/openstackclient/tests/identity/v3/test_service_provider.py
@@ -188,10 +188,13 @@ class TestServiceProviderDelete(TestServiceProvider):
('service_provider', service_fakes.sp_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+
+ result = self.cmd.take_action(parsed_args)
+
self.service_providers_mock.delete.assert_called_with(
service_fakes.sp_id,
)
+ self.assertIsNone(result)
class TestServiceProviderList(TestServiceProvider):
@@ -238,47 +241,6 @@ class TestServiceProviderList(TestServiceProvider):
self.assertEqual(tuple(data), datalist)
-class TestServiceProviderShow(TestServiceProvider):
-
- def setUp(self):
- super(TestServiceProviderShow, self).setUp()
-
- ret = fakes.FakeResource(
- None,
- copy.deepcopy(service_fakes.SERVICE_PROVIDER),
- loaded=True,
- )
- self.service_providers_mock.get.return_value = ret
- # Get the command object to test
- self.cmd = service_provider.ShowServiceProvider(self.app, None)
-
- def test_service_provider_show(self):
- arglist = [
- service_fakes.sp_id,
- ]
- verifylist = [
- ('service_provider', service_fakes.sp_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.service_providers_mock.get.assert_called_with(
- service_fakes.sp_id,
- )
-
- collist = ('auth_url', 'description', 'enabled', 'id', 'sp_url')
- self.assertEqual(collist, columns)
- datalist = (
- service_fakes.sp_auth_url,
- service_fakes.sp_description,
- True,
- service_fakes.sp_id,
- service_fakes.service_provider_url
- )
- self.assertEqual(data, datalist)
-
-
class TestServiceProviderSet(TestServiceProvider):
columns = (
@@ -417,3 +379,44 @@ class TestServiceProviderSet(TestServiceProvider):
# was set.
self.assertIsNone(columns)
self.assertIsNone(data)
+
+
+class TestServiceProviderShow(TestServiceProvider):
+
+ def setUp(self):
+ super(TestServiceProviderShow, self).setUp()
+
+ ret = fakes.FakeResource(
+ None,
+ copy.deepcopy(service_fakes.SERVICE_PROVIDER),
+ loaded=True,
+ )
+ self.service_providers_mock.get.return_value = ret
+ # Get the command object to test
+ self.cmd = service_provider.ShowServiceProvider(self.app, None)
+
+ def test_service_provider_show(self):
+ arglist = [
+ service_fakes.sp_id,
+ ]
+ verifylist = [
+ ('service_provider', service_fakes.sp_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.service_providers_mock.get.assert_called_with(
+ service_fakes.sp_id,
+ )
+
+ collist = ('auth_url', 'description', 'enabled', 'id', 'sp_url')
+ self.assertEqual(collist, columns)
+ datalist = (
+ service_fakes.sp_auth_url,
+ service_fakes.sp_description,
+ True,
+ service_fakes.sp_id,
+ service_fakes.service_provider_url
+ )
+ self.assertEqual(data, datalist)
diff --git a/openstackclient/tests/identity/v3/test_token.py b/openstackclient/tests/identity/v3/test_token.py
index 80c397bc..b68bc242 100644
--- a/openstackclient/tests/identity/v3/test_token.py
+++ b/openstackclient/tests/identity/v3/test_token.py
@@ -123,6 +123,7 @@ class TestTokenRevoke(TestToken):
verifylist = [('token', self.TOKEN)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.tokens_mock.revoke_token.assert_called_with(self.TOKEN)
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/identity/v3/test_trust.py b/openstackclient/tests/identity/v3/test_trust.py
index 2a748872..1ea2feb4 100644
--- a/openstackclient/tests/identity/v3/test_trust.py
+++ b/openstackclient/tests/identity/v3/test_trust.py
@@ -141,11 +141,12 @@ class TestTrustDelete(TestTrust):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.trusts_mock.delete.assert_called_with(
identity_fakes.trust_id,
)
+ self.assertIsNone(result)
class TestTrustList(TestTrust):
diff --git a/openstackclient/tests/identity/v3/test_unscoped_saml.py b/openstackclient/tests/identity/v3/test_unscoped_saml.py
index c2f14493..d12cb454 100644
--- a/openstackclient/tests/identity/v3/test_unscoped_saml.py
+++ b/openstackclient/tests/identity/v3/test_unscoped_saml.py
@@ -30,23 +30,23 @@ class TestUnscopedSAML(identity_fakes.TestFederatedIdentity):
self.domains_mock.reset_mock()
-class TestProjectList(TestUnscopedSAML):
+class TestDomainList(TestUnscopedSAML):
def setUp(self):
- super(TestProjectList, self).setUp()
+ super(TestDomainList, self).setUp()
- self.projects_mock.list.return_value = [
+ self.domains_mock.list.return_value = [
fakes.FakeResource(
None,
- copy.deepcopy(identity_fakes.PROJECT),
+ copy.deepcopy(identity_fakes.DOMAIN),
loaded=True,
),
]
# Get the command object to test
- self.cmd = unscoped_saml.ListAccessibleProjects(self.app, None)
+ self.cmd = unscoped_saml.ListAccessibleDomains(self.app, None)
- def test_accessible_projects_list(self):
+ def test_accessible_domains_list(self):
self.app.client_manager.auth_plugin_name = 'v3unscopedsaml'
arglist = []
verifylist = []
@@ -57,19 +57,19 @@ class TestProjectList(TestUnscopedSAML):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.projects_mock.list.assert_called_with()
+ self.domains_mock.list.assert_called_with()
- collist = ('ID', 'Domain ID', 'Enabled', 'Name')
+ collist = ('ID', 'Enabled', 'Name', 'Description')
self.assertEqual(collist, columns)
datalist = ((
- identity_fakes.project_id,
identity_fakes.domain_id,
True,
- identity_fakes.project_name,
+ identity_fakes.domain_name,
+ identity_fakes.domain_description,
), )
self.assertEqual(datalist, tuple(data))
- def test_accessible_projects_list_wrong_auth(self):
+ def test_accessible_domains_list_wrong_auth(self):
auth = identity_fakes.FakeAuth("wrong auth")
self.app.client_manager.identity.session.auth = auth
arglist = []
@@ -81,23 +81,23 @@ class TestProjectList(TestUnscopedSAML):
parsed_args)
-class TestDomainList(TestUnscopedSAML):
+class TestProjectList(TestUnscopedSAML):
def setUp(self):
- super(TestDomainList, self).setUp()
+ super(TestProjectList, self).setUp()
- self.domains_mock.list.return_value = [
+ self.projects_mock.list.return_value = [
fakes.FakeResource(
None,
- copy.deepcopy(identity_fakes.DOMAIN),
+ copy.deepcopy(identity_fakes.PROJECT),
loaded=True,
),
]
# Get the command object to test
- self.cmd = unscoped_saml.ListAccessibleDomains(self.app, None)
+ self.cmd = unscoped_saml.ListAccessibleProjects(self.app, None)
- def test_accessible_domains_list(self):
+ def test_accessible_projects_list(self):
self.app.client_manager.auth_plugin_name = 'v3unscopedsaml'
arglist = []
verifylist = []
@@ -108,19 +108,19 @@ class TestDomainList(TestUnscopedSAML):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.domains_mock.list.assert_called_with()
+ self.projects_mock.list.assert_called_with()
- collist = ('ID', 'Enabled', 'Name', 'Description')
+ collist = ('ID', 'Domain ID', 'Enabled', 'Name')
self.assertEqual(collist, columns)
datalist = ((
+ identity_fakes.project_id,
identity_fakes.domain_id,
True,
- identity_fakes.domain_name,
- identity_fakes.domain_description,
+ identity_fakes.project_name,
), )
self.assertEqual(datalist, tuple(data))
- def test_accessible_domains_list_wrong_auth(self):
+ def test_accessible_projects_list_wrong_auth(self):
auth = identity_fakes.FakeAuth("wrong auth")
self.app.client_manager.identity.session.auth = auth
arglist = []
diff --git a/openstackclient/tests/identity/v3/test_user.py b/openstackclient/tests/identity/v3/test_user.py
index 571e2d79..5dafa772 100644
--- a/openstackclient/tests/identity/v3/test_user.py
+++ b/openstackclient/tests/identity/v3/test_user.py
@@ -499,11 +499,12 @@ class TestUserDelete(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.users_mock.delete.assert_called_with(
identity_fakes.user_id,
)
+ self.assertIsNone(result)
class TestUserList(TestUser):
@@ -774,7 +775,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -787,6 +788,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_password(self):
arglist = [
@@ -805,7 +807,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -818,6 +820,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_password_prompt(self):
arglist = [
@@ -839,7 +842,7 @@ class TestUserSet(TestUser):
mocker = mock.Mock()
mocker.return_value = 'abc123'
with mock.patch("openstackclient.common.utils.get_password", mocker):
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -852,6 +855,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_email(self):
arglist = [
@@ -869,7 +873,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -882,6 +886,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_project(self):
arglist = [
@@ -899,7 +904,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -912,6 +917,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_project_domain(self):
arglist = [
@@ -931,7 +937,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -944,6 +950,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_enable(self):
arglist = [
@@ -961,7 +968,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -973,6 +980,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
def test_user_set_disable(self):
arglist = [
@@ -990,7 +998,7 @@ class TestUserSet(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
@@ -1002,6 +1010,7 @@ class TestUserSet(TestUser):
identity_fakes.user_id,
**kwargs
)
+ self.assertIsNone(result)
class TestUserSetPassword(TestUser):
@@ -1030,11 +1039,12 @@ class TestUserSetPassword(TestUser):
# Mock getting user current password.
with self._mock_get_password(current_pass):
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.users_mock.update_password.assert_called_with(
current_pass, new_pass
)
+ self.assertIsNone(result)
def test_user_create_password_prompt(self):
current_pass = 'old_pass'
@@ -1043,11 +1053,12 @@ class TestUserSetPassword(TestUser):
# Mock getting user current and new password.
with self._mock_get_password(current_pass, new_pass):
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.users_mock.update_password.assert_called_with(
current_pass, new_pass
)
+ self.assertIsNone(result)
def test_user_password_change_no_prompt(self):
current_pass = 'old_pass'
@@ -1062,11 +1073,12 @@ class TestUserSetPassword(TestUser):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.cmd.take_action(parsed_args)
+ result = self.cmd.take_action(parsed_args)
self.users_mock.update_password.assert_called_with(
current_pass, new_pass
)
+ self.assertIsNone(result)
class TestUserShow(TestUser):
diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py
index 9e6bf97f..7f89ef7a 100644
--- a/openstackclient/tests/network/v2/fakes.py
+++ b/openstackclient/tests/network/v2/fakes.py
@@ -152,6 +152,7 @@ class FakeNetwork(object):
'router_external': True,
'availability_zones': [],
'availability_zone_hints': [],
+ 'is_default': False,
}
# Overwrite default attributes.
@@ -161,7 +162,7 @@ class FakeNetwork(object):
network_methods = {
'keys': ['id', 'name', 'admin_state_up', 'router_external',
'status', 'subnets', 'tenant_id', 'availability_zones',
- 'availability_zone_hints'],
+ 'availability_zone_hints', 'is_default'],
}
# Overwrite default methods.
@@ -419,7 +420,7 @@ class FakeSecurityGroup(object):
"""Fake one or more security groups."""
@staticmethod
- def create_one_security_group(attrs={}, methods={}):
+ def create_one_security_group(attrs=None, methods=None):
"""Create a fake security group.
:param Dictionary attrs:
@@ -429,6 +430,11 @@ class FakeSecurityGroup(object):
:return:
A FakeResource object, with id, name, etc.
"""
+ if attrs is None:
+ attrs = {}
+ if methods is None:
+ methods = {}
+
# Set default attributes.
security_group_attrs = {
'id': 'security-group-id-' + uuid.uuid4().hex,
@@ -442,7 +448,10 @@ class FakeSecurityGroup(object):
security_group_attrs.update(attrs)
# Set default methods.
- security_group_methods = {}
+ security_group_methods = {
+ 'keys': ['id', 'name', 'description', 'tenant_id',
+ 'security_group_rules'],
+ }
# Overwrite default methods.
security_group_methods.update(methods)
@@ -451,10 +460,14 @@ class FakeSecurityGroup(object):
info=copy.deepcopy(security_group_attrs),
methods=copy.deepcopy(security_group_methods),
loaded=True)
+
+ # Set attributes with special mapping in OpenStack SDK.
+ security_group.project_id = security_group_attrs['tenant_id']
+
return security_group
@staticmethod
- def create_security_groups(attrs={}, methods={}, count=2):
+ def create_security_groups(attrs=None, methods=None, count=2):
"""Create multiple fake security groups.
:param Dictionary attrs:
@@ -478,7 +491,7 @@ class FakeSecurityGroupRule(object):
"""Fake one or more security group rules."""
@staticmethod
- def create_one_security_group_rule(attrs={}, methods={}):
+ def create_one_security_group_rule(attrs=None, methods=None):
"""Create a fake security group rule.
:param Dictionary attrs:
@@ -488,16 +501,21 @@ class FakeSecurityGroupRule(object):
:return:
A FakeResource object, with id, etc.
"""
+ if attrs is None:
+ attrs = {}
+ if methods is None:
+ methods = {}
+
# Set default attributes.
security_group_rule_attrs = {
'direction': 'ingress',
'ethertype': 'IPv4',
'id': 'security-group-rule-id-' + uuid.uuid4().hex,
- 'port_range_max': None,
- 'port_range_min': None,
- 'protocol': None,
- 'remote_group_id': 'remote-security-group-id-' + uuid.uuid4().hex,
- 'remote_ip_prefix': None,
+ 'port_range_max': 0,
+ 'port_range_min': 0,
+ 'protocol': 'tcp',
+ 'remote_group_id': None,
+ 'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': 'security-group-id-' + uuid.uuid4().hex,
'tenant_id': 'project-id-' + uuid.uuid4().hex,
}
@@ -520,13 +538,13 @@ class FakeSecurityGroupRule(object):
methods=copy.deepcopy(security_group_rule_methods),
loaded=True)
- # Set attributes with special mappings.
+ # Set attributes with special mapping in OpenStack SDK.
security_group_rule.project_id = security_group_rule_attrs['tenant_id']
return security_group_rule
@staticmethod
- def create_security_group_rules(attrs={}, methods={}, count=2):
+ def create_security_group_rules(attrs=None, methods=None, count=2):
"""Create multiple fake security group rules.
:param Dictionary attrs:
@@ -573,7 +591,7 @@ class FakeSubnet(object):
'dns_nameservers': [],
'allocation_pools': [],
'host_routes': [],
- 'ip_version': '4',
+ 'ip_version': 4,
'gateway_ip': '10.10.10.1',
'ipv6_address_mode': 'None',
'ipv6_ra_mode': 'None',
@@ -734,15 +752,15 @@ class FakeSubnetPool(object):
'id': 'subnet-pool-id-' + uuid.uuid4().hex,
'name': 'subnet-pool-name-' + uuid.uuid4().hex,
'prefixes': ['10.0.0.0/24', '10.1.0.0/24'],
- 'default_prefixlen': 8,
+ 'default_prefixlen': '8',
'address_scope_id': 'address-scope-id-' + uuid.uuid4().hex,
'tenant_id': 'project-id-' + uuid.uuid4().hex,
'is_default': False,
'shared': False,
- 'max_prefixlen': 32,
- 'min_prefixlen': 8,
+ 'max_prefixlen': '32',
+ 'min_prefixlen': '8',
'default_quota': None,
- 'ip_version': 4,
+ 'ip_version': '4',
}
# Overwrite default attributes.
diff --git a/openstackclient/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py
index 1c1088a3..3e261fb5 100644
--- a/openstackclient/tests/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/network/v2/test_floating_ip.py
@@ -16,6 +16,7 @@ import mock
from openstackclient.network.v2 import floating_ip
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests.network.v2 import fakes as network_fakes
+from openstackclient.tests import utils as tests_utils
# Tests for Neutron network
@@ -29,6 +30,115 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2):
self.network = self.app.client_manager.network
+class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
+
+ # Fake data for option tests.
+ floating_network = network_fakes.FakeNetwork.create_one_network()
+ subnet = network_fakes.FakeSubnet.create_one_subnet()
+ port = network_fakes.FakePort.create_one_port()
+
+ # The floating ip to be deleted.
+ floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
+ attrs={
+ 'floating_network_id': floating_network.id,
+ 'port_id': port.id,
+ }
+ )
+
+ columns = (
+ 'dns_domain',
+ 'dns_name',
+ 'fixed_ip_address',
+ 'floating_ip_address',
+ 'floating_network_id',
+ 'id',
+ 'port_id',
+ 'project_id',
+ 'router_id',
+ 'status',
+ )
+
+ data = (
+ floating_ip.dns_domain,
+ floating_ip.dns_name,
+ floating_ip.fixed_ip_address,
+ floating_ip.floating_ip_address,
+ floating_ip.floating_network_id,
+ floating_ip.id,
+ floating_ip.port_id,
+ floating_ip.project_id,
+ floating_ip.router_id,
+ floating_ip.status,
+ )
+
+ def setUp(self):
+ super(TestCreateFloatingIPNetwork, self).setUp()
+
+ self.network.create_ip = mock.Mock(return_value=self.floating_ip)
+
+ self.network.find_network = mock.Mock(
+ return_value=self.floating_network)
+ self.network.find_subnet = mock.Mock(return_value=self.subnet)
+ self.network.find_port = mock.Mock(return_value=self.port)
+
+ # Get the command object to test
+ self.cmd = floating_ip.CreateFloatingIP(self.app, self.namespace)
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_default_options(self):
+ arglist = [
+ self.floating_ip.floating_network_id,
+ ]
+ verifylist = [
+ ('network', self.floating_ip.floating_network_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_ip.assert_called_once_with(**{
+ 'floating_network_id': self.floating_ip.floating_network_id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--subnet', self.subnet.id,
+ '--port', self.floating_ip.port_id,
+ '--floating-ip-address', self.floating_ip.floating_ip_address,
+ '--fixed-ip-address', self.floating_ip.fixed_ip_address,
+ self.floating_ip.floating_network_id,
+ ]
+ verifylist = [
+ ('subnet', self.subnet.id),
+ ('port', self.floating_ip.port_id),
+ ('floating_ip_address', self.floating_ip.floating_ip_address),
+ ('fixed_ip_address', self.floating_ip.fixed_ip_address),
+ ('network', self.floating_ip.floating_network_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_ip.assert_called_once_with(**{
+ 'subnet_id': self.subnet.id,
+ 'port_id': self.floating_ip.port_id,
+ 'floating_ip_address': self.floating_ip.floating_ip_address,
+ 'fixed_ip_address': self.floating_ip.fixed_ip_address,
+ 'floating_network_id': self.floating_ip.floating_network_id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork):
# The floating ip to be deleted.
@@ -54,8 +164,8 @@ class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork):
result = self.cmd.take_action(parsed_args)
- self.network.find_ip.assert_called_with(self.floating_ip.id)
- self.network.delete_ip.assert_called_with(self.floating_ip)
+ self.network.find_ip.assert_called_once_with(self.floating_ip.id)
+ self.network.delete_ip.assert_called_once_with(self.floating_ip)
self.assertIsNone(result)
@@ -95,7 +205,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.ips.assert_called_with(**{})
+ self.network.ips.assert_called_once_with(**{})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -150,7 +260,7 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_ip.assert_called_with(
+ self.network.find_ip.assert_called_once_with(
self.floating_ip.id,
ignore_missing=False
)
@@ -169,6 +279,62 @@ class TestFloatingIPCompute(compute_fakes.TestComputev2):
self.compute = self.app.client_manager.compute
+class TestCreateFloatingIPCompute(TestFloatingIPCompute):
+
+ # The floating ip to be deleted.
+ floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip()
+
+ columns = (
+ 'fixed_ip',
+ 'id',
+ 'instance_id',
+ 'ip',
+ 'pool',
+ )
+
+ data = (
+ floating_ip.fixed_ip,
+ floating_ip.id,
+ floating_ip.instance_id,
+ floating_ip.ip,
+ floating_ip.pool,
+ )
+
+ def setUp(self):
+ super(TestCreateFloatingIPCompute, self).setUp()
+
+ self.app.client_manager.network_endpoint_enabled = False
+
+ self.compute.floating_ips.create.return_value = self.floating_ip
+
+ # Get the command object to test
+ self.cmd = floating_ip.CreateFloatingIP(self.app, None)
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_default_options(self):
+ arglist = [
+ self.floating_ip.pool,
+ ]
+ verifylist = [
+ ('network', self.floating_ip.pool),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.floating_ips.create.assert_called_once_with(
+ self.floating_ip.pool)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
class TestDeleteFloatingIPCompute(TestFloatingIPCompute):
# The floating ip to be deleted.
@@ -198,7 +364,7 @@ class TestDeleteFloatingIPCompute(TestFloatingIPCompute):
result = self.cmd.take_action(parsed_args)
- self.compute.floating_ips.delete.assert_called_with(
+ self.compute.floating_ips.delete.assert_called_once_with(
self.floating_ip.id
)
self.assertIsNone(result)
@@ -244,7 +410,7 @@ class TestListFloatingIPCompute(TestFloatingIPCompute):
columns, data = self.cmd.take_action(parsed_args)
- self.compute.floating_ips.list.assert_called_with()
+ self.compute.floating_ips.list.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py
index e70a66c1..0dec0e2f 100644
--- a/openstackclient/tests/network/v2/test_network.py
+++ b/openstackclient/tests/network/v2/test_network.py
@@ -51,6 +51,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'availability_zone_hints',
'availability_zones',
'id',
+ 'is_default',
'name',
'project_id',
'router_external',
@@ -63,6 +64,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
utils.format_list(_network.availability_zone_hints),
utils.format_list(_network.availability_zones),
_network.id,
+ _network.is_default,
_network.name,
_network.project_id,
network._format_router_external(_network.router_external),
@@ -116,15 +118,16 @@ class TestCreateNetworkIdentityV3(TestNetwork):
]
verifylist = [
('name', self._network.name),
- ('admin_state', True),
- ('shared', None),
+ ('enable', True),
+ ('share', None),
('project', None),
+ ('external', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.network.create_network.assert_called_with(**{
+ self.network.create_network.assert_called_once_with(**{
'admin_state_up': True,
'name': self._network.name,
})
@@ -138,26 +141,40 @@ class TestCreateNetworkIdentityV3(TestNetwork):
"--project", identity_fakes_v3.project_name,
"--project-domain", identity_fakes_v3.domain_name,
"--availability-zone-hint", "nova",
+ "--external", "--default",
+ "--provider-network-type", "vlan",
+ "--provider-physical-network", "physnet1",
+ "--provider-segment", "400",
self._network.name,
]
verifylist = [
- ('admin_state', False),
- ('shared', True),
+ ('disable', True),
+ ('share', True),
('project', identity_fakes_v3.project_name),
('project_domain', identity_fakes_v3.domain_name),
('availability_zone_hints', ["nova"]),
+ ('external', True),
+ ('default', True),
+ ('provider_network_type', 'vlan'),
+ ('physical_network', 'physnet1'),
+ ('segmentation_id', '400'),
('name', self._network.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
- self.network.create_network.assert_called_with(**{
+ self.network.create_network.assert_called_once_with(**{
'admin_state_up': False,
'availability_zone_hints': ["nova"],
'name': self._network.name,
'shared': True,
'tenant_id': identity_fakes_v3.project_id,
+ 'is_default': True,
+ 'router:external': True,
+ 'provider:network_type': 'vlan',
+ 'provider:physical_network': 'physnet1',
+ 'provider:segmentation_id': '400',
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -169,15 +186,16 @@ class TestCreateNetworkIdentityV3(TestNetwork):
self._network.name,
]
verifylist = [
- ('admin_state', True),
- ('shared', False),
+ ('enable', True),
+ ('no_share', True),
('name', self._network.name),
+ ('external', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.network.create_network.assert_called_with(**{
+ self.network.create_network.assert_called_once_with(**{
'admin_state_up': True,
'name': self._network.name,
'shared': False,
@@ -198,6 +216,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'availability_zone_hints',
'availability_zones',
'id',
+ 'is_default',
'name',
'project_id',
'router_external',
@@ -210,6 +229,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
utils.format_list(_network.availability_zone_hints),
utils.format_list(_network.availability_zones),
_network.id,
+ _network.is_default,
_network.name,
_network.project_id,
network._format_router_external(_network.router_external),
@@ -249,16 +269,17 @@ class TestCreateNetworkIdentityV2(TestNetwork):
self._network.name,
]
verifylist = [
- ('admin_state', True),
- ('shared', None),
+ ('enable', True),
+ ('share', None),
('name', self._network.name),
('project', identity_fakes_v2.project_name),
+ ('external', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.network.create_network.assert_called_with(**{
+ self.network.create_network.assert_called_once_with(**{
'admin_state_up': True,
'name': self._network.name,
'tenant_id': identity_fakes_v2.project_id,
@@ -273,11 +294,12 @@ class TestCreateNetworkIdentityV2(TestNetwork):
self._network.name,
]
verifylist = [
- ('admin_state', True),
- ('shared', None),
+ ('enable', True),
+ ('share', None),
('project', identity_fakes_v3.project_name),
('project_domain', identity_fakes_v3.domain_name),
('name', self._network.name),
+ ('external', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -315,7 +337,7 @@ class TestDeleteNetwork(TestNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_network.assert_called_with(self._network)
+ self.network.delete_network.assert_called_once_with(self._network)
self.assertIsNone(result)
@@ -386,7 +408,7 @@ class TestListNetwork(TestNetwork):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.network.networks.assert_called_with()
+ self.network.networks.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -405,7 +427,7 @@ class TestListNetwork(TestNetwork):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.network.networks.assert_called_with(
+ self.network.networks.assert_called_once_with(
**{'router:external': True}
)
self.assertEqual(self.columns, columns)
@@ -426,7 +448,7 @@ class TestListNetwork(TestNetwork):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.network.networks.assert_called_with()
+ self.network.networks.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
@@ -455,9 +477,9 @@ class TestSetNetwork(TestNetwork):
]
verifylist = [
('network', self._network.name),
- ('admin_state', True),
+ ('enable', True),
('name', 'noob'),
- ('shared', True),
+ ('share', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -468,7 +490,8 @@ class TestSetNetwork(TestNetwork):
'admin_state_up': True,
'shared': True,
}
- self.network.update_network.assert_called_with(self._network, **attrs)
+ self.network.update_network.assert_called_once_with(
+ self._network, **attrs)
self.assertIsNone(result)
def test_set_that(self):
@@ -479,8 +502,8 @@ class TestSetNetwork(TestNetwork):
]
verifylist = [
('network', self._network.name),
- ('admin_state', False),
- ('shared', False),
+ ('disable', True),
+ ('no_share', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -490,7 +513,8 @@ class TestSetNetwork(TestNetwork):
'admin_state_up': False,
'shared': False,
}
- self.network.update_network.assert_called_with(self._network, **attrs)
+ self.network.update_network.assert_called_once_with(
+ self._network, **attrs)
self.assertIsNone(result)
def test_set_nothing(self):
@@ -512,6 +536,7 @@ class TestShowNetwork(TestNetwork):
'availability_zone_hints',
'availability_zones',
'id',
+ 'is_default',
'name',
'project_id',
'router_external',
@@ -524,6 +549,7 @@ class TestShowNetwork(TestNetwork):
utils.format_list(_network.availability_zone_hints),
utils.format_list(_network.availability_zones),
_network.id,
+ _network.is_default,
_network.name,
_network.project_id,
network._format_router_external(_network.router_external),
@@ -558,8 +584,8 @@ class TestShowNetwork(TestNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_network.assert_called_with(self._network.name,
- ignore_missing=False)
+ self.network.find_network.assert_called_once_with(
+ self._network.name, ignore_missing=False)
self.assertEqual(tuple(self.columns), columns)
self.assertEqual(list(self.data), list(data))
@@ -682,7 +708,7 @@ class TestCreateNetworkCompute(TestNetworkCompute):
columns, data = self.cmd.take_action(parsed_args)
- self.compute.networks.create.assert_called_with(**{
+ self.compute.networks.create.assert_called_once_with(**{
'cidr': self._network.cidr,
'label': self._network.label,
})
@@ -719,7 +745,7 @@ class TestDeleteNetworkCompute(TestNetworkCompute):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.compute.networks.delete.assert_called_with(self._network.id)
+ self.compute.networks.delete.assert_called_once_with(self._network.id)
self.assertIsNone(result)
@@ -765,7 +791,7 @@ class TestListNetworkCompute(TestNetworkCompute):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.compute.networks.list.assert_called_with()
+ self.compute.networks.list.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py
index 30e290c6..3b1a641a 100644
--- a/openstackclient/tests/network/v2/test_port.py
+++ b/openstackclient/tests/network/v2/test_port.py
@@ -103,14 +103,14 @@ class TestCreatePort(TestPort):
]
verifylist = [
('network', self._port.network_id,),
- ('admin_state', True),
+ ('enable', True),
('name', 'test-port'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
- self.network.create_port.assert_called_with(**{
+ self.network.create_port.assert_called_once_with(**{
'admin_state_up': True,
'network_id': self._port.network_id,
'name': 'test-port',
@@ -125,7 +125,7 @@ class TestCreatePort(TestPort):
'--mac-address', 'aa:aa:aa:aa:aa:aa',
'--fixed-ip', 'subnet=%s,ip-address=10.0.0.2'
% self.fake_subnet.id,
- '--device-id', 'deviceid',
+ '--device', 'deviceid',
'--device-owner', 'fakeowner',
'--disable',
'--vnic-type', 'macvtap',
@@ -141,9 +141,9 @@ class TestCreatePort(TestPort):
'fixed_ip',
[{'subnet': self.fake_subnet.id, 'ip-address': '10.0.0.2'}]
),
- ('device_id', 'deviceid'),
+ ('device', 'deviceid'),
('device_owner', 'fakeowner'),
- ('admin_state', False),
+ ('disable', True),
('vnic_type', 'macvtap'),
('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
('network', self._port.network_id),
@@ -154,7 +154,7 @@ class TestCreatePort(TestPort):
columns, data = (self.cmd.take_action(parsed_args))
- self.network.create_port.assert_called_with(**{
+ self.network.create_port.assert_called_once_with(**{
'mac_address': 'aa:aa:aa:aa:aa:aa',
'fixed_ips': [{'subnet_id': self.fake_subnet.id,
'ip_address': '10.0.0.2'}],
@@ -195,7 +195,7 @@ class TestDeletePort(TestPort):
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.network.delete_port.assert_called_once_with(self._port)
self.assertIsNone(result)
@@ -224,8 +224,11 @@ class TestListPort(TestPort):
# Get the command object to test
self.cmd = port.ListPort(self.app, self.namespace)
-
self.network.ports = mock.Mock(return_value=self._ports)
+ fake_router = network_fakes.FakeRouter.create_one_router({
+ 'id': 'fake-router-id',
+ })
+ self.network.find_router = mock.Mock(return_value=fake_router)
def test_port_list_no_options(self):
arglist = []
@@ -235,10 +238,117 @@ class TestListPort(TestPort):
columns, data = self.cmd.take_action(parsed_args)
- self.network.ports.assert_called_with()
+ self.network.ports.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_port_list_router_opt(self):
+ arglist = [
+ '--router', 'fake-router-name',
+ ]
+
+ verifylist = [
+ ('router', 'fake-router-name')
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.ports.assert_called_once_with(**{
+ 'device_id': 'fake-router-id'
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+
+class TestSetPort(TestPort):
+
+ _port = network_fakes.FakePort.create_one_port()
+
+ def setUp(self):
+ super(TestSetPort, self).setUp()
+
+ self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet()
+ self.network.find_subnet = mock.Mock(return_value=self.fake_subnet)
+ self.network.find_port = mock.Mock(return_value=self._port)
+ self.network.update_port = mock.Mock(return_value=None)
+
+ # Get the command object to test
+ self.cmd = port.SetPort(self.app, self.namespace)
+
+ def test_set_fixed_ip(self):
+ arglist = [
+ '--fixed-ip', 'ip-address=10.0.0.11',
+ self._port.name,
+ ]
+ verifylist = [
+ ('fixed_ip', [{'ip-address': '10.0.0.11'}]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'fixed_ips': [{'ip_address': '10.0.0.11'}],
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_this(self):
+ arglist = [
+ '--disable',
+ '--no-fixed-ip',
+ '--no-binding-profile',
+ self._port.name,
+ ]
+ verifylist = [
+ ('disable', True),
+ ('no_binding_profile', True),
+ ('no_fixed_ip', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'admin_state_up': False,
+ 'binding:profile': {},
+ 'fixed_ips': [],
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_that(self):
+ arglist = [
+ '--enable',
+ '--vnic-type', 'macvtap',
+ '--binding-profile', 'foo=bar',
+ '--host', 'binding-host-id-xxxx',
+ '--name', 'newName',
+ self._port.name,
+ ]
+ verifylist = [
+ ('enable', True),
+ ('vnic_type', 'macvtap'),
+ ('binding_profile', {'foo': 'bar'}),
+ ('host', 'binding-host-id-xxxx'),
+ ('name', 'newName')
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'admin_state_up': True,
+ 'binding:vnic_type': 'macvtap',
+ 'binding:profile': {'foo': 'bar'},
+ 'binding:host_id': 'binding-host-id-xxxx',
+ 'name': 'newName',
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
class TestShowPort(TestPort):
@@ -271,8 +381,8 @@ class TestShowPort(TestPort):
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_port.assert_called_with(self._port.name,
- ignore_missing=False)
+ self.network.find_port.assert_called_once_with(
+ self._port.name, ignore_missing=False)
ref_columns, ref_data = self._get_common_cols_data(self._port)
self.assertEqual(ref_columns, columns)
diff --git a/openstackclient/tests/network/v2/test_router.py b/openstackclient/tests/network/v2/test_router.py
index 40941fbc..5d3e80d7 100644
--- a/openstackclient/tests/network/v2/test_router.py
+++ b/openstackclient/tests/network/v2/test_router.py
@@ -114,17 +114,16 @@ class TestCreateRouter(TestRouter):
]
verifylist = [
('name', self.new_router.name),
- ('admin_state_up', True),
+ ('enable', True),
('distributed', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
- self.network.create_router.assert_called_with(**{
+ self.network.create_router.assert_called_once_with(**{
'admin_state_up': True,
'name': self.new_router.name,
- 'distributed': False,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -138,16 +137,15 @@ class TestCreateRouter(TestRouter):
verifylist = [
('name', self.new_router.name),
('availability_zone_hints', ['fake-az', 'fake-az2']),
- ('admin_state_up', True),
+ ('enable', True),
('distributed', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
- self.network.create_router.assert_called_with(**{
+ self.network.create_router.assert_called_once_with(**{
'admin_state_up': True,
'name': self.new_router.name,
- 'distributed': False,
'availability_zone_hints': ['fake-az', 'fake-az2'],
})
@@ -180,7 +178,7 @@ class TestDeleteRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_router.assert_called_with(self._router)
+ self.network.delete_router.assert_called_once_with(self._router)
self.assertIsNone(result)
@@ -246,7 +244,7 @@ class TestListRouter(TestRouter):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.network.routers.assert_called_with()
+ self.network.routers.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -264,7 +262,7 @@ class TestListRouter(TestRouter):
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.network.routers.assert_called_with()
+ self.network.routers.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
@@ -333,7 +331,7 @@ class TestSetRouter(TestRouter):
]
verifylist = [
('router', self._router.name),
- ('admin_state_up', True),
+ ('enable', True),
('distributed', True),
('name', 'noob'),
]
@@ -346,7 +344,8 @@ class TestSetRouter(TestRouter):
'distributed': True,
'name': 'noob',
}
- self.network.update_router.assert_called_with(self._router, **attrs)
+ self.network.update_router.assert_called_once_with(
+ self._router, **attrs)
self.assertIsNone(result)
def test_set_that(self):
@@ -357,8 +356,8 @@ class TestSetRouter(TestRouter):
]
verifylist = [
('router', self._router.name),
- ('admin_state_up', False),
- ('distributed', False),
+ ('disable', True),
+ ('centralized', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -368,7 +367,8 @@ class TestSetRouter(TestRouter):
'admin_state_up': False,
'distributed': False,
}
- self.network.update_router.assert_called_with(self._router, **attrs)
+ self.network.update_router.assert_called_once_with(
+ self._router, **attrs)
self.assertIsNone(result)
def test_set_distributed_centralized(self):
@@ -405,7 +405,8 @@ class TestSetRouter(TestRouter):
'routes': [{'destination': '10.20.30.0/24',
'gateway': '10.20.30.1'}],
}
- self.network.update_router.assert_called_with(self._router, **attrs)
+ self.network.update_router.assert_called_once_with(
+ self._router, **attrs)
self.assertIsNone(result)
def test_set_clear_routes(self):
@@ -424,7 +425,8 @@ class TestSetRouter(TestRouter):
attrs = {
'routes': [],
}
- self.network.update_router.assert_called_with(self._router, **attrs)
+ self.network.update_router.assert_called_once_with(
+ self._router, **attrs)
self.assertIsNone(result)
def test_set_route_clear_routes(self):
@@ -503,7 +505,7 @@ class TestShowRouter(TestRouter):
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_router.assert_called_with(self._router.name,
- ignore_missing=False)
+ self.network.find_router.assert_called_once_with(
+ self._router.name, ignore_missing=False)
self.assertEqual(tuple(self.columns), columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_security_group.py b/openstackclient/tests/network/v2/test_security_group.py
index b8114cbc..dd6a3d41 100644
--- a/openstackclient/tests/network/v2/test_security_group.py
+++ b/openstackclient/tests/network/v2/test_security_group.py
@@ -11,10 +11,13 @@
# under the License.
#
+import copy
import mock
from openstackclient.network.v2 import security_group
from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils as tests_utils
@@ -37,6 +40,191 @@ class TestSecurityGroupCompute(compute_fakes.TestComputev2):
self.compute = self.app.client_manager.compute
+class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
+
+ # The security group to be created.
+ _security_group = \
+ network_fakes.FakeSecurityGroup.create_one_security_group()
+
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'rules',
+ )
+
+ data = (
+ _security_group.description,
+ _security_group.id,
+ _security_group.name,
+ _security_group.project_id,
+ '',
+ )
+
+ def setUp(self):
+ super(TestCreateSecurityGroupNetwork, self).setUp()
+
+ self.network.create_security_group = mock.Mock(
+ return_value=self._security_group)
+
+ # Set identity client v3. And get a shortcut to Identity client.
+ identity_client = identity_fakes.FakeIdentityv3Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+ self.app.client_manager.identity = identity_client
+ self.identity = self.app.client_manager.identity
+
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.identity.projects
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ )
+
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.identity.domains
+ self.domains_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = security_group.CreateSecurityGroup(self.app, self.namespace)
+
+ def test_create_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_create_min_options(self):
+ arglist = [
+ self._security_group.name,
+ ]
+ verifylist = [
+ ('name', self._security_group.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_security_group.assert_called_once_with(**{
+ 'description': self._security_group.name,
+ 'name': self._security_group.name,
+ })
+ self.assertEqual(tuple(self.columns), columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--description', self._security_group.description,
+ '--project', identity_fakes.project_name,
+ '--project-domain', identity_fakes.domain_name,
+ self._security_group.name,
+ ]
+ verifylist = [
+ ('description', self._security_group.description),
+ ('name', self._security_group.name),
+ ('project', identity_fakes.project_name),
+ ('project_domain', identity_fakes.domain_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_security_group.assert_called_once_with(**{
+ 'description': self._security_group.description,
+ 'name': self._security_group.name,
+ 'tenant_id': identity_fakes.project_id,
+ })
+ self.assertEqual(tuple(self.columns), columns)
+ self.assertEqual(self.data, data)
+
+
+class TestCreateSecurityGroupCompute(TestSecurityGroupCompute):
+
+ # The security group to be shown.
+ _security_group = \
+ compute_fakes.FakeSecurityGroup.create_one_security_group()
+
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'rules',
+ )
+
+ data = (
+ _security_group.description,
+ _security_group.id,
+ _security_group.name,
+ _security_group.tenant_id,
+ '',
+ )
+
+ def setUp(self):
+ super(TestCreateSecurityGroupCompute, self).setUp()
+
+ self.app.client_manager.network_endpoint_enabled = False
+
+ self.compute.security_groups.create.return_value = self._security_group
+
+ # Get the command object to test
+ self.cmd = security_group.CreateSecurityGroup(self.app, None)
+
+ def test_create_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_create_network_options(self):
+ arglist = [
+ '--project', identity_fakes.project_name,
+ '--project-domain', identity_fakes.domain_name,
+ self._security_group.name,
+ ]
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_create_min_options(self):
+ arglist = [
+ self._security_group.name,
+ ]
+ verifylist = [
+ ('name', self._security_group.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.security_groups.create.assert_called_once_with(
+ self._security_group.name,
+ self._security_group.name)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--description', self._security_group.description,
+ self._security_group.name,
+ ]
+ verifylist = [
+ ('description', self._security_group.description),
+ ('name', self._security_group.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.security_groups.create.assert_called_once_with(
+ self._security_group.name,
+ self._security_group.description)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork):
# The security group to be deleted.
@@ -65,7 +253,7 @@ class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_security_group.assert_called_with(
+ self.network.delete_security_group.assert_called_once_with(
self._security_group)
self.assertIsNone(result)
@@ -100,7 +288,7 @@ class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.compute.security_groups.delete.assert_called_with(
+ self.compute.security_groups.delete.assert_called_once_with(
self._security_group.id)
self.assertIsNone(result)
@@ -143,7 +331,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.security_groups.assert_called_with()
+ self.network.security_groups.assert_called_once_with()
self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, tuple(data))
@@ -158,7 +346,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.security_groups.assert_called_with()
+ self.network.security_groups.assert_called_once_with()
self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, tuple(data))
@@ -212,7 +400,7 @@ class TestListSecurityGroupCompute(TestSecurityGroupCompute):
columns, data = self.cmd.take_action(parsed_args)
kwargs = {'search_opts': {'all_tenants': False}}
- self.compute.security_groups.list.assert_called_with(**kwargs)
+ self.compute.security_groups.list.assert_called_once_with(**kwargs)
self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, tuple(data))
@@ -228,7 +416,7 @@ class TestListSecurityGroupCompute(TestSecurityGroupCompute):
columns, data = self.cmd.take_action(parsed_args)
kwargs = {'search_opts': {'all_tenants': True}}
- self.compute.security_groups.list.assert_called_with(**kwargs)
+ self.compute.security_groups.list.assert_called_once_with(**kwargs)
self.assertEqual(self.expected_columns_all_projects, columns)
self.assertEqual(self.expected_data_all_projects, tuple(data))
@@ -363,3 +551,122 @@ class TestSetSecurityGroupCompute(TestSecurityGroupCompute):
new_description
)
self.assertIsNone(result)
+
+
+class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
+
+ # The security group rule to be shown with the group.
+ _security_group_rule = \
+ network_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
+
+ # The security group to be shown.
+ _security_group = \
+ network_fakes.FakeSecurityGroup.create_one_security_group(
+ attrs={'security_group_rules': [_security_group_rule._info]}
+ )
+
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'rules',
+ )
+
+ data = (
+ _security_group.description,
+ _security_group.id,
+ _security_group.name,
+ _security_group.project_id,
+ security_group._format_network_security_group_rules(
+ [_security_group_rule._info]),
+ )
+
+ def setUp(self):
+ super(TestShowSecurityGroupNetwork, self).setUp()
+
+ self.network.find_security_group = mock.Mock(
+ return_value=self._security_group)
+
+ # Get the command object to test
+ self.cmd = security_group.ShowSecurityGroup(self.app, self.namespace)
+
+ def test_show_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_show_all_options(self):
+ arglist = [
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.find_security_group.assert_called_once_with(
+ self._security_group.id, ignore_missing=False)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestShowSecurityGroupCompute(TestSecurityGroupCompute):
+
+ # The security group rule to be shown with the group.
+ _security_group_rule = \
+ compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
+
+ # The security group to be shown.
+ _security_group = \
+ compute_fakes.FakeSecurityGroup.create_one_security_group(
+ attrs={'rules': [_security_group_rule._info]}
+ )
+
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'rules',
+ )
+
+ data = (
+ _security_group.description,
+ _security_group.id,
+ _security_group.name,
+ _security_group.tenant_id,
+ security_group._format_compute_security_group_rules(
+ [_security_group_rule._info]),
+ )
+
+ def setUp(self):
+ super(TestShowSecurityGroupCompute, self).setUp()
+
+ self.app.client_manager.network_endpoint_enabled = False
+
+ self.compute.security_groups.get.return_value = self._security_group
+
+ # Get the command object to test
+ self.cmd = security_group.ShowSecurityGroup(self.app, None)
+
+ def test_show_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_show_all_options(self):
+ arglist = [
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.security_groups.get.assert_called_once_with(
+ self._security_group.id)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_security_group_rule.py b/openstackclient/tests/network/v2/test_security_group_rule.py
index db15d0e2..81b9e18b 100644
--- a/openstackclient/tests/network/v2/test_security_group_rule.py
+++ b/openstackclient/tests/network/v2/test_security_group_rule.py
@@ -39,6 +39,317 @@ class TestSecurityGroupRuleCompute(compute_fakes.TestComputev2):
self.compute = self.app.client_manager.compute
+class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
+
+ # The security group rule to be created.
+ _security_group_rule = None
+
+ # The security group that will contain the rule created.
+ _security_group = \
+ network_fakes.FakeSecurityGroup.create_one_security_group()
+
+ expected_columns = (
+ 'direction',
+ 'ethertype',
+ 'id',
+ 'port_range_max',
+ 'port_range_min',
+ 'project_id',
+ 'protocol',
+ 'remote_group_id',
+ 'remote_ip_prefix',
+ 'security_group_id',
+ )
+
+ expected_data = None
+
+ def _setup_security_group_rule(self, attrs=None):
+ self._security_group_rule = \
+ network_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
+ attrs)
+ self.network.create_security_group_rule = mock.Mock(
+ return_value=self._security_group_rule)
+ self.expected_data = (
+ self._security_group_rule.direction,
+ self._security_group_rule.ethertype,
+ self._security_group_rule.id,
+ self._security_group_rule.port_range_max,
+ self._security_group_rule.port_range_min,
+ self._security_group_rule.project_id,
+ self._security_group_rule.protocol,
+ self._security_group_rule.remote_group_id,
+ self._security_group_rule.remote_ip_prefix,
+ self._security_group_rule.security_group_id,
+ )
+
+ def setUp(self):
+ super(TestCreateSecurityGroupRuleNetwork, self).setUp()
+
+ self.network.find_security_group = mock.Mock(
+ return_value=self._security_group)
+
+ # Get the command object to test
+ self.cmd = security_group_rule.CreateSecurityGroupRule(
+ self.app, self.namespace)
+
+ def test_create_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_create_source_group_and_ip(self):
+ arglist = [
+ '--src-ip', '10.10.0.0/24',
+ '--src-group', self._security_group.id,
+ self._security_group.id,
+ ]
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_create_bad_protocol(self):
+ arglist = [
+ '--protocol', 'foo',
+ self._security_group.id,
+ ]
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_create_default_rule(self):
+ self._setup_security_group_rule({
+ 'port_range_max': 443,
+ 'port_range_min': 443,
+ })
+ arglist = [
+ '--dst-port', str(self._security_group_rule.port_range_min),
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_security_group_rule.assert_called_once_with(**{
+ 'direction': self._security_group_rule.direction,
+ 'ethertype': self._security_group_rule.ethertype,
+ 'port_range_max': self._security_group_rule.port_range_max,
+ 'port_range_min': self._security_group_rule.port_range_min,
+ 'protocol': self._security_group_rule.protocol,
+ 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
+ 'security_group_id': self._security_group.id,
+ })
+ self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_data, data)
+
+ def test_create_source_group(self):
+ self._setup_security_group_rule({
+ 'port_range_max': 22,
+ 'port_range_min': 22,
+ 'remote_group_id': self._security_group.id,
+ })
+ arglist = [
+ '--dst-port', str(self._security_group_rule.port_range_min),
+ '--src-group', self._security_group.name,
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('dst_port', (self._security_group_rule.port_range_min,
+ self._security_group_rule.port_range_max)),
+ ('src_group', self._security_group.name),
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_security_group_rule.assert_called_once_with(**{
+ 'direction': self._security_group_rule.direction,
+ 'ethertype': self._security_group_rule.ethertype,
+ 'port_range_max': self._security_group_rule.port_range_max,
+ 'port_range_min': self._security_group_rule.port_range_min,
+ 'protocol': self._security_group_rule.protocol,
+ 'remote_group_id': self._security_group_rule.remote_group_id,
+ 'security_group_id': self._security_group.id,
+ })
+ self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_data, data)
+
+ def test_create_source_ip(self):
+ self._setup_security_group_rule({
+ 'protocol': 'icmp',
+ 'port_range_max': -1,
+ 'port_range_min': -1,
+ 'remote_ip_prefix': '10.0.2.0/24',
+ })
+ arglist = [
+ '--proto', self._security_group_rule.protocol,
+ '--src-ip', self._security_group_rule.remote_ip_prefix,
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('proto', self._security_group_rule.protocol),
+ ('src_ip', self._security_group_rule.remote_ip_prefix),
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_security_group_rule.assert_called_once_with(**{
+ 'direction': self._security_group_rule.direction,
+ 'ethertype': self._security_group_rule.ethertype,
+ 'protocol': self._security_group_rule.protocol,
+ 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
+ 'security_group_id': self._security_group.id,
+ })
+ self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_data, data)
+
+
+class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
+
+ # The security group rule to be created.
+ _security_group_rule = None
+
+ # The security group that will contain the rule created.
+ _security_group = \
+ compute_fakes.FakeSecurityGroup.create_one_security_group()
+
+ def _setup_security_group_rule(self, attrs=None):
+ self._security_group_rule = \
+ compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
+ attrs)
+ self.compute.security_group_rules.create.return_value = \
+ self._security_group_rule
+ expected_columns, expected_data = \
+ security_group_rule._format_security_group_rule_show(
+ self._security_group_rule._info)
+ return expected_columns, expected_data
+
+ def setUp(self):
+ super(TestCreateSecurityGroupRuleCompute, self).setUp()
+
+ self.app.client_manager.network_endpoint_enabled = False
+
+ self.compute.security_groups.get.return_value = self._security_group
+
+ # Get the command object to test
+ self.cmd = security_group_rule.CreateSecurityGroupRule(self.app, None)
+
+ def test_create_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_create_source_group_and_ip(self):
+ arglist = [
+ '--src-ip', '10.10.0.0/24',
+ '--src-group', self._security_group.id,
+ self._security_group.id,
+ ]
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_create_bad_protocol(self):
+ arglist = [
+ '--protocol', 'foo',
+ self._security_group.id,
+ ]
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_create_default_rule(self):
+ expected_columns, expected_data = self._setup_security_group_rule()
+ dst_port = str(self._security_group_rule.from_port) + ':' + \
+ str(self._security_group_rule.to_port)
+ arglist = [
+ '--dst-port', dst_port,
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('dst_port', (self._security_group_rule.from_port,
+ self._security_group_rule.to_port)),
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.security_group_rules.create.assert_called_once_with(
+ self._security_group.id,
+ self._security_group_rule.ip_protocol,
+ self._security_group_rule.from_port,
+ self._security_group_rule.to_port,
+ self._security_group_rule.ip_range['cidr'],
+ None,
+ )
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, data)
+
+ def test_create_source_group(self):
+ expected_columns, expected_data = self._setup_security_group_rule({
+ 'from_port': 22,
+ 'to_port': 22,
+ 'group': {'name': self._security_group.name},
+ })
+ arglist = [
+ '--dst-port', str(self._security_group_rule.from_port),
+ '--src-group', self._security_group.name,
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('dst_port', (self._security_group_rule.from_port,
+ self._security_group_rule.to_port)),
+ ('src_group', self._security_group.name),
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.security_group_rules.create.assert_called_once_with(
+ self._security_group.id,
+ self._security_group_rule.ip_protocol,
+ self._security_group_rule.from_port,
+ self._security_group_rule.to_port,
+ self._security_group_rule.ip_range['cidr'],
+ self._security_group.id,
+ )
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, data)
+
+ def test_create_source_ip(self):
+ expected_columns, expected_data = self._setup_security_group_rule({
+ 'ip_protocol': 'icmp',
+ 'from_port': -1,
+ 'to_port': -1,
+ 'ip_range': {'cidr': '10.0.2.0/24'},
+ })
+ arglist = [
+ '--proto', self._security_group_rule.ip_protocol,
+ '--src-ip', self._security_group_rule.ip_range['cidr'],
+ self._security_group.id,
+ ]
+ verifylist = [
+ ('proto', self._security_group_rule.ip_protocol),
+ ('src_ip', self._security_group_rule.ip_range['cidr']),
+ ('group', self._security_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.compute.security_group_rules.create.assert_called_once_with(
+ self._security_group.id,
+ self._security_group_rule.ip_protocol,
+ self._security_group_rule.from_port,
+ self._security_group_rule.to_port,
+ self._security_group_rule.ip_range['cidr'],
+ None,
+ )
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, data)
+
+
class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
# The security group rule to be deleted.
@@ -68,7 +379,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
result = self.cmd.take_action(parsed_args)
- self.network.delete_security_group_rule.assert_called_with(
+ self.network.delete_security_group_rule.assert_called_once_with(
self._security_group_rule)
self.assertIsNone(result)
@@ -98,7 +409,7 @@ class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
result = self.cmd.take_action(parsed_args)
- self.compute.security_group_rules.delete.assert_called_with(
+ self.compute.security_group_rules.delete.assert_called_once_with(
self._security_group_rule.id)
self.assertIsNone(result)
@@ -160,7 +471,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_security_group_rule.assert_called_with(
+ self.network.find_security_group_rule.assert_called_once_with(
self._security_group_rule.id, ignore_missing=False)
self.assertEqual(tuple(self.columns), columns)
self.assertEqual(self.data, data)
@@ -207,6 +518,6 @@ class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
columns, data = self.cmd.take_action(parsed_args)
- self.compute.security_groups.list.assert_called_with()
+ self.compute.security_groups.list.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py
index a95635ff..2535bbe6 100644
--- a/openstackclient/tests/network/v2/test_subnet.py
+++ b/openstackclient/tests/network/v2/test_subnet.py
@@ -11,10 +11,14 @@
# under the License.
#
+import copy
import mock
+from openstackclient.common import exceptions
from openstackclient.common import utils
from openstackclient.network.v2 import subnet as subnet_v2
+from openstackclient.tests import fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils as tests_utils
@@ -28,6 +32,333 @@ class TestSubnet(network_fakes.TestNetworkV2):
self.network = self.app.client_manager.network
+class TestCreateSubnet(TestSubnet):
+
+ # An IPv4 subnet to be created with mostly default values
+ _subnet = network_fakes.FakeSubnet.create_one_subnet(
+ attrs={
+ 'tenant_id': identity_fakes_v3.project_id,
+ }
+ )
+
+ # Subnet pool to be used to create a subnet from a pool
+ _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+
+ # An IPv4 subnet to be created using a specific subnet pool
+ _subnet_from_pool = network_fakes.FakeSubnet.create_one_subnet(
+ attrs={
+ 'tenant_id': identity_fakes_v3.project_id,
+ 'subnetpool_id': _subnet_pool.id,
+ 'dns_nameservers': ['8.8.8.8',
+ '8.8.4.4'],
+ 'host_routes': [{'destination': '10.20.20.0/24',
+ 'nexthop': '10.20.20.1'},
+ {'destination': '10.30.30.0/24',
+ 'nexthop': '10.30.30.1'}],
+ }
+ )
+
+ # An IPv6 subnet to be created with most options specified
+ _subnet_ipv6 = network_fakes.FakeSubnet.create_one_subnet(
+ attrs={
+ 'tenant_id': identity_fakes_v3.project_id,
+ 'cidr': 'fe80:0:0:a00a::/64',
+ 'enable_dhcp': True,
+ 'dns_nameservers': ['fe80:27ff:a00a:f00f::ffff',
+ 'fe80:37ff:a00a:f00f::ffff'],
+ 'allocation_pools': [{'start': 'fe80::a00a:0:c0de:0:100',
+ 'end': 'fe80::a00a:0:c0de:0:f000'},
+ {'start': 'fe80::a00a:0:c0de:1:100',
+ 'end': 'fe80::a00a:0:c0de:1:f000'}],
+ 'host_routes': [{'destination': 'fe80:27ff:a00a:f00f::/64',
+ 'nexthop': 'fe80:27ff:a00a:f00f::1'},
+ {'destination': 'fe80:37ff:a00a:f00f::/64',
+ 'nexthop': 'fe80:37ff:a00a:f00f::1'}],
+ 'ip_version': 6,
+ 'gateway_ip': 'fe80::a00a:0:c0de:0:1',
+ 'ipv6_address_mode': 'slaac',
+ 'ipv6_ra_mode': 'slaac',
+ 'subnetpool_id': 'None',
+ }
+ )
+
+ # The network to be returned from find_network
+ _network = network_fakes.FakeNetwork.create_one_network(
+ attrs={
+ 'id': _subnet.network_id,
+ }
+ )
+
+ columns = (
+ 'allocation_pools',
+ 'cidr',
+ 'dns_nameservers',
+ 'enable_dhcp',
+ 'gateway_ip',
+ 'host_routes',
+ 'id',
+ 'ip_version',
+ 'ipv6_address_mode',
+ 'ipv6_ra_mode',
+ 'name',
+ 'network_id',
+ 'project_id',
+ 'subnetpool_id',
+ )
+
+ data = (
+ subnet_v2._format_allocation_pools(_subnet.allocation_pools),
+ _subnet.cidr,
+ utils.format_list(_subnet.dns_nameservers),
+ _subnet.enable_dhcp,
+ _subnet.gateway_ip,
+ subnet_v2._format_host_routes(_subnet.host_routes),
+ _subnet.id,
+ _subnet.ip_version,
+ _subnet.ipv6_address_mode,
+ _subnet.ipv6_ra_mode,
+ _subnet.name,
+ _subnet.network_id,
+ _subnet.project_id,
+ _subnet.subnetpool_id,
+ )
+
+ data_subnet_pool = (
+ subnet_v2._format_allocation_pools(_subnet_from_pool.allocation_pools),
+ _subnet_from_pool.cidr,
+ utils.format_list(_subnet_from_pool.dns_nameservers),
+ _subnet_from_pool.enable_dhcp,
+ _subnet_from_pool.gateway_ip,
+ subnet_v2._format_host_routes(_subnet_from_pool.host_routes),
+ _subnet_from_pool.id,
+ _subnet_from_pool.ip_version,
+ _subnet_from_pool.ipv6_address_mode,
+ _subnet_from_pool.ipv6_ra_mode,
+ _subnet_from_pool.name,
+ _subnet_from_pool.network_id,
+ _subnet_from_pool.project_id,
+ _subnet_from_pool.subnetpool_id,
+ )
+
+ data_ipv6 = (
+ subnet_v2._format_allocation_pools(_subnet_ipv6.allocation_pools),
+ _subnet_ipv6.cidr,
+ utils.format_list(_subnet_ipv6.dns_nameservers),
+ _subnet_ipv6.enable_dhcp,
+ _subnet_ipv6.gateway_ip,
+ subnet_v2._format_host_routes(_subnet_ipv6.host_routes),
+ _subnet_ipv6.id,
+ _subnet_ipv6.ip_version,
+ _subnet_ipv6.ipv6_address_mode,
+ _subnet_ipv6.ipv6_ra_mode,
+ _subnet_ipv6.name,
+ _subnet_ipv6.network_id,
+ _subnet_ipv6.project_id,
+ _subnet_ipv6.subnetpool_id,
+ )
+
+ def setUp(self):
+ super(TestCreateSubnet, self).setUp()
+
+ # Get the command object to test
+ self.cmd = subnet_v2.CreateSubnet(self.app, self.namespace)
+
+ # Set identity client v3. And get a shortcut to Identity client.
+ identity_client = identity_fakes_v3.FakeIdentityv3Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+ self.app.client_manager.identity = identity_client
+ self.identity = self.app.client_manager.identity
+
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.identity.projects
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes_v3.PROJECT),
+ loaded=True,
+ )
+
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.identity.domains
+ self.domains_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes_v3.DOMAIN),
+ loaded=True,
+ )
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Testing that a call without the required argument will fail and
+ # throw a "ParserExecption"
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, verifylist)
+
+ def test_create_default_options(self):
+ # Mock create_subnet and find_network sdk calls to return the
+ # values we want for this test
+ self.network.create_subnet = mock.Mock(return_value=self._subnet)
+ self._network.id = self._subnet.network_id
+ self.network.find_network = mock.Mock(return_value=self._network)
+
+ arglist = [
+ "--subnet-range", self._subnet.cidr,
+ "--network", self._subnet.network_id,
+ self._subnet.name,
+ ]
+ verifylist = [
+ ('name', self._subnet.name),
+ ('subnet_range', self._subnet.cidr),
+ ('network', self._subnet.network_id),
+ ('ip_version', self._subnet.ip_version),
+ ('gateway', 'auto'),
+
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_subnet.assert_called_once_with(**{
+ 'cidr': self._subnet.cidr,
+ 'enable_dhcp': self._subnet.enable_dhcp,
+ 'ip_version': self._subnet.ip_version,
+ 'name': self._subnet.name,
+ 'network_id': self._subnet.network_id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_from_subnet_pool_options(self):
+ # Mock create_subnet, find_subnet_pool, and find_network sdk calls
+ # to return the values we want for this test
+ self.network.create_subnet = \
+ mock.Mock(return_value=self._subnet_from_pool)
+ self._network.id = self._subnet_from_pool.network_id
+ self.network.find_network = mock.Mock(return_value=self._network)
+ self.network.find_subnet_pool = \
+ mock.Mock(return_value=self._subnet_pool)
+
+ arglist = [
+ self._subnet_from_pool.name,
+ "--subnet-pool", self._subnet_from_pool.subnetpool_id,
+ "--prefix-length", '24',
+ "--network", self._subnet_from_pool.network_id,
+ "--ip-version", str(self._subnet_from_pool.ip_version),
+ "--gateway", self._subnet_from_pool.gateway_ip,
+ "--dhcp",
+ ]
+
+ for dns_addr in self._subnet_from_pool.dns_nameservers:
+ arglist.append('--dns-nameserver')
+ arglist.append(dns_addr)
+
+ for host_route in self._subnet_from_pool.host_routes:
+ arglist.append('--host-route')
+ value = 'gateway=' + host_route.get('nexthop', '') + \
+ ',destination=' + host_route.get('destination', '')
+ arglist.append(value)
+
+ verifylist = [
+ ('name', self._subnet_from_pool.name),
+ ('prefix_length', '24'),
+ ('network', self._subnet_from_pool.network_id),
+ ('ip_version', self._subnet_from_pool.ip_version),
+ ('gateway', self._subnet_from_pool.gateway_ip),
+ ('dns_nameservers', self._subnet_from_pool.dns_nameservers),
+ ('dhcp', self._subnet_from_pool.enable_dhcp),
+ ('host_routes', subnet_v2.convert_entries_to_gateway(
+ self._subnet_from_pool.host_routes)),
+ ('subnet_pool', self._subnet_from_pool.subnetpool_id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_subnet.assert_called_once_with(**{
+ 'dns_nameservers': self._subnet_from_pool.dns_nameservers,
+ 'enable_dhcp': self._subnet_from_pool.enable_dhcp,
+ 'gateway_ip': self._subnet_from_pool.gateway_ip,
+ 'host_routes': self._subnet_from_pool.host_routes,
+ 'ip_version': self._subnet_from_pool.ip_version,
+ 'name': self._subnet_from_pool.name,
+ 'network_id': self._subnet_from_pool.network_id,
+ 'prefixlen': '24',
+ 'subnetpool_id': self._subnet_from_pool.subnetpool_id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data_subnet_pool, data)
+
+ def test_create_options_subnet_range_ipv6(self):
+ # Mock create_subnet and find_network sdk calls to return the
+ # values we want for this test
+ self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6)
+ self._network.id = self._subnet_ipv6.network_id
+ self.network.find_network = mock.Mock(return_value=self._network)
+
+ arglist = [
+ self._subnet_ipv6.name,
+ "--subnet-range", self._subnet_ipv6.cidr,
+ "--network", self._subnet_ipv6.network_id,
+ "--ip-version", str(self._subnet_ipv6.ip_version),
+ "--ipv6-ra-mode", self._subnet_ipv6.ipv6_ra_mode,
+ "--ipv6-address-mode", self._subnet_ipv6.ipv6_address_mode,
+ "--gateway", self._subnet_ipv6.gateway_ip,
+ "--dhcp",
+ ]
+
+ for dns_addr in self._subnet_ipv6.dns_nameservers:
+ arglist.append('--dns-nameserver')
+ arglist.append(dns_addr)
+
+ for host_route in self._subnet_ipv6.host_routes:
+ arglist.append('--host-route')
+ value = 'gateway=' + host_route.get('nexthop', '') + \
+ ',destination=' + host_route.get('destination', '')
+ arglist.append(value)
+
+ for pool in self._subnet_ipv6.allocation_pools:
+ arglist.append('--allocation-pool')
+ value = 'start=' + pool.get('start', '') + \
+ ',end=' + pool.get('end', '')
+ arglist.append(value)
+
+ verifylist = [
+ ('name', self._subnet_ipv6.name),
+ ('subnet_range', self._subnet_ipv6.cidr),
+ ('network', self._subnet_ipv6.network_id),
+ ('ip_version', self._subnet_ipv6.ip_version),
+ ('ipv6_ra_mode', self._subnet_ipv6.ipv6_ra_mode),
+ ('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode),
+ ('gateway', self._subnet_ipv6.gateway_ip),
+ ('dns_nameservers', self._subnet_ipv6.dns_nameservers),
+ ('dhcp', self._subnet_ipv6.enable_dhcp),
+ ('host_routes', subnet_v2.convert_entries_to_gateway(
+ self._subnet_ipv6.host_routes)),
+ ('allocation_pools', self._subnet_ipv6.allocation_pools),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_subnet.assert_called_once_with(**{
+ 'cidr': self._subnet_ipv6.cidr,
+ 'dns_nameservers': self._subnet_ipv6.dns_nameservers,
+ 'enable_dhcp': self._subnet_ipv6.enable_dhcp,
+ 'gateway_ip': self._subnet_ipv6.gateway_ip,
+ 'host_routes': self._subnet_ipv6.host_routes,
+ 'ip_version': self._subnet_ipv6.ip_version,
+ 'ipv6_address_mode': self._subnet_ipv6.ipv6_address_mode,
+ 'ipv6_ra_mode': self._subnet_ipv6.ipv6_ra_mode,
+ 'name': self._subnet_ipv6.name,
+ 'network_id': self._subnet_ipv6.network_id,
+ 'allocation_pools': self._subnet_ipv6.allocation_pools,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data_ipv6, data)
+
+
class TestDeleteSubnet(TestSubnet):
# The subnet to delete.
@@ -53,7 +384,7 @@ class TestDeleteSubnet(TestSubnet):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.network.delete_subnet.assert_called_with(self._subnet)
+ self.network.delete_subnet.assert_called_once_with(self._subnet)
self.assertIsNone(result)
@@ -65,7 +396,7 @@ class TestListSubnet(TestSubnet):
'ID',
'Name',
'Network',
- 'Subnet'
+ 'Subnet',
)
columns_long = columns + (
'Project',
@@ -74,7 +405,7 @@ class TestListSubnet(TestSubnet):
'Allocation Pools',
'Host Routes',
'IP Version',
- 'Gateway'
+ 'Gateway',
)
data = []
@@ -99,7 +430,7 @@ class TestListSubnet(TestSubnet):
subnet_v2._format_allocation_pools(subnet.allocation_pools),
utils.format_list(subnet.host_routes),
subnet.ip_version,
- subnet.gateway_ip
+ subnet.gateway_ip,
))
def setUp(self):
@@ -119,7 +450,7 @@ class TestListSubnet(TestSubnet):
columns, data = self.cmd.take_action(parsed_args)
- self.network.subnets.assert_called_with()
+ self.network.subnets.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -134,11 +465,78 @@ class TestListSubnet(TestSubnet):
columns, data = self.cmd.take_action(parsed_args)
- self.network.subnets.assert_called_with()
+ self.network.subnets.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
+class TestSetSubnet(TestSubnet):
+
+ _subnet = network_fakes.FakeSubnet.create_one_subnet()
+
+ def setUp(self):
+ super(TestSetSubnet, self).setUp()
+ self.network.update_subnet = mock.Mock(return_value=None)
+ self.network.find_subnet = mock.Mock(return_value=self._subnet)
+ self.cmd = subnet_v2.SetSubnet(self.app, self.namespace)
+
+ def test_set_this(self):
+ arglist = [
+ "--name", "new_subnet",
+ "--dhcp",
+ "--gateway", self._subnet.gateway_ip,
+ self._subnet.name,
+ ]
+ verifylist = [
+ ('name', "new_subnet"),
+ ('dhcp', True),
+ ('gateway', self._subnet.gateway_ip),
+ ('subnet', self._subnet.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'enable_dhcp': True,
+ 'gateway_ip': self._subnet.gateway_ip,
+ 'name': "new_subnet",
+ }
+ self.network.update_subnet.assert_called_with(self._subnet, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_that(self):
+ arglist = [
+ "--name", "new_subnet",
+ "--no-dhcp",
+ "--gateway", "none",
+ self._subnet.name,
+ ]
+ verifylist = [
+ ('name', "new_subnet"),
+ ('no_dhcp', True),
+ ('gateway', "none"),
+ ('subnet', self._subnet.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'enable_dhcp': False,
+ 'gateway_ip': None,
+ 'name': "new_subnet",
+ }
+ self.network.update_subnet.assert_called_with(self._subnet, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_nothing(self):
+ arglist = [self._subnet.name, ]
+ verifylist = [('subnet', self._subnet.name)]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+
class TestShowSubnet(TestSubnet):
# The subnets to be shown
_subnet = network_fakes.FakeSubnet.create_one_subnet()
@@ -205,8 +603,8 @@ class TestShowSubnet(TestSubnet):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_subnet.assert_called_with(self._subnet.name,
- ignore_missing=False)
+ self.network.find_subnet.assert_called_once_with(
+ self._subnet.name, ignore_missing=False)
self.assertEqual(self.columns, columns)
self.assertEqual(list(self.data), list(data))
diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py
index c4e3340d..093e26c6 100644
--- a/openstackclient/tests/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/network/v2/test_subnet_pool.py
@@ -11,10 +11,15 @@
# under the License.
#
+import argparse
+import copy
import mock
+from openstackclient.common import exceptions
from openstackclient.common import utils
from openstackclient.network.v2 import subnet_pool
+from openstackclient.tests import fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils as tests_utils
@@ -28,6 +33,167 @@ class TestSubnetPool(network_fakes.TestNetworkV2):
self.network = self.app.client_manager.network
+class TestCreateSubnetPool(TestSubnetPool):
+
+ # The new subnet pool to create.
+ _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+
+ columns = (
+ 'address_scope_id',
+ 'default_prefixlen',
+ 'default_quota',
+ 'id',
+ 'ip_version',
+ 'is_default',
+ 'max_prefixlen',
+ 'min_prefixlen',
+ 'name',
+ 'prefixes',
+ 'project_id',
+ 'shared',
+ )
+ data = (
+ _subnet_pool.address_scope_id,
+ _subnet_pool.default_prefixlen,
+ _subnet_pool.default_quota,
+ _subnet_pool.id,
+ _subnet_pool.ip_version,
+ _subnet_pool.is_default,
+ _subnet_pool.max_prefixlen,
+ _subnet_pool.min_prefixlen,
+ _subnet_pool.name,
+ utils.format_list(_subnet_pool.prefixes),
+ _subnet_pool.project_id,
+ _subnet_pool.shared,
+ )
+
+ def setUp(self):
+ super(TestCreateSubnetPool, self).setUp()
+
+ self.network.create_subnet_pool = mock.Mock(
+ return_value=self._subnet_pool)
+
+ # Get the command object to test
+ self.cmd = subnet_pool.CreateSubnetPool(self.app, self.namespace)
+
+ # Set identity client. And get a shortcut to Identity client.
+ identity_client = identity_fakes_v3.FakeIdentityv3Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+ self.app.client_manager.identity = identity_client
+ self.identity = self.app.client_manager.identity
+
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.identity.projects
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes_v3.PROJECT),
+ loaded=True,
+ )
+
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.identity.domains
+ self.domains_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes_v3.DOMAIN),
+ loaded=True,
+ )
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_default_options(self):
+ arglist = [
+ '--pool-prefix', '10.0.10.0/24',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('prefixes', ['10.0.10.0/24']),
+ ('name', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_subnet_pool.assert_called_once_with(**{
+ 'prefixes': ['10.0.10.0/24'],
+ 'name': self._subnet_pool.name,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_prefixlen_options(self):
+ arglist = [
+ '--default-prefix-length', self._subnet_pool.default_prefixlen,
+ '--max-prefix-length', self._subnet_pool.max_prefixlen,
+ '--min-prefix-length', self._subnet_pool.min_prefixlen,
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('default_prefix_length', self._subnet_pool.default_prefixlen),
+ ('max_prefix_length', self._subnet_pool.max_prefixlen),
+ ('min_prefix_length', self._subnet_pool.min_prefixlen),
+ ('name', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_subnet_pool.assert_called_once_with(**{
+ 'default_prefixlen': self._subnet_pool.default_prefixlen,
+ 'max_prefixlen': self._subnet_pool.max_prefixlen,
+ 'min_prefixlen': self._subnet_pool.min_prefixlen,
+ 'prefixes': [],
+ 'name': self._subnet_pool.name,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_len_negative(self):
+ arglist = [
+ self._subnet_pool.name,
+ '--min-prefix-length', '-16',
+ ]
+ verifylist = [
+ ('subnet_pool', self._subnet_pool.name),
+ ('min_prefix_length', '-16'),
+ ]
+
+ self.assertRaises(argparse.ArgumentTypeError, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_project_domain(self):
+ arglist = [
+ '--pool-prefix', '10.0.10.0/24',
+ "--project", identity_fakes_v3.project_name,
+ "--project-domain", identity_fakes_v3.domain_name,
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('prefixes', ['10.0.10.0/24']),
+ ('project', identity_fakes_v3.project_name),
+ ('project_domain', identity_fakes_v3.domain_name),
+ ('name', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_subnet_pool.assert_called_once_with(**{
+ 'prefixes': ['10.0.10.0/24'],
+ 'tenant_id': identity_fakes_v3.project_id,
+ 'name': self._subnet_pool.name,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
class TestDeleteSubnetPool(TestSubnetPool):
# The subnet pool to delete.
@@ -56,7 +222,8 @@ class TestDeleteSubnetPool(TestSubnetPool):
result = self.cmd.take_action(parsed_args)
- self.network.delete_subnet_pool.assert_called_with(self._subnet_pool)
+ self.network.delete_subnet_pool.assert_called_once_with(
+ self._subnet_pool)
self.assertIsNone(result)
@@ -109,7 +276,7 @@ class TestListSubnetPool(TestSubnetPool):
columns, data = self.cmd.take_action(parsed_args)
- self.network.subnet_pools.assert_called_with()
+ self.network.subnet_pools.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -124,11 +291,101 @@ class TestListSubnetPool(TestSubnetPool):
columns, data = self.cmd.take_action(parsed_args)
- self.network.subnet_pools.assert_called_with()
+ self.network.subnet_pools.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
+class TestSetSubnetPool(TestSubnetPool):
+
+ # The subnet_pool to set.
+ _subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+
+ def setUp(self):
+ super(TestSetSubnetPool, self).setUp()
+
+ self.network.update_subnet_pool = mock.Mock(return_value=None)
+
+ self.network.find_subnet_pool = mock.Mock(
+ return_value=self._subnet_pool)
+
+ # Get the command object to test
+ self.cmd = subnet_pool.SetSubnetPool(self.app, self.namespace)
+
+ def test_set_this(self):
+ arglist = [
+ self._subnet_pool.name,
+ '--name', 'noob',
+ '--default-prefix-length', '8',
+ '--min-prefix-length', '8',
+ ]
+ verifylist = [
+ ('subnet_pool', self._subnet_pool.name),
+ ('name', 'noob'),
+ ('default_prefix_length', '8'),
+ ('min_prefix_length', '8'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'name': 'noob',
+ 'default_prefixlen': '8',
+ 'min_prefixlen': '8',
+ }
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_that(self):
+ arglist = [
+ self._subnet_pool.name,
+ '--pool-prefix', '10.0.1.0/24',
+ '--pool-prefix', '10.0.2.0/24',
+ '--max-prefix-length', '16',
+ ]
+ verifylist = [
+ ('subnet_pool', self._subnet_pool.name),
+ ('prefixes', ['10.0.1.0/24', '10.0.2.0/24']),
+ ('max_prefix_length', '16'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ prefixes = ['10.0.1.0/24', '10.0.2.0/24']
+ prefixes.extend(self._subnet_pool.prefixes)
+ attrs = {
+ 'prefixes': prefixes,
+ 'max_prefixlen': '16',
+ }
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_nothing(self):
+ arglist = [self._subnet_pool.name, ]
+ verifylist = [('subnet_pool', self._subnet_pool.name), ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_set_len_negative(self):
+ arglist = [
+ self._subnet_pool.name,
+ '--max-prefix-length', '-16',
+ ]
+ verifylist = [
+ ('subnet_pool', self._subnet_pool.name),
+ ('max_prefix_length', '-16'),
+ ]
+
+ self.assertRaises(argparse.ArgumentTypeError, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+
class TestShowSubnetPool(TestSubnetPool):
# The subnet_pool to set.
@@ -189,14 +446,13 @@ class TestShowSubnetPool(TestSubnetPool):
verifylist = [
('subnet_pool', self._subnet_pool.name),
]
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
columns, data = self.cmd.take_action(parsed_args)
- self.network.find_subnet_pool.assert_called_with(
+ self.network.find_subnet_pool.assert_called_once_with(
self._subnet_pool.name,
ignore_missing=False
)
-
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py
index ea3c6fe2..ab97dd91 100644
--- a/openstackclient/tests/test_shell.py
+++ b/openstackclient/tests/test_shell.py
@@ -14,6 +14,7 @@
#
import copy
+import fixtures
import mock
import os
import testtools
@@ -79,6 +80,8 @@ CLOUD_2 = {
'region_name': 'occ-cloud,krikkit,occ-env',
'log_file': '/tmp/test_log_file',
'log_level': 'debug',
+ 'cert': 'mycert',
+ 'key': 'mickey',
}
}
}
@@ -161,6 +164,23 @@ def fake_execute(shell, cmd):
return shell.run(cmd.split())
+class EnvFixture(fixtures.Fixture):
+ """Environment Fixture.
+
+ This fixture replaces os.environ with provided env or an empty env.
+ """
+
+ def __init__(self, env=None):
+ self.new_env = env or {}
+
+ def _setUp(self):
+ self.orig_env, os.environ = os.environ, self.new_env
+ self.addCleanup(self.revert)
+
+ def revert(self):
+ os.environ = self.orig_env
+
+
class TestShell(utils.TestCase):
def setUp(self):
@@ -168,12 +188,9 @@ class TestShell(utils.TestCase):
patch = "openstackclient.shell.OpenStackShell.run_subcommand"
self.cmd_patch = mock.patch(patch)
self.cmd_save = self.cmd_patch.start()
+ self.addCleanup(self.cmd_patch.stop)
self.app = mock.Mock("Test Shell")
- def tearDown(self):
- super(TestShell, self).tearDown()
- self.cmd_patch.stop()
-
def _assert_initialize_app_arg(self, cmd_options, default_args):
"""Check the args passed to initialize_app()
@@ -285,11 +302,7 @@ class TestShellHelp(TestShell):
def setUp(self):
super(TestShellHelp, self).setUp()
- self.orig_env, os.environ = os.environ, {}
-
- def tearDown(self):
- super(TestShellHelp, self).tearDown()
- os.environ = self.orig_env
+ self.useFixture(EnvFixture())
@testtools.skip("skip until bug 1444983 is resolved")
def test_help_options(self):
@@ -310,11 +323,7 @@ class TestShellOptions(TestShell):
def setUp(self):
super(TestShellOptions, self).setUp()
- self.orig_env, os.environ = os.environ, {}
-
- def tearDown(self):
- super(TestShellOptions, self).tearDown()
- os.environ = self.orig_env
+ self.useFixture(EnvFixture())
def _test_options_init_app(self, test_opts):
for opt in test_opts.keys():
@@ -402,11 +411,7 @@ class TestShellTokenAuthEnv(TestShell):
"OS_TOKEN": DEFAULT_TOKEN,
"OS_AUTH_URL": DEFAULT_AUTH_URL,
}
- self.orig_env, os.environ = os.environ, env.copy()
-
- def tearDown(self):
- super(TestShellTokenAuthEnv, self).tearDown()
- os.environ = self.orig_env
+ self.useFixture(EnvFixture(env.copy()))
def test_env(self):
flag = ""
@@ -450,11 +455,7 @@ class TestShellTokenEndpointAuthEnv(TestShell):
"OS_TOKEN": DEFAULT_TOKEN,
"OS_URL": DEFAULT_SERVICE_URL,
}
- self.orig_env, os.environ = os.environ, env.copy()
-
- def tearDown(self):
- super(TestShellTokenEndpointAuthEnv, self).tearDown()
- os.environ = self.orig_env
+ self.useFixture(EnvFixture(env.copy()))
def test_env(self):
flag = ""
@@ -501,11 +502,7 @@ class TestShellCli(TestShell):
"OS_VOLUME_API_VERSION": DEFAULT_VOLUME_API_VERSION,
"OS_NETWORK_API_VERSION": DEFAULT_NETWORK_API_VERSION,
}
- self.orig_env, os.environ = os.environ, env.copy()
-
- def tearDown(self):
- super(TestShellCli, self).tearDown()
- os.environ = self.orig_env
+ self.useFixture(EnvFixture(env.copy()))
def test_shell_args_no_options(self):
_shell = make_shell()
@@ -567,6 +564,24 @@ class TestShellCli(TestShell):
self.assertEqual('foo', _shell.options.cacert)
self.assertFalse(_shell.verify)
+ def test_shell_args_cert_options(self):
+ _shell = make_shell()
+
+ # Default
+ fake_execute(_shell, "list user")
+ self.assertEqual('', _shell.options.cert)
+ self.assertEqual('', _shell.options.key)
+
+ # --os-cert
+ fake_execute(_shell, "--os-cert mycert list user")
+ self.assertEqual('mycert', _shell.options.cert)
+ self.assertEqual('', _shell.options.key)
+
+ # --os-key
+ fake_execute(_shell, "--os-key mickey list user")
+ self.assertEqual('', _shell.options.cert)
+ self.assertEqual('mickey', _shell.options.key)
+
def test_default_env(self):
flag = ""
kwargs = {
@@ -670,6 +685,9 @@ class TestShellCli(TestShell):
_shell.cloud.config['region_name'],
)
+ self.assertEqual('mycert', _shell.cloud.config['cert'])
+ self.assertEqual('mickey', _shell.cloud.config['key'])
+
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
def test_shell_args_precedence(self, config_mock, vendor_mock):
@@ -719,11 +737,7 @@ class TestShellCliEnv(TestShell):
env = {
'OS_REGION_NAME': 'occ-env',
}
- self.orig_env, os.environ = os.environ, env.copy()
-
- def tearDown(self):
- super(TestShellCliEnv, self).tearDown()
- os.environ = self.orig_env
+ self.useFixture(EnvFixture(env.copy()))
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file")
diff --git a/openstackclient/tests/utils.py b/openstackclient/tests/utils.py
index d3f3853f..319c1c11 100644
--- a/openstackclient/tests/utils.py
+++ b/openstackclient/tests/utils.py
@@ -17,7 +17,6 @@
import os
import fixtures
-import sys
import testtools
from openstackclient.tests import fakes
@@ -50,29 +49,6 @@ class TestCase(testtools.TestCase):
msg = 'method %s should not have been called' % m
self.fail(msg)
- # 2.6 doesn't have the assert dict equals so make sure that it exists
- if tuple(sys.version_info)[0:2] < (2, 7):
-
- def assertIsInstance(self, obj, cls, msg=None):
- """self.assertTrue(isinstance(obj, cls)), with a nicer message"""
-
- if not isinstance(obj, cls):
- standardMsg = '%s is not an instance of %r' % (obj, cls)
- self.fail(self._formatMessage(msg, standardMsg))
-
- def assertDictEqual(self, d1, d2, msg=None):
- # Simple version taken from 2.7
- self.assertIsInstance(d1, dict,
- 'First argument is not a dictionary')
- self.assertIsInstance(d2, dict,
- 'Second argument is not a dictionary')
- if d1 != d2:
- if msg:
- self.fail(msg)
- else:
- standardMsg = '%r != %r' % (d1, d2)
- self.fail(standardMsg)
-
class TestCommand(TestCase):
"""Test cliff command classes"""
diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py
index 61d9df3a..97bbc59b 100644
--- a/openstackclient/tests/volume/v2/fakes.py
+++ b/openstackclient/tests/volume/v2/fakes.py
@@ -211,6 +211,26 @@ IMAGE = {
'name': image_name
}
+extension_name = 'SchedulerHints'
+extension_namespace = 'http://docs.openstack.org/'\
+ 'block-service/ext/scheduler-hints/api/v2'
+extension_description = 'Pass arbitrary key/value'\
+ 'pairs to the scheduler.'
+extension_updated = '2013-04-18T00:00:00+00:00'
+extension_alias = 'OS-SCH-HNT'
+extension_links = '[{"href":'\
+ '"https://github.com/openstack/block-api", "type":'\
+ ' "text/html", "rel": "describedby"}]'
+
+EXTENSION = {
+ 'name': extension_name,
+ 'namespace': extension_namespace,
+ 'description': extension_description,
+ 'updated': extension_updated,
+ 'alias': extension_alias,
+ 'links': extension_links,
+}
+
class FakeVolumeClient(object):
diff --git a/openstackclient/tests/volume/v2/test_backup.py b/openstackclient/tests/volume/v2/test_backup.py
index cc8dff5a..0e906e7b 100644
--- a/openstackclient/tests/volume/v2/test_backup.py
+++ b/openstackclient/tests/volume/v2/test_backup.py
@@ -77,6 +77,30 @@ class TestBackupCreate(TestBackup):
self.assertEqual(columns, volume_fakes.BACKUP_columns)
self.assertEqual(data, volume_fakes.BACKUP_data)
+ def test_backup_create_without_name(self):
+ arglist = [
+ volume_fakes.volume_id,
+ "--description", volume_fakes.backup_description,
+ "--container", volume_fakes.backup_name
+ ]
+ verifylist = [
+ ("volume", volume_fakes.volume_id),
+ ("description", volume_fakes.backup_description),
+ ("container", volume_fakes.backup_name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.backups_mock.create.assert_called_with(
+ volume_fakes.volume_id,
+ container=volume_fakes.backup_name,
+ name=None,
+ description=volume_fakes.backup_description
+ )
+ self.assertEqual(columns, volume_fakes.BACKUP_columns)
+ self.assertEqual(data, volume_fakes.BACKUP_data)
+
class TestBackupDelete(TestBackup):
diff --git a/openstackclient/tests/volume/v2/test_snapshot.py b/openstackclient/tests/volume/v2/test_snapshot.py
index 87e2fccf..8c75dfb2 100644
--- a/openstackclient/tests/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/volume/v2/test_snapshot.py
@@ -75,6 +75,30 @@ class TestSnapshotCreate(TestSnapshot):
self.assertEqual(columns, volume_fakes.SNAPSHOT_columns)
self.assertEqual(data, volume_fakes.SNAPSHOT_data)
+ def test_snapshot_create_without_name(self):
+ arglist = [
+ volume_fakes.volume_id,
+ "--description", volume_fakes.snapshot_description,
+ "--force"
+ ]
+ verifylist = [
+ ("volume", volume_fakes.volume_id),
+ ("description", volume_fakes.snapshot_description),
+ ("force", True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.create.assert_called_with(
+ volume_fakes.volume_id,
+ force=True,
+ name=None,
+ description=volume_fakes.snapshot_description
+ )
+ self.assertEqual(columns, volume_fakes.SNAPSHOT_columns)
+ self.assertEqual(data, volume_fakes.SNAPSHOT_data)
+
class TestSnapshotDelete(TestSnapshot):
diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py
index a836f79e..12253806 100644
--- a/openstackclient/tests/volume/v2/test_volume.py
+++ b/openstackclient/tests/volume/v2/test_volume.py
@@ -753,3 +753,86 @@ class TestVolumeShow(TestVolume):
self.assertEqual(volume_fakes.VOLUME_columns, columns)
self.assertEqual(volume_fakes.VOLUME_data, data)
+
+
+class TestVolumeSet(TestVolume):
+
+ def setUp(self):
+ super(TestVolumeSet, self).setUp()
+
+ self.new_volume = volume_fakes.FakeVolume.create_one_volume()
+ self.volumes_mock.create.return_value = self.new_volume
+
+ # Get the command object to test
+ self.cmd = volume.SetVolume(self.app, None)
+
+ def test_volume_set_image_property(self):
+ arglist = [
+ '--image-property', 'Alpha=a',
+ '--image-property', 'Beta=b',
+ self.new_volume.id,
+ ]
+ verifylist = [
+ ('image_property', {'Alpha': 'a', 'Beta': 'b'}),
+ ('volume', self.new_volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns nothing
+ self.cmd.take_action(parsed_args)
+ self.volumes_mock.set_image_metadata.assert_called_with(
+ self.volumes_mock.get().id, parsed_args.image_property)
+
+
+class TestVolumeUnset(TestVolume):
+
+ def setUp(self):
+ super(TestVolumeUnset, self).setUp()
+
+ self.new_volume = volume_fakes.FakeVolume.create_one_volume()
+ self.volumes_mock.create.return_value = self.new_volume
+
+ # Get the command object to set property
+ self.cmd_set = volume.SetVolume(self.app, None)
+
+ # Get the command object to unset property
+ self.cmd_unset = volume.UnsetVolume(self.app, None)
+
+ def test_volume_unset_image_property(self):
+
+ # Arguments for setting image properties
+ arglist = [
+ '--image-property', 'Alpha=a',
+ '--image-property', 'Beta=b',
+ self.new_volume.id,
+ ]
+ verifylist = [
+ ('image_property', {'Alpha': 'a', 'Beta': 'b'}),
+ ('volume', self.new_volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd_set, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns nothing
+ self.cmd_set.take_action(parsed_args)
+
+ # Arguments for unsetting image properties
+ arglist_unset = [
+ '--image-property', 'Alpha',
+ self.new_volume.id,
+ ]
+ verifylist_unset = [
+ ('image_property', ['Alpha']),
+ ('volume', self.new_volume.id),
+ ]
+ parsed_args_unset = self.check_parser(self.cmd_unset,
+ arglist_unset,
+ verifylist_unset)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns nothing
+ self.cmd_unset.take_action(parsed_args_unset)
+
+ self.volumes_mock.delete_image_metadata.assert_called_with(
+ self.volumes_mock.get().id, parsed_args_unset.image_property)
diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py
index 32f39fb5..40b60315 100644
--- a/openstackclient/volume/v1/backup.py
+++ b/openstackclient/volume/v1/backup.py
@@ -41,7 +41,6 @@ class CreateBackup(command.ShowOne):
parser.add_argument(
'--name',
metavar='<name>',
- required=False,
help='Name of the backup',
)
parser.add_argument(
diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py
index 95200e40..c54bac8a 100644
--- a/openstackclient/volume/v1/snapshot.py
+++ b/openstackclient/volume/v1/snapshot.py
@@ -36,7 +36,6 @@ class CreateSnapshot(command.ShowOne):
parser.add_argument(
'--name',
metavar='<name>',
- required=True,
help='Name of the snapshot',
)
parser.add_argument(
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index 90827d20..29c197ef 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -31,20 +31,30 @@ class CreateVolume(command.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help='New volume name',
+ help='Volume name',
)
parser.add_argument(
'--size',
metavar='<size>',
required=True,
type=int,
- help='New volume size in GB',
+ help='Volume size in GB',
+ )
+ parser.add_argument(
+ '--type',
+ metavar='<volume-type>',
+ help="Set the type of volume",
+ )
+ parser.add_argument(
+ '--image',
+ metavar='<image>',
+ help='Use <image> as source of volume (name or ID)',
)
snapshot_group = parser.add_mutually_exclusive_group()
snapshot_group.add_argument(
'--snapshot',
metavar='<snapshot>',
- help='Use <snapshot> as source of new volume',
+ help='Use <snapshot> as source of volume (name or ID)',
)
snapshot_group.add_argument(
'--snapshot-id',
@@ -52,14 +62,14 @@ class CreateVolume(command.ShowOne):
help=argparse.SUPPRESS,
)
parser.add_argument(
- '--description',
- metavar='<description>',
- help='New volume description',
+ '--source',
+ metavar='<volume>',
+ help='Volume to clone (name or ID)',
)
parser.add_argument(
- '--type',
- metavar='<volume-type>',
- help='Use <volume-type> as the new volume type',
+ '--description',
+ metavar='<description>',
+ help='Volume description',
)
parser.add_argument(
'--user',
@@ -74,17 +84,7 @@ class CreateVolume(command.ShowOne):
parser.add_argument(
'--availability-zone',
metavar='<availability-zone>',
- help='Create new volume in <availability-zone>',
- )
- parser.add_argument(
- '--image',
- metavar='<image>',
- help='Use <image> as source of new volume (name or ID)',
- )
- parser.add_argument(
- '--source',
- metavar='<volume>',
- help='Volume to clone (name or ID)',
+ help='Create volume in <availability-zone>',
)
parser.add_argument(
'--property',
@@ -308,7 +308,7 @@ class SetVolume(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help='Volume to change (name or ID)',
+ help='Volume to modify (name or ID)',
)
parser.add_argument(
'--name',
@@ -330,7 +330,7 @@ class SetVolume(command.Command):
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help='Property to add or modify for this volume '
+ help='Set a property on this volume '
'(repeat option to set multiple properties)',
)
return parser
@@ -411,7 +411,7 @@ class UnsetVolume(command.Command):
metavar='<key>',
action='append',
default=[],
- help='Property to remove from volume '
+ help='Remove a property from volume '
'(repeat option to remove multiple properties)',
required=True,
)
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index 24d0b235..05671c1f 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -30,13 +30,13 @@ class CreateVolumeType(command.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help='New volume type name',
+ help='Volume type name',
)
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help='Property to add for this volume type '
+ help='Set a property on this volume type '
'(repeat option to set multiple properties)',
)
return parser
@@ -114,7 +114,7 @@ class SetVolumeType(command.Command):
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help='Property to add or modify for this volume type '
+ help='Set a property on this volume type '
'(repeat option to set multiple properties)',
)
return parser
@@ -128,6 +128,27 @@ class SetVolumeType(command.Command):
volume_type.set_keys(parsed_args.property)
+class ShowVolumeType(command.ShowOne):
+ """Display volume type details"""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowVolumeType, self).get_parser(prog_name)
+ parser.add_argument(
+ "volume_type",
+ metavar="<volume-type>",
+ help="Volume type to display (name or ID)"
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume_type = utils.find_resource(
+ volume_client.volume_types, parsed_args.volume_type)
+ properties = utils.format_dict(volume_type._info.pop('extra_specs'))
+ volume_type._info.update({'properties': properties})
+ return zip(*sorted(six.iteritems(volume_type._info)))
+
+
class UnsetVolumeType(command.Command):
"""Unset volume type properties"""
@@ -143,7 +164,7 @@ class UnsetVolumeType(command.Command):
metavar='<key>',
action='append',
default=[],
- help='Property to remove from volume type '
+ help='Remove a property from this volume type '
'(repeat option to remove multiple properties)',
required=True,
)
@@ -160,24 +181,3 @@ class UnsetVolumeType(command.Command):
volume_type.unset_keys(parsed_args.property)
else:
self.app.log.error("No changes requested\n")
-
-
-class ShowVolumeType(command.ShowOne):
- """Display volume type details"""
-
- def get_parser(self, prog_name):
- parser = super(ShowVolumeType, self).get_parser(prog_name)
- parser.add_argument(
- "volume_type",
- metavar="<volume-type>",
- help="Volume type to display (name or ID)"
- )
- return parser
-
- def take_action(self, parsed_args):
- volume_client = self.app.client_manager.volume
- volume_type = utils.find_resource(
- volume_client.volume_types, parsed_args.volume_type)
- properties = utils.format_dict(volume_type._info.pop('extra_specs'))
- volume_type._info.update({'properties': properties})
- return zip(*sorted(six.iteritems(volume_type._info)))
diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/backup.py
index 64ca97ae..016a414c 100644
--- a/openstackclient/volume/v2/backup.py
+++ b/openstackclient/volume/v2/backup.py
@@ -35,7 +35,6 @@ class CreateBackup(command.ShowOne):
parser.add_argument(
"--name",
metavar="<name>",
- required=True,
help="Name of the backup"
)
parser.add_argument(
diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py
index 4d00b726..c9e50297 100644
--- a/openstackclient/volume/v2/snapshot.py
+++ b/openstackclient/volume/v2/snapshot.py
@@ -36,7 +36,6 @@ class CreateSnapshot(command.ShowOne):
parser.add_argument(
"--name",
metavar="<name>",
- required=True,
help="Name of the snapshot"
)
parser.add_argument(
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index 8f2122eb..5b7511e8 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -32,29 +32,39 @@ class CreateVolume(command.ShowOne):
parser.add_argument(
"name",
metavar="<name>",
- help="New volume name"
+ help="Volume name",
)
parser.add_argument(
"--size",
metavar="<size>",
type=int,
required=True,
- help="New volume size in GB"
+ help="Volume size in GB",
+ )
+ parser.add_argument(
+ "--type",
+ metavar="<volume-type>",
+ help="Set the type of volume",
+ )
+ parser.add_argument(
+ "--image",
+ metavar="<image>",
+ help="Use <image> as source of volume (name or ID)",
)
parser.add_argument(
"--snapshot",
metavar="<snapshot>",
- help="Use <snapshot> as source of new volume (name or ID)"
+ help="Use <snapshot> as source of volume (name or ID)",
)
parser.add_argument(
- "--description",
- metavar="<description>",
- help="New volume description"
+ "--source",
+ metavar="<volume>",
+ help="Volume to clone (name or ID)",
)
parser.add_argument(
- "--type",
- metavar="<volume-type>",
- help="Use <volume-type> as the new volume type",
+ "--description",
+ metavar="<description>",
+ help="Volume description",
)
parser.add_argument(
'--user',
@@ -69,24 +79,14 @@ class CreateVolume(command.ShowOne):
parser.add_argument(
"--availability-zone",
metavar="<availability-zone>",
- help="Create new volume in <availability_zone>"
- )
- parser.add_argument(
- "--image",
- metavar="<image>",
- help="Use <image> as source of new volume (name or ID)"
- )
- parser.add_argument(
- "--source",
- metavar="<volume>",
- help="Volume to clone (name or ID)"
+ help="Create volume in <availability-zone>",
)
parser.add_argument(
"--property",
metavar="<key=value>",
action=parseractions.KeyValueAction,
help="Set a property to this volume "
- "(repeat option to set multiple properties)"
+ "(repeat option to set multiple properties)",
)
return parser
@@ -188,13 +188,13 @@ class ListVolume(command.Lister):
parser = super(ListVolume, self).get_parser(prog_name)
parser.add_argument(
'--project',
- metavar='<project-id>',
+ metavar='<project>',
help='Filter results by project (name or ID) (admin only)'
)
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--user',
- metavar='<user-id>',
+ metavar='<user>',
help='Filter results by user (name or ID) (admin only)'
)
identity_common.add_user_domain_option_to_parser(parser)
@@ -320,7 +320,7 @@ class SetVolume(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help='Volume to change (name or ID)',
+ help='Volume to modify (name or ID)',
)
parser.add_argument(
'--name',
@@ -328,23 +328,30 @@ class SetVolume(command.Command):
help='New volume name',
)
parser.add_argument(
- '--description',
- metavar='<description>',
- help='New volume description',
- )
- parser.add_argument(
'--size',
metavar='<size>',
type=int,
help='Extend volume size in GB',
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help='New volume description',
+ )
+ parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help='Property to add or modify for this volume '
+ help='Set a property on this volume '
'(repeat option to set multiple properties)',
)
+ parser.add_argument(
+ '--image-property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help='Set an image property on this volume '
+ '(repeat option to set multiple image properties)',
+ )
return parser
def take_action(self, parsed_args):
@@ -365,6 +372,9 @@ class SetVolume(command.Command):
if parsed_args.property:
volume_client.volumes.set_metadata(volume.id, parsed_args.property)
+ if parsed_args.image_property:
+ volume_client.volumes.set_image_metadata(
+ volume.id, parsed_args.image_property)
kwargs = {}
if parsed_args.name:
@@ -374,7 +384,8 @@ class SetVolume(command.Command):
if kwargs:
volume_client.volumes.update(volume.id, **kwargs)
- if not kwargs and not parsed_args.property and not parsed_args.size:
+ if (not kwargs and not parsed_args.property
+ and not parsed_args.image_property and not parsed_args.size):
self.app.log.error("No changes requested\n")
@@ -422,12 +433,17 @@ class UnsetVolume(command.Command):
parser.add_argument(
'--property',
metavar='<key>',
- required=True,
action='append',
- default=[],
- help='Property to remove from volume '
+ help='Remove a property from volume '
'(repeat option to remove multiple properties)',
)
+ parser.add_argument(
+ '--image-property',
+ metavar='<key>',
+ action='append',
+ help='Remove an image property from volume '
+ '(repeat option to remove multiple image properties)',
+ )
return parser
def take_action(self, parsed_args):
@@ -435,5 +451,12 @@ class UnsetVolume(command.Command):
volume = utils.find_resource(
volume_client.volumes, parsed_args.volume)
- volume_client.volumes.delete_metadata(
- volume.id, parsed_args.property)
+ if parsed_args.property:
+ volume_client.volumes.delete_metadata(
+ volume.id, parsed_args.property)
+ if parsed_args.image_property:
+ volume_client.volumes.delete_image_metadata(
+ volume.id, parsed_args.image_property)
+
+ if (not parsed_args.image_property and not parsed_args.property):
+ self.app.log.error("No changes requested\n")
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index d2b3ed6a..5509ac52 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -29,12 +29,12 @@ class CreateVolumeType(command.ShowOne):
parser.add_argument(
"name",
metavar="<name>",
- help="New volume type name"
+ help="Volume type name",
)
parser.add_argument(
"--description",
metavar="<description>",
- help="New volume type description",
+ help="Volume type description",
)
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
@@ -55,7 +55,7 @@ class CreateVolumeType(command.ShowOne):
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help='Property to add for this volume type'
+ help='Set a property on this volume type'
'(repeat option to set multiple properties)',
)
return parser
@@ -153,7 +153,7 @@ class SetVolumeType(command.Command):
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help='Property to add or modify for this volume type '
+ help='Set a property on this volume type '
'(repeat option to set multiple properties)',
)
return parser
@@ -221,7 +221,7 @@ class UnsetVolumeType(command.Command):
metavar='<key>',
default=[],
required=True,
- help='Property to remove from volume type '
+ help='Remove a property from this volume type '
'(repeat option to remove multiple properties)',
)
return parser