summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/image_v1.py6
-rw-r--r--openstackclient/api/image_v2.py6
-rw-r--r--openstackclient/api/object_store_v1.py2
-rw-r--r--openstackclient/common/client_config.py96
-rw-r--r--openstackclient/compute/v2/flavor.py2
-rw-r--r--openstackclient/compute/v2/server.py68
-rw-r--r--openstackclient/compute/v2/usage.py2
-rw-r--r--openstackclient/identity/v2_0/project.py24
-rw-r--r--openstackclient/identity/v2_0/role.py23
-rw-r--r--openstackclient/identity/v2_0/user.py24
-rw-r--r--openstackclient/identity/v3/group.py22
-rw-r--r--openstackclient/identity/v3/project.py30
-rw-r--r--openstackclient/identity/v3/role.py27
-rw-r--r--openstackclient/identity/v3/trust.py26
-rw-r--r--openstackclient/identity/v3/user.py30
-rw-r--r--openstackclient/image/v1/image.py2
-rw-r--r--openstackclient/image/v2/image.py38
-rw-r--r--openstackclient/network/v2/address_scope.py61
-rw-r--r--openstackclient/network/v2/ip_availability.py4
-rw-r--r--openstackclient/network/v2/meter.py190
-rw-r--r--openstackclient/network/v2/network.py57
-rw-r--r--openstackclient/network/v2/network_agent.py41
-rw-r--r--openstackclient/network/v2/network_qos_rule_type.py43
-rw-r--r--openstackclient/network/v2/network_rbac.py36
-rw-r--r--openstackclient/network/v2/network_service_provider.py4
-rw-r--r--openstackclient/network/v2/port.py54
-rw-r--r--openstackclient/network/v2/router.py115
-rw-r--r--openstackclient/network/v2/security_group.py6
-rw-r--r--openstackclient/tests/functional/common/test_quota.py6
-rw-r--r--openstackclient/tests/functional/compute/v2/test_flavor.py227
-rw-r--r--openstackclient/tests/functional/identity/v3/common.py3
-rw-r--r--openstackclient/tests/functional/identity/v3/test_idp.py3
-rw-r--r--openstackclient/tests/functional/image/v2/test_image.py22
-rw-r--r--openstackclient/tests/functional/network/v2/test_address_scope.py148
-rw-r--r--openstackclient/tests/functional/network/v2/test_floating_ip.py149
-rw-r--r--openstackclient/tests/functional/network/v2/test_meter.py102
-rw-r--r--openstackclient/tests/functional/network/v2/test_network.py263
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_agent.py4
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py32
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_service_provider.py2
-rw-r--r--openstackclient/tests/functional/network/v2/test_port.py3
-rw-r--r--openstackclient/tests/functional/network/v2/test_security_group_rule.py3
-rwxr-xr-xopenstackclient/tests/functional/post_test_hook_tips.sh46
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_type.py5
-rw-r--r--openstackclient/tests/unit/api/test_image_v1.py2
-rw-r--r--openstackclient/tests/unit/api/test_image_v2.py2
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py65
-rw-r--r--openstackclient/tests/unit/compute/v2/test_flavor.py5
-rw-r--r--openstackclient/tests/unit/compute/v2/test_usage.py179
-rw-r--r--openstackclient/tests/unit/fakes.py3
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_project.py29
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_role.py27
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_user.py27
-rw-r--r--openstackclient/tests/unit/identity/v3/test_group.py27
-rw-r--r--openstackclient/tests/unit/identity/v3/test_project.py27
-rw-r--r--openstackclient/tests/unit/identity/v3/test_role.py34
-rw-r--r--openstackclient/tests/unit/identity/v3/test_trust.py31
-rw-r--r--openstackclient/tests/unit/identity/v3/test_user.py29
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py33
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py119
-rw-r--r--openstackclient/tests/unit/network/v2/test_address_scope.py98
-rw-r--r--openstackclient/tests/unit/network/v2/test_ip_availability.py10
-rw-r--r--openstackclient/tests/unit/network/v2/test_meter.py304
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py55
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_agent.py35
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py62
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py64
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py183
-rw-r--r--openstackclient/tests/unit/test_shell.py2
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume.py136
-rw-r--r--openstackclient/tests/unit/volume/v2/test_backup.py24
-rw-r--r--openstackclient/tests/unit/volume/v2/test_snapshot.py28
-rw-r--r--openstackclient/tests/unit/volume/v2/test_type.py27
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py265
-rw-r--r--openstackclient/volume/v1/volume.py38
-rw-r--r--openstackclient/volume/v1/volume_snapshot.py12
-rw-r--r--openstackclient/volume/v2/volume.py47
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py51
-rw-r--r--openstackclient/volume/v2/volume_type.py29
80 files changed, 3742 insertions, 426 deletions
diff --git a/openstackclient/api/image_v1.py b/openstackclient/api/image_v1.py
index 534c7750..e15d825a 100644
--- a/openstackclient/api/image_v1.py
+++ b/openstackclient/api/image_v1.py
@@ -19,7 +19,7 @@ from openstackclient.api import api
class APIv1(api.BaseAPI):
"""Image v1 API"""
- _endpoint_suffix = 'v1'
+ _endpoint_suffix = '/v1'
def __init__(self, endpoint=None, **kwargs):
super(APIv1, self).__init__(endpoint=endpoint, **kwargs)
@@ -29,8 +29,8 @@ class APIv1(api.BaseAPI):
def _munge_url(self):
# Hack this until discovery is up
- if self._endpoint_suffix not in self.endpoint.split('/')[-1]:
- self.endpoint = '/'.join([self.endpoint, self._endpoint_suffix])
+ if not self.endpoint.endswith(self._endpoint_suffix):
+ self.endpoint = self.endpoint + self._endpoint_suffix
def image_list(
self,
diff --git a/openstackclient/api/image_v2.py b/openstackclient/api/image_v2.py
index 026498fa..c3628121 100644
--- a/openstackclient/api/image_v2.py
+++ b/openstackclient/api/image_v2.py
@@ -19,12 +19,12 @@ from openstackclient.api import image_v1
class APIv2(image_v1.APIv1):
"""Image v2 API"""
- _endpoint_suffix = 'v2'
+ _endpoint_suffix = '/v2'
def _munge_url(self):
# Hack this until discovery is up, and ignore parent endpoint setting
- if 'v2' not in self.endpoint.split('/')[-1]:
- self.endpoint = '/'.join([self.endpoint, 'v2'])
+ if not self.endpoint.endswith(self._endpoint_suffix):
+ self.endpoint = self.endpoint + self._endpoint_suffix
def image_list(
self,
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py
index 184814c6..74c4a46f 100644
--- a/openstackclient/api/object_store_v1.py
+++ b/openstackclient/api/object_store_v1.py
@@ -380,7 +380,7 @@ class APIv1(api.BaseAPI):
if len(os.path.dirname(file)) > 0:
os.makedirs(os.path.dirname(file))
with open(file, 'wb') as f:
- for chunk in response.iter_content():
+ for chunk in response.iter_content(64 * 1024):
f.write(chunk)
def object_set(
diff --git a/openstackclient/common/client_config.py b/openstackclient/common/client_config.py
index 1ac53f7f..ad86e9f8 100644
--- a/openstackclient/common/client_config.py
+++ b/openstackclient/common/client_config.py
@@ -15,10 +15,8 @@
import logging
-from os_client_config import config
from os_client_config import exceptions as occ_exceptions
-from oslo_utils import strutils
-import six
+from osc_lib.cli import client_config
LOG = logging.getLogger(__name__)
@@ -26,7 +24,7 @@ LOG = logging.getLogger(__name__)
# Sublcass OpenStackConfig in order to munge config values
# before auth plugins are loaded
-class OSC_Config(config.OpenStackConfig):
+class OSC_Config(client_config.OSC_Config):
# TODO(dtroyer): Once os-client-config with pw_func argument is in
# global-requirements we can remove __init()__
@@ -62,75 +60,8 @@ class OSC_Config(config.OpenStackConfig):
return ret
- def _auth_select_default_plugin(self, config):
- """Select a default plugin based on supplied arguments
-
- Migrated from auth.select_auth_plugin()
- """
-
- identity_version = config.get('identity_api_version', '')
-
- if config.get('username', None) and not config.get('auth_type', None):
- if identity_version == '3':
- config['auth_type'] = 'v3password'
- elif identity_version.startswith('2'):
- config['auth_type'] = 'v2password'
- else:
- # let keystoneauth figure it out itself
- config['auth_type'] = 'password'
- elif config.get('token', None) and not config.get('auth_type', None):
- if identity_version == '3':
- config['auth_type'] = 'v3token'
- elif identity_version.startswith('2'):
- config['auth_type'] = 'v2token'
- else:
- # let keystoneauth figure it out itself
- config['auth_type'] = 'token'
- else:
- # The ultimate default is similar to the original behaviour,
- # but this time with version discovery
- if not config.get('auth_type', None):
- config['auth_type'] = 'password'
-
- LOG.debug("Auth plugin %s selected" % config['auth_type'])
- return config
-
- def _auth_v2_arguments(self, config):
- """Set up v2-required arguments from v3 info
-
- Migrated from auth.build_auth_params()
- """
-
- if ('auth_type' in config and config['auth_type'].startswith("v2")):
- if 'project_id' in config['auth']:
- config['auth']['tenant_id'] = config['auth']['project_id']
- if 'project_name' in config['auth']:
- config['auth']['tenant_name'] = config['auth']['project_name']
- return config
-
- def _auth_v2_ignore_v3(self, config):
- """Remove v3 arguemnts if present for v2 plugin
-
- Migrated from clientmanager.setup_auth()
- """
-
- # NOTE(hieulq): If USER_DOMAIN_NAME, USER_DOMAIN_ID, PROJECT_DOMAIN_ID
- # or PROJECT_DOMAIN_NAME is present and API_VERSION is 2.0, then
- # ignore all domain related configs.
- if (config.get('identity_api_version', '').startswith('2') and
- config.get('auth_type', None).endswith('password')):
- domain_props = [
- 'project_domain_id',
- 'project_domain_name',
- 'user_domain_id',
- 'user_domain_name',
- ]
- for prop in domain_props:
- if config['auth'].pop(prop, None) is not None:
- LOG.warning("Ignoring domain related config " +
- prop + " because identity API version is 2.0")
- return config
-
+ # TODO(dtroyer): Remove _auth_default_domain when the v3otp fix is
+ # backported to osc-lib, should be in release 1.3.0
def _auth_default_domain(self, config):
"""Set a default domain from available arguments
@@ -171,23 +102,6 @@ class OSC_Config(config.OpenStackConfig):
config['auth']['user_domain_id'] = default_domain
return config
- def auth_config_hook(self, config):
- """Allow examination of config values before loading auth plugin
-
- OpenStackClient will override this to perform additional chacks
- on auth_type.
- """
-
- config = self._auth_select_default_plugin(config)
- config = self._auth_v2_arguments(config)
- config = self._auth_v2_ignore_v3(config)
- config = self._auth_default_domain(config)
-
- if LOG.isEnabledFor(logging.DEBUG):
- LOG.debug("auth_config_hook(): %s",
- strutils.mask_password(six.text_type(config)))
- return config
-
def load_auth_plugin(self, config):
"""Get auth plugin and validate args"""
@@ -196,10 +110,12 @@ class OSC_Config(config.OpenStackConfig):
auth_plugin = loader.load_from_options(**config['auth'])
return auth_plugin
+ # TODO(dtroyer): Remove _validate_auth_ksc when it is in osc-lib 1.3.0
def _validate_auth_ksc(self, config, cloud, fixed_argparse=None):
"""Old compatibility hack for OSC, no longer needed/wanted"""
return config
+ # TODO(dtroyer): Remove _validate_auth when it is in osc-lib 1.3.0
def _validate_auth(self, config, loader, fixed_argparse=None):
"""Validate auth plugin arguments"""
# May throw a keystoneauth1.exceptions.NoMatchingPlugin
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index f20d154b..e562cd40 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -167,7 +167,7 @@ class CreateFlavor(command.ShowOne):
parsed_args.project_domain,
).id
compute_client.flavor_access.add_tenant_access(
- parsed_args.id, project_id)
+ flavor.id, project_id)
except Exception as e:
msg = _("Failed to add project %(project)s access to "
"flavor: %(e)s")
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 48d8b2d0..d9221873 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -393,7 +393,10 @@ class CreateServer(command.ShowOne):
"net-id: attach NIC to network with this UUID, "
"port-id: attach NIC to port with this UUID, "
"v4-fixed-ip: IPv4 fixed address for NIC (optional), "
- "v6-fixed-ip: IPv6 fixed address for NIC (optional)."),
+ "v6-fixed-ip: IPv6 fixed address for NIC (optional), "
+ "none: (v2.37+) no network is attached, "
+ "auto: (v2.37+) the compute service will automatically "
+ "allocate a network."),
)
parser.add_argument(
'--hint',
@@ -513,36 +516,39 @@ class CreateServer(command.ShowOne):
block_device_mapping.update({dev_key: block_volume})
nics = []
- for nic_str in parsed_args.nic:
- nic_info = {"net-id": "", "v4-fixed-ip": "",
- "v6-fixed-ip": "", "port-id": ""}
- nic_info.update(dict(kv_str.split("=", 1)
- for kv_str in nic_str.split(",")))
- if bool(nic_info["net-id"]) == bool(nic_info["port-id"]):
- msg = _("either net-id or port-id should be specified "
- "but not both")
- raise exceptions.CommandError(msg)
- if self.app.client_manager.is_network_endpoint_enabled():
- network_client = self.app.client_manager.network
- if nic_info["net-id"]:
- net = network_client.find_network(
- nic_info["net-id"], ignore_missing=False)
- nic_info["net-id"] = net.id
- if nic_info["port-id"]:
- port = network_client.find_port(
- nic_info["port-id"], ignore_missing=False)
- nic_info["port-id"] = port.id
- else:
- if nic_info["net-id"]:
- nic_info["net-id"] = utils.find_resource(
- compute_client.networks,
- nic_info["net-id"]
- ).id
- if nic_info["port-id"]:
- msg = _("can't create server with port specified "
- "since network endpoint not enabled")
+ if parsed_args.nic in ('auto', 'none'):
+ nics = [parsed_args.nic]
+ else:
+ for nic_str in parsed_args.nic:
+ nic_info = {"net-id": "", "v4-fixed-ip": "",
+ "v6-fixed-ip": "", "port-id": ""}
+ nic_info.update(dict(kv_str.split("=", 1)
+ for kv_str in nic_str.split(",")))
+ if bool(nic_info["net-id"]) == bool(nic_info["port-id"]):
+ msg = _("either net-id or port-id should be specified "
+ "but not both")
raise exceptions.CommandError(msg)
- nics.append(nic_info)
+ if self.app.client_manager.is_network_endpoint_enabled():
+ network_client = self.app.client_manager.network
+ if nic_info["net-id"]:
+ net = network_client.find_network(
+ nic_info["net-id"], ignore_missing=False)
+ nic_info["net-id"] = net.id
+ if nic_info["port-id"]:
+ port = network_client.find_port(
+ nic_info["port-id"], ignore_missing=False)
+ nic_info["port-id"] = port.id
+ else:
+ if nic_info["net-id"]:
+ nic_info["net-id"] = utils.find_resource(
+ compute_client.networks,
+ nic_info["net-id"]
+ ).id
+ if nic_info["port-id"]:
+ msg = _("can't create server with port specified "
+ "since network endpoint not enabled")
+ raise exceptions.CommandError(msg)
+ nics.append(nic_info)
hints = {}
for hint in parsed_args.hint:
@@ -1003,7 +1009,7 @@ class MigrateServer(command.Command):
parser.add_argument(
'--wait',
action='store_true',
- help=_('Wait for resize to complete'),
+ help=_('Wait for migrate to complete'),
)
return parser
diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py
index 89601ae3..3edcffe4 100644
--- a/openstackclient/compute/v2/usage.py
+++ b/openstackclient/compute/v2/usage.py
@@ -89,7 +89,7 @@ class ListUsage(command.Lister):
# Cache the project list
project_cache = {}
try:
- for p in self.app.client_manager.identity.tenants.list():
+ for p in self.app.client_manager.identity.projects.list():
project_cache[p.id] = p
except Exception:
# Just forget it if there's any trouble
diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py
index 8526d6bd..ca565d4d 100644
--- a/openstackclient/identity/v2_0/project.py
+++ b/openstackclient/identity/v2_0/project.py
@@ -20,6 +20,7 @@ import logging
from keystoneauth1 import exceptions as ks_exc
from osc_lib.cli import parseractions
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -117,12 +118,25 @@ class DeleteProject(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+ errors = 0
for project in parsed_args.projects:
- project_obj = utils.find_resource(
- identity_client.tenants,
- project,
- )
- identity_client.tenants.delete(project_obj.id)
+ try:
+ project_obj = utils.find_resource(
+ identity_client.tenants,
+ project,
+ )
+ identity_client.tenants.delete(project_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete project with "
+ "name or ID '%(project)s': %(e)s"),
+ {'project': project, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.projects)
+ msg = (_("%(errors)s of %(total)s projects failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListProject(command.Lister):
diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py
index 0a28a70a..e254e05f 100644
--- a/openstackclient/identity/v2_0/role.py
+++ b/openstackclient/identity/v2_0/role.py
@@ -124,12 +124,25 @@ class DeleteRole(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+ errors = 0
for role in parsed_args.roles:
- role_obj = utils.find_resource(
- identity_client.roles,
- role,
- )
- identity_client.roles.delete(role_obj.id)
+ try:
+ role_obj = utils.find_resource(
+ identity_client.roles,
+ role,
+ )
+ identity_client.roles.delete(role_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete role with "
+ "name or ID '%(role)s': %(e)s"),
+ {'role': role, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.roles)
+ msg = (_("%(errors)s of %(total)s roles failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListRole(command.Lister):
diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py
index ddd5b981..2a3dde6b 100644
--- a/openstackclient/identity/v2_0/user.py
+++ b/openstackclient/identity/v2_0/user.py
@@ -19,6 +19,7 @@ import logging
from keystoneauth1 import exceptions as ks_exc
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -145,12 +146,25 @@ class DeleteUser(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+ errors = 0
for user in parsed_args.users:
- user_obj = utils.find_resource(
- identity_client.users,
- user,
- )
- identity_client.users.delete(user_obj.id)
+ try:
+ user_obj = utils.find_resource(
+ identity_client.users,
+ user,
+ )
+ identity_client.users.delete(user_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete user with "
+ "name or ID '%(user)s': %(e)s"),
+ {'user': user, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.users)
+ msg = (_("%(errors)s of %(total)s users failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListUser(command.Lister):
diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py
index df684c12..a03a86eb 100644
--- a/openstackclient/identity/v3/group.py
+++ b/openstackclient/identity/v3/group.py
@@ -20,6 +20,7 @@ import sys
from keystoneauth1 import exceptions as ks_exc
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -194,11 +195,24 @@ class DeleteGroup(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
+ errors = 0
for group in parsed_args.groups:
- group_obj = common.find_group(identity_client,
- group,
- parsed_args.domain)
- identity_client.groups.delete(group_obj.id)
+ try:
+ group_obj = common.find_group(identity_client,
+ group,
+ parsed_args.domain)
+ identity_client.groups.delete(group_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete group with "
+ "name or ID '%(group)s': %(e)s"),
+ {'group': group, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.groups)
+ msg = (_("%(errors)s of %(total)s groups failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListGroup(command.Lister):
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index a6348659..12197cdd 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -20,6 +20,7 @@ import logging
from keystoneauth1 import exceptions as ks_exc
from osc_lib.cli import parseractions
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -148,15 +149,28 @@ class DeleteProject(command.Command):
domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client, parsed_args.domain)
+ errors = 0
for project in parsed_args.projects:
- if domain is not None:
- project_obj = utils.find_resource(identity_client.projects,
- project,
- domain_id=domain.id)
- else:
- project_obj = utils.find_resource(identity_client.projects,
- project)
- identity_client.projects.delete(project_obj.id)
+ try:
+ if domain is not None:
+ project_obj = utils.find_resource(identity_client.projects,
+ project,
+ domain_id=domain.id)
+ else:
+ project_obj = utils.find_resource(identity_client.projects,
+ project)
+ identity_client.projects.delete(project_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete project with "
+ "name or ID '%(project)s': %(e)s"),
+ {'project': project, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.projects)
+ msg = (_("%(errors)s of %(total)s projects failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListProject(command.Lister):
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index c9d0fbf3..994ecc9c 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -20,6 +20,7 @@ import sys
from keystoneauth1 import exceptions as ks_exc
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -223,14 +224,26 @@ class DeleteRole(command.Command):
if parsed_args.domain:
domain_id = common.find_domain(identity_client,
parsed_args.domain).id
-
+ errors = 0
for role in parsed_args.roles:
- role_obj = utils.find_resource(
- identity_client.roles,
- role,
- domain_id=domain_id
- )
- identity_client.roles.delete(role_obj.id)
+ try:
+ role_obj = utils.find_resource(
+ identity_client.roles,
+ role,
+ domain_id=domain_id
+ )
+ identity_client.roles.delete(role_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete role with "
+ "name or ID '%(role)s': %(e)s"),
+ {'role': role, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.roles)
+ msg = (_("%(errors)s of %(total)s roles failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListRole(command.Lister):
diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py
index 62d72ea1..04ee4dce 100644
--- a/openstackclient/identity/v3/trust.py
+++ b/openstackclient/identity/v3/trust.py
@@ -14,8 +14,10 @@
"""Identity v3 Trust action implementations"""
import datetime
+import logging
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -23,6 +25,9 @@ from openstackclient.i18n import _
from openstackclient.identity import common
+LOG = logging.getLogger(__name__)
+
+
class CreateTrust(command.ShowOne):
_description = _("Create new trust")
@@ -145,9 +150,24 @@ class DeleteTrust(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- for t in parsed_args.trust:
- trust_obj = utils.find_resource(identity_client.trusts, t)
- identity_client.trusts.delete(trust_obj.id)
+
+ errors = 0
+ for trust in parsed_args.trust:
+ try:
+ trust_obj = utils.find_resource(identity_client.trusts,
+ trust)
+ identity_client.trusts.delete(trust_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete trust with "
+ "name or ID '%(trust)s': %(e)s"),
+ {'trust': trust, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.trust)
+ msg = (_("%(errors)s of %(total)s trusts failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListTrust(command.Lister):
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index 796cf28c..19a4c298 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -20,6 +20,7 @@ import logging
from keystoneauth1 import exceptions as ks_exc
from osc_lib.command import command
+from osc_lib import exceptions
from osc_lib import utils
import six
@@ -161,15 +162,28 @@ class DeleteUser(command.Command):
domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client, parsed_args.domain)
+ errors = 0
for user in parsed_args.users:
- if domain is not None:
- user_obj = utils.find_resource(identity_client.users,
- user,
- domain_id=domain.id)
- else:
- user_obj = utils.find_resource(identity_client.users,
- user)
- identity_client.users.delete(user_obj.id)
+ try:
+ if domain is not None:
+ user_obj = utils.find_resource(identity_client.users,
+ user,
+ domain_id=domain.id)
+ else:
+ user_obj = utils.find_resource(identity_client.users,
+ user)
+ identity_client.users.delete(user_obj.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete user with "
+ "name or ID '%(user)s': %(e)s"),
+ {'user': user, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.users)
+ msg = (_("%(errors)s of %(total)s users failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
class ListUser(command.Lister):
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index eb79cd2f..1f239b67 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -39,7 +39,7 @@ from openstackclient.i18n import _
DEFAULT_CONTAINER_FORMAT = 'bare'
DEFAULT_DISK_FORMAT = 'raw'
DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx",
- "vdi", "iso"]
+ "vdi", "iso", "ploop"]
LOG = logging.getLogger(__name__)
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 1d167605..418cc397 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -33,7 +33,7 @@ from openstackclient.identity import common
DEFAULT_CONTAINER_FORMAT = 'bare'
DEFAULT_DISK_FORMAT = 'raw'
DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx",
- "vdi", "iso"]
+ "vdi", "iso", "ploop"]
LOG = logging.getLogger(__name__)
@@ -777,6 +777,23 @@ class SetImage(command.Command):
dest=deadopt.replace('-', '_'),
help=argparse.SUPPRESS,
)
+
+ membership_group = parser.add_mutually_exclusive_group()
+ membership_group.add_argument(
+ "--accept",
+ action="store_true",
+ help=_("Accept the image membership"),
+ )
+ membership_group.add_argument(
+ "--reject",
+ action="store_true",
+ help=_("Reject the image membership"),
+ )
+ membership_group.add_argument(
+ "--pending",
+ action="store_true",
+ help=_("Reset the image membership to 'pending'"),
+ )
return parser
def take_action(self, parsed_args):
@@ -828,12 +845,14 @@ class SetImage(command.Command):
project_arg = parsed_args.owner
LOG.warning(_('The --owner option is deprecated, '
'please use --project instead.'))
+ project_id = None
if project_arg:
- kwargs['owner'] = common.find_project(
+ project_id = common.find_project(
identity_client,
project_arg,
parsed_args.project_domain,
).id
+ kwargs['owner'] = project_id
image = utils.find_resource(
image_client.images, parsed_args.image)
@@ -846,6 +865,21 @@ class SetImage(command.Command):
image_client.images.reactivate(image.id)
activation_status = "activated"
+ membership_group_args = ('accept', 'reject', 'pending')
+ membership_status = [status for status in membership_group_args
+ if getattr(parsed_args, status)]
+ if membership_status:
+ # If a specific project is not passed, assume we want to update
+ # our own membership
+ if not project_id:
+ project_id = self.app.client_manager.auth_ref.project_id
+ # The mutually exclusive group of the arg parser ensure we have at
+ # most one item in the membership_status list.
+ if membership_status[0] != 'pending':
+ membership_status[0] += 'ed' # Glance expects the past form
+ image_client.image_members.update(
+ image.id, project_id, membership_status[0])
+
if parsed_args.tags:
# Tags should be extended, but duplicates removed
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 0d8f80d0..9f77aed6 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -140,9 +140,48 @@ class DeleteAddressScope(command.Command):
raise exceptions.CommandError(msg)
+# TODO(yanxing'an): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class ListAddressScope(command.Lister):
_description = _("List address scopes")
+ def get_parser(self, prog_name):
+ parser = super(ListAddressScope, self).get_parser(prog_name)
+
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_("List only address scopes of given name in output")
+ )
+ parser.add_argument(
+ '--ip-version',
+ type=int,
+ choices=[4, 6],
+ metavar='<ip-version>',
+ dest='ip_version',
+ help=_("List address scopes of given IP version networks (4 or 6)")
+ )
+ parser.add_argument(
+ '--project',
+ metavar="<project>",
+ help=_("List address scopes according to their project "
+ "(name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+
+ shared_group = parser.add_mutually_exclusive_group()
+ shared_group.add_argument(
+ '--share',
+ action='store_true',
+ help=_("List address scopes shared between projects")
+ )
+ shared_group.add_argument(
+ '--no-share',
+ action='store_true',
+ help=_("List address scopes not shared between projects")
+ )
+ return parser
+
def take_action(self, parsed_args):
client = self.app.client_manager.network
columns = (
@@ -159,7 +198,27 @@ class ListAddressScope(command.Lister):
'Shared',
'Project',
)
- data = client.address_scopes()
+ attrs = {}
+ if parsed_args.name:
+ attrs['name'] = parsed_args.name
+ if parsed_args.ip_version:
+ attrs['ip_version'] = parsed_args.ip_version
+ if parsed_args.share:
+ attrs['shared'] = True
+ attrs['is_shared'] = True
+ if parsed_args.no_share:
+ attrs['shared'] = False
+ attrs['is_shared'] = False
+ if 'project' in parsed_args and 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
+ attrs['project_id'] = project_id
+ data = client.address_scopes(**attrs)
return (column_headers,
(utils.get_item_properties(
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index a80fe1c4..1d963580 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -107,7 +107,9 @@ class ShowIPAvailability(command.ShowOne):
def take_action(self, parsed_args):
client = self.app.client_manager.network
- obj = client.find_network_ip_availability(parsed_args.network,
+ network_id = client.find_network(parsed_args.network,
+ ignore_missing=False).id
+ obj = client.find_network_ip_availability(network_id,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
diff --git a/openstackclient/network/v2/meter.py b/openstackclient/network/v2/meter.py
new file mode 100644
index 00000000..df0e1da1
--- /dev/null
+++ b/openstackclient/network/v2/meter.py
@@ -0,0 +1,190 @@
+# 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.
+#
+
+"""Metering Label Implementations"""
+
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
+from openstackclient.network import sdk_utils
+
+LOG = logging.getLogger(__name__)
+
+
+def _get_columns(item):
+ column_map = {
+ 'is_shared': 'shared',
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+
+
+def _get_attrs(client_manager, parsed_args):
+ attrs = {}
+
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+ if parsed_args.project is not None and 'project' in parsed_args:
+ 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
+ if parsed_args.share:
+ attrs['shared'] = True
+ if parsed_args.no_share:
+ attrs['shared'] = False
+ if parsed_args.name is not None:
+ attrs['name'] = parsed_args.name
+
+ return attrs
+
+
+# TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
+class CreateMeter(command.ShowOne):
+ _description = _("Create network meter")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateMeter, self).get_parser(prog_name)
+
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("Create description for meter")
+ )
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Owner's project (name or ID)")
+ )
+
+ identity_common.add_project_domain_option_to_parser(parser)
+ share_group = parser.add_mutually_exclusive_group()
+ share_group.add_argument(
+ '--share',
+ action='store_true',
+ default=None,
+ help=_("Share meter between projects")
+ )
+ share_group.add_argument(
+ '--no-share',
+ action='store_true',
+ help=_("Do not share meter between projects")
+ )
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help=_('Name of meter'),
+ )
+
+ 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_metering_label(**attrs)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters={})
+
+ return (display_columns, data)
+
+
+# TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
+class DeleteMeter(command.Command):
+ _description = _("Delete network meter")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteMeter, self).get_parser(prog_name)
+
+ parser.add_argument(
+ 'meter',
+ metavar='<meter>',
+ nargs='+',
+ help=_('Meter to delete (name or ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ result = 0
+
+ for meter in parsed_args.meter:
+ try:
+ obj = client.find_metering_label(meter, ignore_missing=False)
+ client.delete_metering_label(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete meter with "
+ "ID '%(meter)s': %(e)s"),
+ {"meter": meter, "e": e})
+ if result > 0:
+ total = len(parsed_args.meter)
+ msg = (_("%(result)s of %(total)s meters failed "
+ "to delete.") % {"result": result, "total": total})
+ raise exceptions.CommandError(msg)
+
+
+class ListMeter(command.Lister):
+ _description = _("List network meters")
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+
+ columns = (
+ 'id',
+ 'name',
+ 'description',
+ 'shared',
+ )
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Description',
+ 'Shared',
+ )
+
+ data = client.metering_labels()
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))
+
+
+class ShowMeter(command.ShowOne):
+ _description = _("Show network meter")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowMeter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'meter',
+ metavar='<meter>',
+ help=_('Meter to display (name or ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_metering_label(parsed_args.meter,
+ ignore_missing=False)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return display_columns, data
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 1c06c462..26cd02c6 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -19,6 +19,7 @@ from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
+from openstackclient.network import sdk_utils
def _format_admin_state(item):
@@ -31,13 +32,33 @@ def _format_router_external(item):
_formatters = {
'subnets': utils.format_list,
+ 'subnet_ids': utils.format_list,
'admin_state_up': _format_admin_state,
+ 'is_admin_state_up': _format_admin_state,
'router:external': _format_router_external,
+ 'is_router_external': _format_router_external,
'availability_zones': utils.format_list,
'availability_zone_hints': utils.format_list,
}
+def _get_network_columns(item):
+ column_map = {
+ 'subnet_ids': 'subnets',
+ 'is_admin_state_up': 'admin_state_up',
+ 'is_router_external': 'router:external',
+ 'is_port_security_enabled': 'port_security_enabled',
+ 'provider_network_type': 'provider:network_type',
+ 'provider_physical_network': 'provider:physical_network',
+ 'provider_segmentation_id': 'provider:segmentation_id',
+ 'is_shared': 'shared',
+ 'ipv4_address_scope_id': 'ipv4_address_scope',
+ 'ipv6_address_scope_id': 'ipv6_address_scope',
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+
+
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@@ -162,6 +183,8 @@ def _get_attrs_compute(client_manager, parsed_args):
return attrs
+# TODO(sindhu): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class CreateNetwork(common.NetworkAndComputeShowOne):
_description = _("Create new network")
@@ -275,9 +298,9 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
def take_action_network(self, client, parsed_args):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_network(**attrs)
- columns = _get_columns(obj)
+ display_columns, columns = _get_network_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+ return (display_columns, data)
def take_action_compute(self, client, parsed_args):
attrs = _get_attrs_compute(self.app.client_manager, parsed_args)
@@ -313,6 +336,8 @@ class DeleteNetwork(common.NetworkAndComputeDelete):
client.networks.delete(network.id)
+# TODO(sindhu): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class ListNetwork(common.NetworkAndComputeLister):
_description = _("List networks")
@@ -405,12 +430,12 @@ class ListNetwork(common.NetworkAndComputeLister):
'id',
'name',
'status',
- 'tenant_id',
- 'admin_state_up',
- 'shared',
- 'subnets',
+ 'project_id',
+ 'is_admin_state_up',
+ 'is_shared',
+ 'subnet_ids',
'provider_network_type',
- 'router:external',
+ 'is_router_external',
'availability_zones',
)
column_headers = (
@@ -429,7 +454,7 @@ class ListNetwork(common.NetworkAndComputeLister):
columns = (
'id',
'name',
- 'subnets'
+ 'subnet_ids'
)
column_headers = (
'ID',
@@ -441,16 +466,20 @@ class ListNetwork(common.NetworkAndComputeLister):
if parsed_args.external:
args['router:external'] = True
+ args['is_router_external'] = True
elif parsed_args.internal:
args['router:external'] = False
+ args['is_router_external'] = False
if parsed_args.name is not None:
args['name'] = parsed_args.name
if parsed_args.enable:
args['admin_state_up'] = True
+ args['is_admin_state_up'] = True
elif parsed_args.disable:
args['admin_state_up'] = False
+ args['is_admin_state_up'] = False
if parsed_args.project:
project = identity_common.find_project(
@@ -459,21 +488,27 @@ class ListNetwork(common.NetworkAndComputeLister):
parsed_args.project_domain,
)
args['tenant_id'] = project.id
+ args['project_id'] = project.id
if parsed_args.share:
args['shared'] = True
+ args['is_shared'] = True
elif parsed_args.no_share:
args['shared'] = False
+ args['is_shared'] = False
if parsed_args.status:
args['status'] = parsed_args.status
if parsed_args.provider_network_type:
args['provider:network_type'] = parsed_args.provider_network_type
+ args['provider_network_type'] = parsed_args.provider_network_type
if parsed_args.physical_network:
args['provider:physical_network'] = parsed_args.physical_network
+ args['provider_physical_network'] = parsed_args.physical_network
if parsed_args.segmentation_id:
args['provider:segmentation_id'] = parsed_args.segmentation_id
+ args['provider_segmentation_id'] = parsed_args.segmentation_id
data = client.networks(**args)
@@ -504,6 +539,8 @@ class ListNetwork(common.NetworkAndComputeLister):
) for s in data))
+# TODO(sindhu): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class SetNetwork(command.Command):
_description = _("Set network properties")
@@ -619,9 +656,9 @@ class ShowNetwork(common.NetworkAndComputeShowOne):
def take_action_network(self, client, parsed_args):
obj = client.find_network(parsed_args.network, ignore_missing=False)
- columns = _get_columns(obj)
+ display_columns, columns = _get_network_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+ return (display_columns, data)
def take_action_compute(self, client, parsed_args):
obj = utils.find_resource(
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index 65708499..b3411166 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -72,6 +72,25 @@ class DeleteNetworkAgent(command.Command):
class ListNetworkAgent(command.Lister):
_description = _("List network agents")
+ def get_parser(self, prog_name):
+ parser = super(ListNetworkAgent, self).get_parser(prog_name)
+ parser.add_argument(
+ '--agent-type',
+ metavar='<agent-type>',
+ choices=["dhcp", "open-vswitch", "linux-bridge", "ofa", "l3",
+ "loadbalancer", "metering", "metadata", "macvtap", "nic"],
+ help=_("List only agents with the specified agent type. "
+ "The supported agent types are: dhcp, open-vswitch, "
+ "linux-bridge, ofa, l3, loadbalancer, metering, "
+ "metadata, macvtap, nic.")
+ )
+ parser.add_argument(
+ '--host',
+ metavar='<host>',
+ help=_("List only agents running on the specified host")
+ )
+ return parser
+
def take_action(self, parsed_args):
client = self.app.client_manager.network
columns = (
@@ -92,7 +111,27 @@ class ListNetworkAgent(command.Lister):
'State',
'Binary'
)
- data = client.agents()
+
+ key_value = {
+ 'dhcp': 'DHCP agent',
+ 'open-vswitch': 'Open vSwitch agent',
+ 'linux-bridge': 'Linux bridge agent',
+ 'ofa': 'OFA driver agent',
+ 'l3': 'L3 agent',
+ 'loadbalancer': 'Loadbalancer agent',
+ 'metering': 'Metering agent',
+ 'metadata': 'Metadata agent',
+ 'macvtap': 'Macvtap agent',
+ 'nic': 'NIC Switch agent'
+ }
+
+ filters = {}
+ if parsed_args.agent_type is not None:
+ filters['agent_type'] = key_value[parsed_args.agent_type]
+ if parsed_args.host is not None:
+ filters['host'] = parsed_args.host
+
+ data = client.agents(**filters)
return (column_headers,
(utils.get_item_properties(
s, columns, formatters=_formatters,
diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py
new file mode 100644
index 00000000..657eb54b
--- /dev/null
+++ b/openstackclient/network/v2/network_qos_rule_type.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2016, Intel Corporation.
+# 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.
+
+import logging
+
+from osc_lib.command import command
+from osc_lib import utils
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
+
+
+class ListNetworkQosRuleType(command.Lister):
+ _description = _("List QoS rule types")
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ columns = (
+ 'type',
+ )
+ column_headers = (
+ 'Type',
+ )
+ data = client.qos_rule_types()
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns, formatters={},
+ ) for s in data))
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index e837af3a..90754737 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -164,6 +164,30 @@ class DeleteNetworkRBAC(command.Command):
class ListNetworkRBAC(command.Lister):
_description = _("List network RBAC policies")
+ def get_parser(self, prog_name):
+ parser = super(ListNetworkRBAC, self).get_parser(prog_name)
+ parser.add_argument(
+ '--type',
+ metavar='<type>',
+ choices=['qos_policy', 'network'],
+ help=_('List network RBAC policies according to '
+ 'given object type ("qos_policy" or "network")')
+ )
+ parser.add_argument(
+ '--action',
+ metavar='<action>',
+ choices=['access_as_external', 'access_as_shared'],
+ help=_('List network RBAC policies according to given '
+ 'action ("access_as_external" or "access_as_shared")')
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_("List additional fields in output")
+ )
+ return parser
+
def take_action(self, parsed_args):
client = self.app.client_manager.network
@@ -178,7 +202,17 @@ class ListNetworkRBAC(command.Lister):
'Object ID',
)
- data = client.rbac_policies()
+ query = {}
+ if parsed_args.long:
+ columns += ('action',)
+ column_headers += ('Action',)
+ if parsed_args.type is not None:
+ query['object_type'] = parsed_args.type
+ if parsed_args.action is not None:
+ query['action'] = parsed_args.action
+
+ data = client.rbac_policies(**query)
+
return (column_headers,
(utils.get_item_properties(
s, columns,
diff --git a/openstackclient/network/v2/network_service_provider.py b/openstackclient/network/v2/network_service_provider.py
index 3aa33c23..157948cc 100644
--- a/openstackclient/network/v2/network_service_provider.py
+++ b/openstackclient/network/v2/network_service_provider.py
@@ -16,9 +16,11 @@
from osc_lib.command import command
from osc_lib import utils
+from openstackclient.i18n import _
+
class ListNetworkServiceProvider(command.Lister):
- """List Service Providers"""
+ _description = _("List Service Providers")
def take_action(self, parsed_args):
client = self.app.client_manager.network
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index bce3e2d3..4525da18 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -25,6 +25,7 @@ from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
@@ -36,34 +37,33 @@ def _format_admin_state(state):
_formatters = {
'admin_state_up': _format_admin_state,
+ 'is_admin_state_up': _format_admin_state,
'allowed_address_pairs': utils.format_list_of_dicts,
'binding_profile': utils.format_dict,
'binding_vif_details': utils.format_dict,
+ 'binding:profile': utils.format_dict,
+ 'binding:vif_details': utils.format_dict,
'dns_assignment': utils.format_list_of_dicts,
'extra_dhcp_opts': utils.format_list_of_dicts,
'fixed_ips': utils.format_list_of_dicts,
+ 'security_group_ids': utils.format_list,
'security_groups': utils.format_list,
}
def _get_columns(item):
- columns = list(item.keys())
- if 'tenant_id' in columns:
- columns.remove('tenant_id')
- if 'project_id' not in columns:
- columns.append('project_id')
- binding_columns = [
- 'binding:host_id',
- 'binding:profile',
- 'binding:vif_details',
- 'binding:vif_type',
- 'binding:vnic_type',
- ]
- for binding_column in binding_columns:
- if binding_column in columns:
- columns.remove(binding_column)
- columns.append(binding_column.replace('binding:', 'binding_', 1))
- return tuple(sorted(columns))
+ column_map = {
+ 'binding:host_id': 'binding_host_id',
+ 'binding:profile': 'binding_profile',
+ 'binding:vif_details': 'binding_vif_details',
+ 'binding:vif_type': 'binding_vif_type',
+ 'binding:vnic_type': 'binding_vnic_type',
+ 'is_admin_state_up': 'admin_state_up',
+ 'is_port_security_enabled': 'port_security_enabled',
+ 'security_group_ids': 'security_groups',
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class JSONKeyValueAction(argparse.Action):
@@ -244,6 +244,8 @@ def _add_updatable_args(parser):
)
+# TODO(abhiraut): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class CreatePort(command.ShowOne):
_description = _("Create a new port")
@@ -349,10 +351,10 @@ class CreatePort(command.ShowOne):
attrs['security_groups'] = []
obj = client.create_port(**attrs)
- columns = _get_columns(obj)
+ display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+ return (display_columns, data)
class DeletePort(command.Command):
@@ -389,6 +391,8 @@ class DeletePort(command.Command):
raise exceptions.CommandError(msg)
+# TODO(abhiraut): Use only the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class ListPort(command.Lister):
_description = _("List ports")
@@ -451,7 +455,7 @@ class ListPort(command.Lister):
filters = {}
if parsed_args.long:
- columns += ('security_groups', 'device_owner',)
+ columns += ('security_group_ids', 'device_owner',)
column_headers += ('Security Groups', 'Device Owner',)
if parsed_args.device_owner is not None:
filters['device_owner'] = parsed_args.device_owner
@@ -479,6 +483,8 @@ class ListPort(command.Lister):
) for s in data))
+# TODO(abhiraut): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class SetPort(command.Command):
_description = _("Set port properties")
@@ -621,11 +627,13 @@ class ShowPort(command.ShowOne):
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_port(parsed_args.port, ignore_missing=False)
- columns = _get_columns(obj)
+ display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+ return (display_columns, data)
+# TODO(abhiraut): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class UnsetPort(command.Command):
_description = _("Unset port properties")
@@ -666,7 +674,7 @@ class UnsetPort(command.Command):
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_port(parsed_args.port, ignore_missing=False)
- # SDK ignores update() if it recieves a modified obj and attrs
+ # SDK ignores update() if it receives a modified obj and attrs
# To handle the same tmp_obj is created in all take_action of
# Unset* classes
tmp_fixed_ips = copy.deepcopy(obj.fixed_ips)
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index cdd634d0..aad35a9e 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -52,6 +52,7 @@ def _format_routes(routes):
_formatters = {
'admin_state_up': _format_admin_state,
+ 'is_admin_state_up': _format_admin_state,
'external_gateway_info': _format_external_gateway_info,
'availability_zones': utils.format_list,
'availability_zone_hints': utils.format_list,
@@ -62,6 +63,9 @@ _formatters = {
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
+ 'is_ha': 'ha',
+ 'is_distributed': 'distributed',
+ 'is_admin_state_up': 'admin_state_up',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
@@ -94,8 +98,6 @@ def _get_attrs(client_manager, parsed_args):
).id
attrs['tenant_id'] = project_id
- # TODO(tangchen): Support getting 'external_gateway_info' property.
-
return attrs
@@ -150,6 +152,8 @@ class AddSubnetToRouter(command.Command):
subnet_id=subnet.id)
+# TODO(yanxing'an): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class CreateRouter(command.ShowOne):
_description = _("Create a new router")
@@ -255,6 +259,8 @@ class DeleteRouter(command.Command):
raise exceptions.CommandError(msg)
+# TODO(yanxing'an): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class ListRouter(command.Lister):
_description = _("List routers")
@@ -297,10 +303,10 @@ class ListRouter(command.Lister):
'id',
'name',
'status',
- 'admin_state_up',
- 'distributed',
- 'ha',
- 'tenant_id',
+ 'is_admin_state_up',
+ 'is_distributed',
+ 'is_ha',
+ 'project_id',
)
column_headers = (
'ID',
@@ -319,8 +325,10 @@ class ListRouter(command.Lister):
if parsed_args.enable:
args['admin_state_up'] = True
+ args['is_admin_state_up'] = True
elif parsed_args.disable:
args['admin_state_up'] = False
+ args['is_admin_state_up'] = False
if parsed_args.project:
project_id = identity_common.find_project(
@@ -329,6 +337,7 @@ class ListRouter(command.Lister):
parsed_args.project_domain,
).id
args['tenant_id'] = project_id
+ args['project_id'] = project_id
if parsed_args.long:
columns = columns + (
'routes',
@@ -407,6 +416,8 @@ class RemoveSubnetFromRouter(command.Command):
subnet_id=subnet.id)
+# TODO(yanxing'an): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
class SetRouter(command.Command):
_description = _("Set router properties")
@@ -451,7 +462,8 @@ class SetRouter(command.Command):
help=_("Set router to centralized mode (disabled router only)")
)
routes_group = parser.add_mutually_exclusive_group()
- routes_group.add_argument(
+ # ToDo(Reedip):Remove mutual exclusiveness once clear-routes is removed
+ parser.add_argument(
'--route',
metavar='destination=<subnet>,gateway=<ip-address>',
action=parseractions.MultiKeyValueAction,
@@ -466,7 +478,9 @@ class SetRouter(command.Command):
routes_group.add_argument(
'--no-route',
action='store_true',
- help=_("Clear routes associated with the router")
+ help=_("Clear routes associated with the router. "
+ "Specify both --route and --no-route to overwrite "
+ "current value of route.")
)
routes_group.add_argument(
'--clear-routes',
@@ -483,12 +497,35 @@ class SetRouter(command.Command):
routes_ha.add_argument(
'--no-ha',
action='store_true',
- help=_("Clear high availablability attribute of the router "
+ help=_("Clear high availability attribute of the router "
"(disabled router only)")
)
- # TODO(tangchen): Support setting 'external_gateway_info' property in
- # 'router set' command.
-
+ parser.add_argument(
+ '--external-gateway',
+ metavar="<network>",
+ help=_("External Network used as router's gateway (name or ID)")
+ )
+ 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)"
+ "on external gateway: "
+ "subnet=<subnet>,ip-address=<ip-address> "
+ "(repeat option to set multiple fixed IP addresses)")
+ )
+ snat_group = parser.add_mutually_exclusive_group()
+ snat_group.add_argument(
+ '--enable-snat',
+ action='store_true',
+ help=_("Enable Source NAT on external gateway")
+ )
+ snat_group.add_argument(
+ '--disable-snat',
+ action='store_true',
+ help=_("Disable Source NAT on external gateway")
+ )
return parser
def take_action(self, parsed_args):
@@ -503,21 +540,50 @@ class SetRouter(command.Command):
attrs['ha'] = True
elif parsed_args.no_ha:
attrs['ha'] = False
- if parsed_args.no_route:
- attrs['routes'] = []
- elif parsed_args.clear_routes:
- attrs['routes'] = []
+ if parsed_args.clear_routes:
LOG.warning(_(
'The --clear-routes option is deprecated, '
'please use --no-route instead.'
))
- elif parsed_args.routes is not None:
- # Map the route keys and append to the current routes.
- # The REST API will handle route validation and duplicates.
+
+ if parsed_args.routes is not None:
for route in parsed_args.routes:
route['nexthop'] = route.pop('gateway')
- attrs['routes'] = obj.routes + parsed_args.routes
-
+ attrs['routes'] = parsed_args.routes
+ if not (parsed_args.no_route or parsed_args.clear_routes):
+ # Map the route keys and append to the current routes.
+ # The REST API will handle route validation and duplicates.
+ attrs['routes'] += obj.routes
+ elif parsed_args.no_route or parsed_args.clear_routes:
+ attrs['routes'] = []
+ if (parsed_args.disable_snat or parsed_args.enable_snat or
+ parsed_args.fixed_ip) and not parsed_args.external_gateway:
+ msg = (_("You must specify '--external-gateway' in order"
+ "to update the SNAT or fixed-ip values"))
+ raise exceptions.CommandError(msg)
+ if parsed_args.external_gateway:
+ gateway_info = {}
+ network = client.find_network(
+ parsed_args.external_gateway, ignore_missing=False)
+ gateway_info['network_id'] = network.id
+ if parsed_args.disable_snat:
+ gateway_info['enable_snat'] = False
+ if parsed_args.enable_snat:
+ gateway_info['enable_snat'] = True
+ if parsed_args.fixed_ip:
+ ips = []
+ for ip_spec in parsed_args.fixed_ip:
+ if ip_spec.get('subnet', False):
+ subnet_name_id = ip_spec.pop('subnet')
+ if subnet_name_id:
+ subnet = client.find_subnet(subnet_name_id,
+ ignore_missing=False)
+ ip_spec['subnet_id'] = subnet.id
+ if ip_spec.get('ip-address', False):
+ ip_spec['ip_address'] = ip_spec.pop('ip-address')
+ ips.append(ip_spec)
+ gateway_info['external_fixed_ips'] = ips
+ attrs['external_gateway_info'] = gateway_info
client.update_router(obj, **attrs)
@@ -559,6 +625,11 @@ class UnsetRouter(command.Command):
"gateway: nexthop IP address "
"(repeat option to unset multiple routes)"))
parser.add_argument(
+ '--external-gateway',
+ action='store_true',
+ default=False,
+ help=_("Remove external gateway information from the router"))
+ parser.add_argument(
'router',
metavar="<router>",
help=_("Router to modify (name or ID)")
@@ -579,5 +650,7 @@ class UnsetRouter(command.Command):
msg = (_("Router does not contain route %s") % route)
raise exceptions.CommandError(msg)
attrs['routes'] = tmp_routes
+ if parsed_args.external_gateway:
+ attrs['external_gateway_info'] = {}
if attrs:
client.update_router(obj, **attrs)
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 5420bc8b..df9e320b 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -74,7 +74,7 @@ _formatters_compute = {
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())
+ columns = list(item.to_dict().keys())
property_column_mappings = []
if 'security_group_rules' in columns:
columns.append('rules')
@@ -156,7 +156,7 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
parsed_args.name,
description,
)
- display_columns, property_columns = _get_columns(obj._info)
+ display_columns, property_columns = _get_columns(obj)
data = utils.get_dict_properties(
obj._info,
property_columns,
@@ -336,7 +336,7 @@ class ShowSecurityGroup(common.NetworkAndComputeShowOne):
client.security_groups,
parsed_args.group,
)
- display_columns, property_columns = _get_columns(obj._info)
+ display_columns, property_columns = _get_columns(obj)
data = utils.get_dict_properties(
obj._info,
property_columns,
diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py
index c1de9aa9..fbb8e563 100644
--- a/openstackclient/tests/functional/common/test_quota.py
+++ b/openstackclient/tests/functional/common/test_quota.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from openstackclient.tests.functional import base
@@ -25,6 +27,7 @@ class QuotaTests(base.TestCase):
cls.PROJECT_NAME =\
cls.get_openstack_configuration_value('auth.project_name')
+ @testtools.skip('broken SDK testing')
def test_quota_set(self):
self.openstack('quota set --instances 11 --volumes 11 --networks 11 ' +
self.PROJECT_NAME)
@@ -32,16 +35,19 @@ class QuotaTests(base.TestCase):
raw_output = self.openstack('quota show ' + self.PROJECT_NAME + opts)
self.assertEqual("11\n11\n11\n", raw_output)
+ @testtools.skip('broken SDK testing')
def test_quota_show(self):
raw_output = self.openstack('quota show ' + self.PROJECT_NAME)
for expected_field in self.EXPECTED_FIELDS:
self.assertIn(expected_field, raw_output)
+ @testtools.skip('broken SDK testing')
def test_quota_show_default_project(self):
raw_output = self.openstack('quota show')
for expected_field in self.EXPECTED_FIELDS:
self.assertIn(expected_field, raw_output)
+ @testtools.skip('broken SDK testing')
def test_quota_show_with_default_option(self):
raw_output = self.openstack('quota show --default')
for expected_field in self.EXPECTED_FIELDS:
diff --git a/openstackclient/tests/functional/compute/v2/test_flavor.py b/openstackclient/tests/functional/compute/v2/test_flavor.py
index 794a6cc3..0b01da51 100644
--- a/openstackclient/tests/functional/compute/v2/test_flavor.py
+++ b/openstackclient/tests/functional/compute/v2/test_flavor.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
import uuid
from openstackclient.tests.functional import base
@@ -18,52 +19,224 @@ from openstackclient.tests.functional import base
class FlavorTests(base.TestCase):
"""Functional tests for flavor."""
- NAME = uuid.uuid4().hex
- HEADERS = ['Name']
- FIELDS = ['name']
+ PROJECT_NAME = uuid.uuid4().hex
@classmethod
def setUpClass(cls):
- opts = cls.get_opts(cls.FIELDS)
- raw_output = cls.openstack(
- 'flavor create --property a=b --property c=d ' + cls.NAME + opts)
- expected = cls.NAME + '\n'
- cls.assertOutput(expected, raw_output)
+ # Make a project
+ cmd_output = json.loads(cls.openstack(
+ "project create -f json --enable " + cls.PROJECT_NAME
+ ))
+ cls.project_id = cmd_output["id"]
@classmethod
def tearDownClass(cls):
- raw_output = cls.openstack('flavor delete ' + cls.NAME)
+ raw_output = cls.openstack("project delete " + cls.PROJECT_NAME)
cls.assertOutput('', raw_output)
+ def test_flavor_delete(self):
+ """Test create w/project, delete multiple"""
+ name1 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ "flavor create -f json " +
+ "--project " + self.PROJECT_NAME + " " +
+ "--private " +
+ name1
+ ))
+ self.assertIsNotNone(cmd_output["id"])
+
+ name2 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ "flavor create -f json " +
+ "--id qaz " +
+ "--project " + self.PROJECT_NAME + " " +
+ "--private " +
+ name2
+ ))
+ self.assertIsNotNone(cmd_output["id"])
+ self.assertEqual(
+ "qaz",
+ cmd_output["id"],
+ )
+
+ raw_output = self.openstack(
+ "flavor delete " + name1 + " " + name2,
+ )
+ self.assertOutput('', raw_output)
+
def test_flavor_list(self):
- opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('flavor list' + opts)
- self.assertIn("small", raw_output)
- self.assertIn(self.NAME, raw_output)
+ """Test create defaults, list filters, delete"""
+ name1 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ "flavor create -f json " +
+ "--property a=b " +
+ "--property c=d " +
+ name1
+ ))
+ self.addCleanup(self.openstack, "flavor delete " + name1)
+ self.assertIsNotNone(cmd_output["id"])
+ self.assertEqual(
+ name1,
+ cmd_output["name"],
+ )
+
+ name2 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ "flavor create -f json " +
+ "--id qaz " +
+ "--ram 123 " +
+ "--private " +
+ "--property a=b2 " +
+ "--property b=d2 " +
+ name2
+ ))
+ self.addCleanup(self.openstack, "flavor delete " + name2)
+ self.assertIsNotNone(cmd_output["id"])
+ self.assertEqual(
+ "qaz",
+ cmd_output["id"],
+ )
+ self.assertEqual(
+ name2,
+ cmd_output["name"],
+ )
+ self.assertEqual(
+ 123,
+ cmd_output["ram"],
+ )
+ self.assertEqual(
+ 0,
+ cmd_output["disk"],
+ )
+ self.assertEqual(
+ False,
+ cmd_output["os-flavor-access:is_public"],
+ )
+ self.assertEqual(
+ "a='b2', b='d2'",
+ cmd_output["properties"],
+ )
- def test_flavor_show(self):
- opts = self.get_opts(self.FIELDS)
- raw_output = self.openstack('flavor show ' + self.NAME + opts)
- self.assertEqual(self.NAME + "\n", raw_output)
+ # Test list
+ cmd_output = json.loads(self.openstack(
+ "flavor list -f json"
+ ))
+ col_name = [x["Name"] for x in cmd_output]
+ self.assertIn(name1, col_name)
+ self.assertNotIn(name2, col_name)
+
+ # Test list --long
+ cmd_output = json.loads(self.openstack(
+ "flavor list -f json " +
+ "--long"
+ ))
+ col_name = [x["Name"] for x in cmd_output]
+ col_properties = [x['Properties'] for x in cmd_output]
+ self.assertIn(name1, col_name)
+ self.assertIn("a='b', c='d'", col_properties)
+ self.assertNotIn(name2, col_name)
+ self.assertNotIn("b2', b='d2'", col_properties)
+
+ # Test list --public
+ cmd_output = json.loads(self.openstack(
+ "flavor list -f json " +
+ "--public"
+ ))
+ col_name = [x["Name"] for x in cmd_output]
+ self.assertIn(name1, col_name)
+ self.assertNotIn(name2, col_name)
+
+ # Test list --private
+ cmd_output = json.loads(self.openstack(
+ "flavor list -f json " +
+ "--private"
+ ))
+ col_name = [x["Name"] for x in cmd_output]
+ self.assertNotIn(name1, col_name)
+ self.assertIn(name2, col_name)
+
+ # Test list --all
+ cmd_output = json.loads(self.openstack(
+ "flavor list -f json " +
+ "--all"
+ ))
+ col_name = [x["Name"] for x in cmd_output]
+ self.assertIn(name1, col_name)
+ self.assertIn(name2, col_name)
def test_flavor_properties(self):
- opts = self.get_opts(['properties'])
- # check the properties we added in create command.
- raw_output = self.openstack('flavor show ' + self.NAME + opts)
- self.assertEqual("a='b', c='d'\n", raw_output)
+ """Test create defaults, list filters, delete"""
+ name1 = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ "flavor create -f json " +
+ "--id qaz " +
+ "--ram 123 " +
+ "--disk 20 " +
+ "--private " +
+ "--property a=first " +
+ "--property b=second " +
+ name1
+ ))
+ self.addCleanup(self.openstack, "flavor delete " + name1)
+ self.assertIsNotNone(cmd_output["id"])
+ self.assertEqual(
+ "qaz",
+ cmd_output["id"],
+ )
+ self.assertEqual(
+ name1,
+ cmd_output["name"],
+ )
+ self.assertEqual(
+ 123,
+ cmd_output["ram"],
+ )
+ self.assertEqual(
+ 20,
+ cmd_output["disk"],
+ )
+ self.assertEqual(
+ False,
+ cmd_output["os-flavor-access:is_public"],
+ )
+ self.assertEqual(
+ "a='first', b='second'",
+ cmd_output["properties"],
+ )
raw_output = self.openstack(
- 'flavor set --property e=f --property g=h ' + self.NAME
+ "flavor set " +
+ "--property a='third and 10' " +
+ "--property g=fourth " +
+ name1
)
self.assertEqual('', raw_output)
- raw_output = self.openstack('flavor show ' + self.NAME + opts)
- self.assertEqual("a='b', c='d', e='f', g='h'\n", raw_output)
+ cmd_output = json.loads(self.openstack(
+ "flavor show -f json " +
+ name1
+ ))
+ self.assertEqual(
+ "qaz",
+ cmd_output["id"],
+ )
+ self.assertEqual(
+ "a='third and 10', b='second', g='fourth'",
+ cmd_output['properties'],
+ )
raw_output = self.openstack(
- 'flavor unset --property a --property c ' + self.NAME
+ "flavor unset " +
+ "--property b " +
+ name1
)
self.assertEqual('', raw_output)
- raw_output = self.openstack('flavor show ' + self.NAME + opts)
- self.assertEqual("e='f', g='h'\n", raw_output)
+ cmd_output = json.loads(self.openstack(
+ "flavor show -f json " +
+ name1
+ ))
+ self.assertEqual(
+ "a='third and 10', g='fourth'",
+ cmd_output["properties"],
+ )
diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py
index 5dd42e70..3b6fc27b 100644
--- a/openstackclient/tests/functional/identity/v3/common.py
+++ b/openstackclient/tests/functional/identity/v3/common.py
@@ -42,7 +42,8 @@ class IdentityTests(base.TestCase):
ENDPOINT_LIST_HEADERS = ['ID', 'Region', 'Service Name', 'Service Type',
'Enabled', 'Interface', 'URL']
- IDENTITY_PROVIDER_FIELDS = ['description', 'enabled', 'id', 'remote_ids']
+ IDENTITY_PROVIDER_FIELDS = ['description', 'enabled', 'id', 'remote_ids',
+ 'domain_id']
IDENTITY_PROVIDER_LIST_HEADERS = ['ID', 'Enabled', 'Description']
SERVICE_PROVIDER_FIELDS = ['auth_url', 'description', 'enabled',
diff --git a/openstackclient/tests/functional/identity/v3/test_idp.py b/openstackclient/tests/functional/identity/v3/test_idp.py
index f9d8cb80..5db3610a 100644
--- a/openstackclient/tests/functional/identity/v3/test_idp.py
+++ b/openstackclient/tests/functional/identity/v3/test_idp.py
@@ -10,9 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from openstackclient.tests.functional.identity.v3 import common
from tempest.lib.common.utils import data_utils
+from openstackclient.tests.functional.identity.v3 import common
+
class IdentityProviderTests(common.IdentityTests):
# Introduce functional test case for command 'Identity Provider'
diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py
index 3f432b02..6faff94a 100644
--- a/openstackclient/tests/functional/image/v2/test_image.py
+++ b/openstackclient/tests/functional/image/v2/test_image.py
@@ -74,3 +74,25 @@ class ImageTests(base.TestCase):
self.openstack('image unset --property a --property c ' + self.NAME)
raw_output = self.openstack('image show ' + self.NAME + opts)
self.assertEqual(self.NAME + "\n\n", raw_output)
+
+ def test_image_members(self):
+ opts = self.get_opts(['project_id'])
+ my_project_id = self.openstack('token issue' + opts).strip()
+ self.openstack(
+ 'image add project {} {}'.format(self.NAME, my_project_id))
+
+ self.openstack(
+ 'image set --accept ' + self.NAME)
+ shared_img_list = self.parse_listing(
+ self.openstack('image list --shared', self.get_opts(['name']))
+ )
+ self.assertIn(self.NAME, [img['Name'] for img in shared_img_list])
+
+ self.openstack(
+ 'image set --reject ' + self.NAME)
+ shared_img_list = self.parse_listing(
+ self.openstack('image list --shared', self.get_opts(['name']))
+ )
+
+ self.openstack(
+ 'image remove project {} {}'.format(self.NAME, my_project_id))
diff --git a/openstackclient/tests/functional/network/v2/test_address_scope.py b/openstackclient/tests/functional/network/v2/test_address_scope.py
index ef4b5756..75f84344 100644
--- a/openstackclient/tests/functional/network/v2/test_address_scope.py
+++ b/openstackclient/tests/functional/network/v2/test_address_scope.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import re
import uuid
from openstackclient.tests.functional import base
@@ -17,33 +18,138 @@ from openstackclient.tests.functional import base
class AddressScopeTests(base.TestCase):
"""Functional tests for address scope. """
- NAME = uuid.uuid4().hex
- HEADERS = ['Name']
- FIELDS = ['name']
+
+ # NOTE(dtroyer): Do not normalize the setup and teardown of the resource
+ # creation and deletion. Little is gained when each test
+ # has its own needs and there are collisions when running
+ # tests in parallel.
@classmethod
def setUpClass(cls):
- opts = cls.get_opts(cls.FIELDS)
- raw_output = cls.openstack('address scope create ' + cls.NAME + opts)
- cls.assertOutput(cls.NAME + "\n", raw_output)
+ # Set up some regex for matching below
+ cls.re_name = re.compile("name\s+\|\s+([^|]+?)\s+\|")
+ cls.re_ip_version = re.compile("ip_version\s+\|\s+(\S+)")
+ cls.re_shared = re.compile("shared\s+\|\s+(\S+)")
- @classmethod
- def tearDownClass(cls):
- raw_output = cls.openstack('address scope delete ' + cls.NAME)
- cls.assertOutput('', raw_output)
+ def test_address_scope_delete(self):
+ """Test create, delete multiple"""
+ name1 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'address scope create ' + name1,
+ )
+ self.assertEqual(
+ name1,
+ re.search(self.re_name, raw_output).group(1),
+ )
+ # Check the default values
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+
+ name2 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'address scope create ' + name2,
+ )
+ self.assertEqual(
+ name2,
+ re.search(self.re_name, raw_output).group(1),
+ )
+
+ raw_output = self.openstack(
+ 'address scope delete ' + name1 + ' ' + name2,
+ )
+ self.assertOutput('', raw_output)
def test_address_scope_list(self):
- opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('address scope list' + opts)
- self.assertIn(self.NAME, raw_output)
+ """Test create defaults, list filters, delete"""
+ name1 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'address scope create --ip-version 4 --share ' + name1,
+ )
+ self.addCleanup(self.openstack, 'address scope delete ' + name1)
+ self.assertEqual(
+ '4',
+ re.search(self.re_ip_version, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'True',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+
+ name2 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'address scope create --ip-version 6 --no-share ' + name2,
+ )
+ self.addCleanup(self.openstack, 'address scope delete ' + name2)
+ self.assertEqual(
+ '6',
+ re.search(self.re_ip_version, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1),
+ )
- def test_address_scope_show(self):
- opts = self.get_opts(self.FIELDS)
- raw_output = self.openstack('address scope show ' + self.NAME + opts)
- self.assertEqual(self.NAME + "\n", raw_output)
+ # Test list
+ raw_output = self.openstack('address scope list')
+ self.assertIsNotNone(re.search(name1 + "\s+\|\s+4", raw_output))
+ self.assertIsNotNone(re.search(name2 + "\s+\|\s+6", raw_output))
+
+ # Test list --share
+ # TODO(dtroyer): returns 'HttpException: Bad Request'
+ # raw_output = self.openstack('address scope list --share')
+ # self.assertIsNotNone(re.search(name1 + "\s+\|\s+4", raw_output))
+ # self.assertIsNotNone(re.search(name2 + "\s+\|\s+6", raw_output))
+
+ # Test list --no-share
+ # TODO(dtroyer): returns 'HttpException: Bad Request'
+ # raw_output = self.openstack('address scope list --no-share')
+ # self.assertIsNotNone(re.search(name1 + "\s+\|\s+4", raw_output))
+ # self.assertIsNotNone(re.search(name2 + "\s+\|\s+6", raw_output))
def test_address_scope_set(self):
- self.openstack('address scope set --share ' + self.NAME)
- opts = self.get_opts(['shared'])
- raw_output = self.openstack('address scope show ' + self.NAME + opts)
- self.assertEqual("True\n", raw_output)
+ """Tests create options, set, show, delete"""
+ name = uuid.uuid4().hex
+ newname = name + "_"
+ raw_output = self.openstack(
+ 'address scope create ' +
+ '--ip-version 4 ' +
+ '--no-share ' +
+ name,
+ )
+ self.addCleanup(self.openstack, 'address scope delete ' + newname)
+ self.assertEqual(
+ name,
+ re.search(self.re_name, raw_output).group(1),
+ )
+ self.assertEqual(
+ '4',
+ re.search(self.re_ip_version, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+
+ raw_output = self.openstack(
+ 'address scope set ' +
+ '--name ' + newname +
+ ' --share ' +
+ name,
+ )
+ self.assertOutput('', raw_output)
+
+ raw_output = self.openstack('address scope show ' + newname)
+ self.assertEqual(
+ newname,
+ re.search(self.re_name, raw_output).group(1),
+ )
+ self.assertEqual(
+ '4',
+ re.search(self.re_ip_version, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'True',
+ re.search(self.re_shared, raw_output).group(1),
+ )
diff --git a/openstackclient/tests/functional/network/v2/test_floating_ip.py b/openstackclient/tests/functional/network/v2/test_floating_ip.py
index f3a1971f..5f642f04 100644
--- a/openstackclient/tests/functional/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/functional/network/v2/test_floating_ip.py
@@ -10,49 +10,154 @@
# License for the specific language governing permissions and limitations
# under the License.
+import random
+import re
import uuid
+import testtools
+
from openstackclient.tests.functional import base
class FloatingIpTests(base.TestCase):
- """Functional tests for floating ip. """
+ """Functional tests for floating ip"""
SUBNET_NAME = uuid.uuid4().hex
NETWORK_NAME = uuid.uuid4().hex
- ID = None
- HEADERS = ['ID']
- FIELDS = ['id']
@classmethod
+ @testtools.skip('broken SDK testing')
def setUpClass(cls):
- # Create a network for the floating ip.
- cls.openstack('network create --external ' + cls.NETWORK_NAME)
- # Create a subnet for the network.
- cls.openstack(
- 'subnet create --network ' + cls.NETWORK_NAME +
- ' --subnet-range 10.10.10.0/24 ' +
- cls.SUBNET_NAME
+ # Set up some regex for matching below
+ cls.re_id = re.compile("id\s+\|\s+(\S+)")
+ cls.re_floating_ip = re.compile("floating_ip_address\s+\|\s+(\S+)")
+ cls.re_fixed_ip = re.compile("fixed_ip_address\s+\|\s+(\S+)")
+ cls.re_description = re.compile("description\s+\|\s+([^|]+?)\s+\|")
+ cls.re_network_id = re.compile("floating_network_id\s+\|\s+(\S+)")
+
+ # Make a random subnet
+ cls.subnet = ".".join(map(
+ str,
+ (random.randint(0, 255) for _ in range(3))
+ )) + ".0/26"
+
+ # Create a network for the floating ip
+ raw_output = cls.openstack(
+ 'network create --external ' + cls.NETWORK_NAME
)
- opts = cls.get_opts(cls.FIELDS)
+ cls.network_id = re.search(cls.re_id, raw_output).group(1)
+
+ # Create a subnet for the network
raw_output = cls.openstack(
- 'floating ip create ' + cls.NETWORK_NAME + opts)
- cls.ID = raw_output.strip('\n')
+ 'subnet create ' +
+ '--network ' + cls.NETWORK_NAME + ' ' +
+ '--subnet-range ' + cls.subnet + ' ' +
+ cls.SUBNET_NAME
+ )
+ cls.subnet_id = re.search(cls.re_id, raw_output).group(1)
@classmethod
def tearDownClass(cls):
- raw_output = cls.openstack('floating ip delete ' + cls.ID)
- cls.assertOutput('', raw_output)
raw_output = cls.openstack('subnet delete ' + cls.SUBNET_NAME)
cls.assertOutput('', raw_output)
raw_output = cls.openstack('network delete ' + cls.NETWORK_NAME)
cls.assertOutput('', raw_output)
+ @testtools.skip('broken SDK testing')
+ def test_floating_ip_delete(self):
+ """Test create, delete multiple"""
+ raw_output = self.openstack(
+ 'floating ip create ' +
+ '--description aaaa ' +
+ self.NETWORK_NAME
+ )
+ re_ip = re.search(self.re_floating_ip, raw_output)
+ self.assertIsNotNone(re_ip)
+ ip1 = re_ip.group(1)
+ self.assertEqual(
+ 'aaaa',
+ re.search(self.re_description, raw_output).group(1),
+ )
+
+ raw_output = self.openstack(
+ 'floating ip create ' +
+ '--description bbbb ' +
+ self.NETWORK_NAME
+ )
+ ip2 = re.search(self.re_floating_ip, raw_output).group(1)
+ self.assertEqual(
+ 'bbbb',
+ re.search(self.re_description, raw_output).group(1),
+ )
+
+ # Clean up after ourselves
+ raw_output = self.openstack('floating ip delete ' + ip1 + ' ' + ip2)
+ self.assertOutput('', raw_output)
+
+ @testtools.skip('broken SDK testing')
def test_floating_ip_list(self):
- opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('floating ip list' + opts)
- self.assertIn(self.ID, raw_output)
+ """Test create defaults, list filters, delete"""
+ raw_output = self.openstack(
+ 'floating ip create ' +
+ '--description aaaa ' +
+ self.NETWORK_NAME
+ )
+ re_ip = re.search(self.re_floating_ip, raw_output)
+ self.assertIsNotNone(re_ip)
+ ip1 = re_ip.group(1)
+ self.addCleanup(self.openstack, 'floating ip delete ' + ip1)
+ self.assertEqual(
+ 'aaaa',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ self.assertIsNotNone(re.search(self.re_network_id, raw_output))
+
+ raw_output = self.openstack(
+ 'floating ip create ' +
+ '--description bbbb ' +
+ self.NETWORK_NAME
+ )
+ ip2 = re.search(self.re_floating_ip, raw_output).group(1)
+ self.addCleanup(self.openstack, 'floating ip delete ' + ip2)
+ self.assertEqual(
+ 'bbbb',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ # Test list
+ raw_output = self.openstack('floating ip list')
+ self.assertIsNotNone(re.search("\|\s+" + ip1 + "\s+\|", raw_output))
+ self.assertIsNotNone(re.search("\|\s+" + ip2 + "\s+\|", raw_output))
+
+ # Test list --long
+ raw_output = self.openstack('floating ip list --long')
+ self.assertIsNotNone(re.search("\|\s+" + ip1 + "\s+\|", raw_output))
+ self.assertIsNotNone(re.search("\|\s+" + ip2 + "\s+\|", raw_output))
+
+ # TODO(dtroyer): add more filter tests
+
+ @testtools.skip('broken SDK testing')
def test_floating_ip_show(self):
- opts = self.get_opts(self.FIELDS)
- raw_output = self.openstack('floating ip show ' + self.ID + opts)
- self.assertEqual(self.ID + "\n", raw_output)
+ """Test show"""
+ raw_output = self.openstack(
+ 'floating ip create ' +
+ '--description shosho ' +
+ # '--fixed-ip-address 1.2.3.4 ' +
+ self.NETWORK_NAME
+ )
+ re_ip = re.search(self.re_floating_ip, raw_output)
+ self.assertIsNotNone(re_ip)
+ ip = re_ip.group(1)
+
+ raw_output = self.openstack('floating ip show ' + ip)
+ self.addCleanup(self.openstack, 'floating ip delete ' + ip)
+
+ self.assertEqual(
+ 'shosho',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ # TODO(dtroyer): not working???
+ # self.assertEqual(
+ # '1.2.3.4',
+ # re.search(self.re_floating_ip, raw_output).group(1),
+ # )
+ self.assertIsNotNone(re.search(self.re_network_id, raw_output))
diff --git a/openstackclient/tests/functional/network/v2/test_meter.py b/openstackclient/tests/functional/network/v2/test_meter.py
new file mode 100644
index 00000000..7dce34e7
--- /dev/null
+++ b/openstackclient/tests/functional/network/v2/test_meter.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2016, Intel Corporation.
+# 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.
+
+import re
+import uuid
+
+from openstackclient.tests.functional import base
+
+
+class TestMeter(base.TestCase):
+ """Functional tests for network meter."""
+
+ # NOTE(dtroyer): Do not normalize the setup and teardown of the resource
+ # creation and deletion. Little is gained when each test
+ # has its own needs and there are collisions when running
+ # tests in parallel.
+
+ @classmethod
+ def setUpClass(cls):
+ # Set up some regex for matching below
+ cls.re_name = re.compile("name\s+\|\s+([^|]+?)\s+\|")
+ cls.re_shared = re.compile("shared\s+\|\s+(\S+)")
+ cls.re_description = re.compile("description\s+\|\s+([^|]+?)\s+\|")
+
+ def test_meter_delete(self):
+ """Test create, delete multiple"""
+ name1 = uuid.uuid4().hex
+ name2 = uuid.uuid4().hex
+
+ raw_output = self.openstack(
+ 'network meter create ' + name1,
+ )
+ self.assertEqual(
+ name1,
+ re.search(self.re_name, raw_output).group(1),
+ )
+ # Check if default shared values
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1)
+ )
+
+ raw_output = self.openstack(
+ 'network meter create ' + name2,
+ )
+ self.assertEqual(
+ name2,
+ re.search(self.re_name, raw_output).group(1),
+ )
+
+ raw_output = self.openstack(
+ 'network meter delete ' + name1 + ' ' + name2,
+ )
+ self.assertOutput('', raw_output)
+
+ def test_meter_list(self):
+ """Test create, list filters, delete"""
+ name1 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network meter create --description Test1 --share ' + name1,
+ )
+ self.addCleanup(self.openstack, 'network meter delete ' + name1)
+
+ self.assertEqual(
+ 'Test1',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'True',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+
+ name2 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network meter create --description Test2 --no-share ' + name2,
+ )
+ self.addCleanup(self.openstack, 'network meter delete ' + name2)
+
+ self.assertEqual(
+ 'Test2',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+
+ raw_output = self.openstack('network meter list')
+ self.assertIsNotNone(re.search(name1 + "\s+\|\s+Test1", raw_output))
+ self.assertIsNotNone(re.search(name2 + "\s+\|\s+Test2", raw_output))
diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py
index c77ff642..ef42dcce 100644
--- a/openstackclient/tests/functional/network/v2/test_network.py
+++ b/openstackclient/tests/functional/network/v2/test_network.py
@@ -10,41 +10,256 @@
# License for the specific language governing permissions and limitations
# under the License.
+import re
import uuid
+import testtools
+
from openstackclient.tests.functional import base
class NetworkTests(base.TestCase):
- """Functional tests for network. """
- NAME = uuid.uuid4().hex
- HEADERS = ['Name']
- FIELDS = ['name']
+ """Functional tests for network"""
@classmethod
def setUpClass(cls):
- opts = cls.get_opts(cls.FIELDS)
- raw_output = cls.openstack('network create ' + cls.NAME + opts)
- expected = cls.NAME + '\n'
- cls.assertOutput(expected, raw_output)
+ # Set up some regex for matching below
+ cls.re_id = re.compile("id\s+\|\s+(\S+)")
+ cls.re_description = re.compile("description\s+\|\s+([^|]+?)\s+\|")
+ cls.re_enabled = re.compile("admin_state_up\s+\|\s+(\S+)")
+ cls.re_shared = re.compile("shared\s+\|\s+(\S+)")
+ cls.re_external = re.compile("router:external\s+\|\s+(\S+)")
+ cls.re_default = re.compile("is_default\s+\|\s+(\S+)")
+ cls.re_port_security = re.compile(
+ "port_security_enabled\s+\|\s+(\S+)"
+ )
- @classmethod
- def tearDownClass(cls):
- raw_output = cls.openstack('network delete ' + cls.NAME)
- cls.assertOutput('', raw_output)
+ def test_network_delete(self):
+ """Test create, delete multiple"""
+ name1 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network create ' +
+ '--description aaaa ' +
+ name1
+ )
+ self.assertEqual(
+ 'aaaa',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ name2 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network create ' +
+ '--description bbbb ' +
+ name2
+ )
+ self.assertEqual(
+ 'bbbb',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ del_output = self.openstack('network delete ' + name1 + ' ' + name2)
+ self.assertOutput('', del_output)
+
+ @testtools.skip('broken SDK testing')
def test_network_list(self):
- opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('network list' + opts)
- self.assertIn(self.NAME, raw_output)
+ """Test create defaults, list filters, delete"""
+ name1 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network create ' +
+ '--description aaaa ' +
+ name1
+ )
+ self.addCleanup(self.openstack, 'network delete ' + name1)
+ self.assertEqual(
+ 'aaaa',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ # Check the default values
+ self.assertEqual(
+ 'UP',
+ re.search(self.re_enabled, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'Internal',
+ re.search(self.re_external, raw_output).group(1),
+ )
+ # NOTE(dtroyer): is_default is not present in the create output
+ # so make sure it stays that way.
+ self.assertIsNone(re.search(self.re_default, raw_output))
+ self.assertEqual(
+ 'True',
+ re.search(self.re_port_security, raw_output).group(1),
+ )
+
+ name2 = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network create ' +
+ '--description bbbb ' +
+ '--disable ' +
+ '--share ' +
+ name2
+ )
+ self.addCleanup(self.openstack, 'network delete ' + name2)
+ self.assertEqual(
+ 'bbbb',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'DOWN',
+ re.search(self.re_enabled, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'True',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+
+ # Test list --long
+ raw_output = self.openstack('network list --long')
+ self.assertIsNotNone(
+ re.search("\|\s+" + name1 + "\s+\|\s+ACTIVE", raw_output)
+ )
+ self.assertIsNotNone(
+ re.search("\|\s+" + name2 + "\s+\|\s+ACTIVE", raw_output)
+ )
+
+ # Test list --long --enable
+ raw_output = self.openstack('network list --long --enable')
+ self.assertIsNotNone(
+ re.search("\|\s+" + name1 + "\s+\|\s+ACTIVE", raw_output)
+ )
+ self.assertIsNone(
+ re.search("\|\s+" + name2 + "\s+\|\s+ACTIVE", raw_output)
+ )
+
+ # Test list --long --disable
+ raw_output = self.openstack('network list --long --disable')
+ self.assertIsNone(
+ re.search("\|\s+" + name1 + "\s+\|\s+ACTIVE", raw_output)
+ )
+ self.assertIsNotNone(
+ re.search("\|\s+" + name2 + "\s+\|\s+ACTIVE", raw_output)
+ )
+
+ # Test list --long --share
+ raw_output = self.openstack('network list --long --share')
+ self.assertIsNone(
+ re.search("\|\s+" + name1 + "\s+\|\s+ACTIVE", raw_output)
+ )
+ self.assertIsNotNone(
+ re.search("\|\s+" + name2 + "\s+\|\s+ACTIVE", raw_output)
+ )
+ # Test list --long --no-share
+ raw_output = self.openstack('network list --long --no-share')
+ self.assertIsNotNone(
+ re.search("\|\s+" + name1 + "\s+\|\s+ACTIVE", raw_output)
+ )
+ self.assertIsNone(
+ re.search("\|\s+" + name2 + "\s+\|\s+ACTIVE", raw_output)
+ )
+
+ @testtools.skip('broken SDK testing')
def test_network_set(self):
- raw_output = self.openstack('network set --disable ' + self.NAME)
- opts = self.get_opts(['name', 'admin_state_up'])
- raw_output = self.openstack('network show ' + self.NAME + opts)
- self.assertEqual("DOWN\n" + self.NAME + "\n", raw_output)
-
- def test_network_show(self):
- opts = self.get_opts(self.FIELDS)
- raw_output = self.openstack('network show ' + self.NAME + opts)
- self.assertEqual(self.NAME + "\n", raw_output)
+ """Tests create options, set, show, delete"""
+ name = uuid.uuid4().hex
+ raw_output = self.openstack(
+ 'network create ' +
+ '--description aaaa ' +
+ '--enable ' +
+ '--no-share ' +
+ '--internal ' +
+ '--no-default ' +
+ '--enable-port-security ' +
+ name
+ )
+ self.addCleanup(self.openstack, 'network delete ' + name)
+ self.assertEqual(
+ 'aaaa',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'UP',
+ re.search(self.re_enabled, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'False',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'Internal',
+ re.search(self.re_external, raw_output).group(1),
+ )
+ # NOTE(dtroyer): is_default is not present in the create output
+ # so make sure it stays that way.
+ self.assertIsNone(re.search(self.re_default, raw_output))
+ self.assertEqual(
+ 'True',
+ re.search(self.re_port_security, raw_output).group(1),
+ )
+
+ raw_output = self.openstack(
+ 'network set ' +
+ '--description cccc ' +
+ '--disable ' +
+ '--share ' +
+ '--external ' +
+ '--disable-port-security ' +
+ name
+ )
+ self.assertOutput('', raw_output)
+
+ raw_output = self.openstack('network show ' + name)
+
+ self.assertEqual(
+ 'cccc',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'DOWN',
+ re.search(self.re_enabled, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'True',
+ re.search(self.re_shared, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'External',
+ re.search(self.re_external, raw_output).group(1),
+ )
+ # why not 'None' like above??
+ self.assertEqual(
+ 'False',
+ re.search(self.re_default, raw_output).group(1),
+ )
+ self.assertEqual(
+ 'False',
+ re.search(self.re_port_security, raw_output).group(1),
+ )
+
+ # NOTE(dtroyer): There is ambiguity around is_default in that
+ # it is not in the API docs and apparently can
+ # not be set when the network is --external,
+ # although the option handling code only looks at
+ # the value of is_default when external is True.
+ raw_output = self.openstack(
+ 'network set ' +
+ '--default ' +
+ name
+ )
+ self.assertOutput('', raw_output)
+
+ raw_output = self.openstack('network show ' + name)
+
+ self.assertEqual(
+ 'cccc',
+ re.search(self.re_description, raw_output).group(1),
+ )
+ # NOTE(dtroyer): This should be 'True'
+ self.assertEqual(
+ 'False',
+ re.search(self.re_default, raw_output).group(1),
+ )
diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py
index dd6112e7..e99dcef6 100644
--- a/openstackclient/tests/functional/network/v2/test_network_agent.py
+++ b/openstackclient/tests/functional/network/v2/test_network_agent.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from openstackclient.tests.functional import base
@@ -26,11 +28,13 @@ class NetworkAgentTests(base.TestCase):
# get the list of network agent IDs.
cls.IDs = raw_output.split('\n')
+ @testtools.skip('broken SDK testing')
def test_network_agent_show(self):
opts = self.get_opts(self.FIELDS)
raw_output = self.openstack('network agent show ' + self.IDs[0] + opts)
self.assertEqual(self.IDs[0] + "\n", raw_output)
+ @testtools.skip('broken SDK testing')
def test_network_agent_set(self):
opts = self.get_opts(['admin_state_up'])
self.openstack('network agent set --disable ' + self.IDs[0])
diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py
new file mode 100644
index 00000000..2bb04a9d
--- /dev/null
+++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2016, Intel Corporation.
+# 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.
+
+import testtools
+
+from openstackclient.tests.functional import base
+
+
+class NetworkQosRuleTypeTests(base.TestCase):
+ """Functional tests for Network QoS rule type. """
+
+ AVAILABLE_RULE_TYPES = ['dscp_marking',
+ 'bandwidth_limit',
+ 'minimum_bandwidth']
+
+ @testtools.skip('broken SDK testing')
+ def test_qos_rule_type_list(self):
+ raw_output = self.openstack('network qos rule type list')
+ for rule_type in self.AVAILABLE_RULE_TYPES:
+ self.assertIn(rule_type, raw_output)
diff --git a/openstackclient/tests/functional/network/v2/test_network_service_provider.py b/openstackclient/tests/functional/network/v2/test_network_service_provider.py
index 379de430..6fbff6c8 100644
--- a/openstackclient/tests/functional/network/v2/test_network_service_provider.py
+++ b/openstackclient/tests/functional/network/v2/test_network_service_provider.py
@@ -19,7 +19,7 @@ from openstackclient.tests.functional import base
class TestNetworkServiceProvider(base.TestCase):
"""Functional tests for network service provider"""
- SERVICE_TYPE = ['L3_ROUTER_NAT']
+ SERVICE_TYPE = 'L3_ROUTER_NAT'
def test_network_service_provider_list(self):
raw_output = self.openstack('network service provider list')
diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py
index decd9553..976fbedb 100644
--- a/openstackclient/tests/functional/network/v2/test_port.py
+++ b/openstackclient/tests/functional/network/v2/test_port.py
@@ -12,6 +12,8 @@
import uuid
+import testtools
+
from openstackclient.tests.functional import base
@@ -23,6 +25,7 @@ class PortTests(base.TestCase):
FIELDS = ['name']
@classmethod
+ @testtools.skip('broken SDK testing')
def setUpClass(cls):
# Create a network for the subnet.
cls.openstack('network create ' + cls.NETWORK_NAME)
diff --git a/openstackclient/tests/functional/network/v2/test_security_group_rule.py b/openstackclient/tests/functional/network/v2/test_security_group_rule.py
index c91de1a5..ec3731eb 100644
--- a/openstackclient/tests/functional/network/v2/test_security_group_rule.py
+++ b/openstackclient/tests/functional/network/v2/test_security_group_rule.py
@@ -12,6 +12,8 @@
import uuid
+import testtools
+
from openstackclient.tests.functional import base
@@ -52,6 +54,7 @@ class SecurityGroupRuleTests(base.TestCase):
cls.SECURITY_GROUP_NAME)
cls.assertOutput('', raw_output)
+ @testtools.skip('broken SDK testing')
def test_security_group_rule_list(self):
opts = self.get_opts(self.ID_HEADER)
raw_output = self.openstack('security group rule list ' +
diff --git a/openstackclient/tests/functional/post_test_hook_tips.sh b/openstackclient/tests/functional/post_test_hook_tips.sh
new file mode 100755
index 00000000..994142d8
--- /dev/null
+++ b/openstackclient/tests/functional/post_test_hook_tips.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+# This is a script that kicks off a series of functional tests against an
+# OpenStack cloud. It will attempt to create an instance if one is not
+# available. Do not run this script unless you know what you're doing.
+# For more information refer to:
+# http://docs.openstack.org/developer/python-openstackclient/
+
+# This particular script differs from the normal post_test_hook because
+# it installs the master (tip) version of osc-lib and openstacksdk
+
+function generate_testr_results {
+ if [ -f .testrepository/0 ]; then
+ sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit
+ sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit
+ sudo .tox/functional/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html
+ sudo gzip -9 $BASE/logs/testrepository.subunit
+ sudo gzip -9 $BASE/logs/testr_results.html
+ sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
+ sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
+ fi
+}
+
+export OPENSTACKCLIENT_DIR="$BASE/new/python-openstackclient"
+sudo chown -R jenkins:stack $OPENSTACKCLIENT_DIR
+
+# Go to the openstackclient dir
+cd $OPENSTACKCLIENT_DIR
+
+# Run tests
+echo "Running openstackclient functional test suite"
+set +e
+
+# Source environment variables to kick things off
+source ~stack/devstack/openrc admin admin
+echo 'Running tests with:'
+env | grep OS
+
+# Preserve env for OS_ credentials
+sudo -E -H -u jenkins tox -e functional-tips
+EXIT_CODE=$?
+set -e
+
+# Collect and parse result
+generate_testr_results
+exit $EXIT_CODE
diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py
index d8bd3a96..b4df5b2d 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py
@@ -42,6 +42,11 @@ class VolumeTypeTests(common.BaseVolumeTests):
raw_output = self.openstack('volume type list' + opts)
self.assertIn(self.NAME, raw_output)
+ def test_volume_type_list_default(self):
+ opts = self.get_opts(self.HEADERS)
+ raw_output = self.openstack('volume type list --default' + opts)
+ self.assertEqual("lvmdriver-1\n", raw_output)
+
def test_volume_type_show(self):
opts = self.get_opts(self.FIELDS)
raw_output = self.openstack('volume type show ' + self.NAME + opts)
diff --git a/openstackclient/tests/unit/api/test_image_v1.py b/openstackclient/tests/unit/api/test_image_v1.py
index e02ef381..6ce3ddea 100644
--- a/openstackclient/tests/unit/api/test_image_v1.py
+++ b/openstackclient/tests/unit/api/test_image_v1.py
@@ -21,7 +21,7 @@ from openstackclient.tests.unit import utils
FAKE_PROJECT = 'xyzpdq'
-FAKE_URL = 'http://gopher.com'
+FAKE_URL = 'http://gopher.dev10.com'
class TestImageAPIv1(utils.TestCase):
diff --git a/openstackclient/tests/unit/api/test_image_v2.py b/openstackclient/tests/unit/api/test_image_v2.py
index 5dbb51e0..22490e46 100644
--- a/openstackclient/tests/unit/api/test_image_v2.py
+++ b/openstackclient/tests/unit/api/test_image_v2.py
@@ -21,7 +21,7 @@ from openstackclient.tests.unit import utils
FAKE_PROJECT = 'xyzpdq'
-FAKE_URL = 'http://gopher.com'
+FAKE_URL = 'http://gopher.dev20.com'
class TestImageAPIv2(utils.TestCase):
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 985ce5e2..4fe735b6 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -168,6 +168,9 @@ class FakeComputev2Client(object):
self.quota_classes = mock.Mock()
self.quota_classes.resource_class = fakes.FakeResource(None, {})
+ self.usage = mock.Mock()
+ self.usage.resource_class = fakes.FakeResource(None, {})
+
self.volumes = mock.Mock()
self.volumes.resource_class = fakes.FakeResource(None, {})
@@ -1248,3 +1251,65 @@ class FakeServerGroup(object):
info=copy.deepcopy(server_group_info),
loaded=True)
return server_group
+
+
+class FakeUsage(object):
+ """Fake one or more usage."""
+
+ @staticmethod
+ def create_one_usage(attrs=None):
+ """Create a fake usage.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with tenant_id and other attributes
+ """
+ if attrs is None:
+ attrs = {}
+
+ # Set default attributes.
+ usage_info = {
+ 'tenant_id': 'usage-tenant-id-' + uuid.uuid4().hex,
+ 'total_memory_mb_usage': 512.0,
+ 'total_vcpus_usage': 1.0,
+ 'total_local_gb_usage': 1.0,
+ 'server_usages': [
+ {
+ 'ended_at': None,
+ 'flavor': 'usage-flavor-' + uuid.uuid4().hex,
+ 'hours': 1.0,
+ 'local_gb': 1,
+ 'memory_mb': 512,
+ 'name': 'usage-name-' + uuid.uuid4().hex,
+ 'state': 'active',
+ 'uptime': 3600,
+ 'vcpus': 1
+ }
+ ]
+ }
+
+ # Overwrite default attributes.
+ usage_info.update(attrs)
+
+ usage = fakes.FakeResource(info=copy.deepcopy(usage_info),
+ loaded=True)
+
+ return usage
+
+ @staticmethod
+ def create_usages(attrs=None, count=2):
+ """Create multiple fake services.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of services to fake
+ :return:
+ A list of FakeResource objects faking the services
+ """
+ usages = []
+ for i in range(0, count):
+ usages.append(FakeUsage.create_one_usage(attrs))
+
+ return usages
diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py
index 93ad9d14..632fcda1 100644
--- a/openstackclient/tests/unit/compute/v2/test_flavor.py
+++ b/openstackclient/tests/unit/compute/v2/test_flavor.py
@@ -160,7 +160,7 @@ class TestFlavorCreate(TestFlavor):
self.flavor.is_public = False
arglist = [
- '--id', self.flavor.id,
+ '--id', 'auto',
'--ram', str(self.flavor.ram),
'--disk', str(self.flavor.disk),
'--ephemeral', str(self.flavor.ephemeral),
@@ -174,7 +174,6 @@ class TestFlavorCreate(TestFlavor):
self.flavor.name,
]
verifylist = [
- ('id', self.flavor.id),
('ram', self.flavor.ram),
('disk', self.flavor.disk),
('ephemeral', self.flavor.ephemeral),
@@ -193,7 +192,7 @@ class TestFlavorCreate(TestFlavor):
self.flavor.ram,
self.flavor.vcpus,
self.flavor.disk,
- self.flavor.id,
+ 'auto',
self.flavor.ephemeral,
self.flavor.swap,
self.flavor.rxtx_factor,
diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py
new file mode 100644
index 00000000..a383e903
--- /dev/null
+++ b/openstackclient/tests/unit/compute/v2/test_usage.py
@@ -0,0 +1,179 @@
+# 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 datetime
+import mock
+
+from openstackclient.compute.v2 import usage
+from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
+
+
+class TestUsage(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestUsage, self).setUp()
+
+ self.usage_mock = self.app.client_manager.compute.usage
+ self.usage_mock.reset_mock()
+
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock.reset_mock()
+
+
+class TestUsageList(TestUsage):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ # Return value of self.usage_mock.list().
+ usages = compute_fakes.FakeUsage.create_usages(
+ attrs={'tenant_id': project.name}, count=1)
+
+ columns = (
+ "Project",
+ "Servers",
+ "RAM MB-Hours",
+ "CPU Hours",
+ "Disk GB-Hours"
+ )
+
+ data = [(
+ usages[0].tenant_id,
+ len(usages[0].server_usages),
+ float("%.2f" % usages[0].total_memory_mb_usage),
+ float("%.2f" % usages[0].total_vcpus_usage),
+ float("%.2f" % usages[0].total_local_gb_usage),
+ )]
+
+ def setUp(self):
+ super(TestUsageList, self).setUp()
+
+ self.usage_mock.list.return_value = self.usages
+
+ self.projects_mock.list.return_value = [self.project]
+ # Get the command object to test
+ self.cmd = usage.ListUsage(self.app, None)
+
+ def test_usage_list_no_options(self):
+
+ arglist = []
+ verifylist = [
+ ('start', None),
+ ('end', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.projects_mock.list.assert_called_with()
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_usage_list_with_options(self):
+ arglist = [
+ '--start', '2016-11-11',
+ '--end', '2016-12-20',
+ ]
+ verifylist = [
+ ('start', '2016-11-11'),
+ ('end', '2016-12-20'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.projects_mock.list.assert_called_with()
+ self.usage_mock.list.assert_called_with(
+ datetime.datetime(2016, 11, 11, 0, 0),
+ datetime.datetime(2016, 12, 20, 0, 0),
+ detailed=True)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+
+class TestUsageShow(TestUsage):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ # Return value of self.usage_mock.list().
+ usage = compute_fakes.FakeUsage.create_one_usage(
+ attrs={'tenant_id': project.name})
+
+ columns = (
+ 'CPU Hours',
+ 'Disk GB-Hours',
+ 'RAM MB-Hours',
+ 'Servers',
+ )
+
+ data = (
+ float("%.2f" % usage.total_vcpus_usage),
+ float("%.2f" % usage.total_local_gb_usage),
+ float("%.2f" % usage.total_memory_mb_usage),
+ len(usage.server_usages),
+ )
+
+ def setUp(self):
+ super(TestUsageShow, self).setUp()
+
+ self.usage_mock.get.return_value = self.usage
+
+ self.projects_mock.get.return_value = self.project
+ # Get the command object to test
+ self.cmd = usage.ShowUsage(self.app, None)
+
+ def test_usage_show_no_options(self):
+
+ self.app.client_manager.auth_ref = mock.Mock()
+ self.app.client_manager.auth_ref.project_id = self.project.id
+
+ arglist = []
+ verifylist = [
+ ('project', None),
+ ('start', None),
+ ('end', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_usage_show_with_options(self):
+
+ arglist = [
+ '--project', self.project.id,
+ '--start', '2016-11-11',
+ '--end', '2016-12-20',
+ ]
+ verifylist = [
+ ('project', self.project.id),
+ ('start', '2016-11-11'),
+ ('end', '2016-12-20'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.usage_mock.get.assert_called_with(
+ self.project.id,
+ datetime.datetime(2016, 11, 11, 0, 0),
+ datetime.datetime(2016, 12, 20, 0, 0))
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py
index f7cb5676..ca6b1d31 100644
--- a/openstackclient/tests/unit/fakes.py
+++ b/openstackclient/tests/unit/fakes.py
@@ -212,6 +212,9 @@ class FakeResource(object):
def keys(self):
return self._info.keys()
+ def to_dict(self):
+ return self._info
+
@property
def info(self):
return self._info
diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py
index c1f00762..4e1077db 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_project.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_project.py
@@ -13,8 +13,11 @@
# under the License.
#
+import mock
+
from keystoneauth1 import exceptions as ks_exc
from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v2_0 import project
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
@@ -302,6 +305,32 @@ class TestProjectDelete(TestProject):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_projects_with_exception(self, find_mock):
+ find_mock.side_effect = [self.fake_project,
+ exceptions.CommandError]
+ arglist = [
+ self.fake_project.id,
+ 'unexist_project',
+ ]
+ verifylist = [
+ ('projects', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 projects failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.projects_mock, self.fake_project.id)
+ find_mock.assert_any_call(self.projects_mock, 'unexist_project')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.projects_mock.delete.assert_called_once_with(self.fake_project.id)
+
class TestProjectList(TestProject):
diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py
index 68ebf141..684ce803 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_role.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_role.py
@@ -17,6 +17,7 @@ import mock
from keystoneauth1 import exceptions as ks_exc
from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v2_0 import role
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
@@ -240,6 +241,32 @@ class TestRoleDelete(TestRole):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_roles_with_exception(self, find_mock):
+ find_mock.side_effect = [self.fake_role,
+ exceptions.CommandError]
+ arglist = [
+ self.fake_role.id,
+ 'unexist_role',
+ ]
+ verifylist = [
+ ('roles', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 roles failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.roles_mock, self.fake_role.id)
+ find_mock.assert_any_call(self.roles_mock, 'unexist_role')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.roles_mock.delete.assert_called_once_with(self.fake_role.id)
+
class TestRoleList(TestRole):
diff --git a/openstackclient/tests/unit/identity/v2_0/test_user.py b/openstackclient/tests/unit/identity/v2_0/test_user.py
index 765f8559..a8b9497e 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_user.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_user.py
@@ -17,6 +17,7 @@ import mock
from keystoneauth1 import exceptions as ks_exc
from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v2_0 import user
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
@@ -411,6 +412,32 @@ class TestUserDelete(TestUser):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_users_with_exception(self, find_mock):
+ find_mock.side_effect = [self.fake_user,
+ exceptions.CommandError]
+ arglist = [
+ self.fake_user.id,
+ 'unexist_user',
+ ]
+ verifylist = [
+ ('users', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 users failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.users_mock, self.fake_user.id)
+ find_mock.assert_any_call(self.users_mock, 'unexist_user')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.users_mock.delete.assert_called_once_with(self.fake_user.id)
+
class TestUserList(TestUser):
diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py
index eb50adb5..8558de95 100644
--- a/openstackclient/tests/unit/identity/v3/test_group.py
+++ b/openstackclient/tests/unit/identity/v3/test_group.py
@@ -16,6 +16,7 @@ from mock import call
from keystoneauth1 import exceptions as ks_exc
from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v3 import group
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -257,6 +258,32 @@ class TestGroupDelete(TestGroup):
self.groups_mock.delete.assert_called_once_with(self.groups[0].id)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_groups_with_exception(self, find_mock):
+ find_mock.side_effect = [self.groups[0],
+ exceptions.CommandError]
+ arglist = [
+ self.groups[0].id,
+ 'unexist_group',
+ ]
+ verifylist = [
+ ('groups', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 groups failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.groups_mock, self.groups[0].id)
+ find_mock.assert_any_call(self.groups_mock, 'unexist_group')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.groups_mock.delete.assert_called_once_with(self.groups[0].id)
+
class TestGroupList(TestGroup):
diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py
index 702d9209..2b898090 100644
--- a/openstackclient/tests/unit/identity/v3/test_project.py
+++ b/openstackclient/tests/unit/identity/v3/test_project.py
@@ -16,6 +16,7 @@
import mock
from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v3 import project
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -445,6 +446,32 @@ class TestProjectDelete(TestProject):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_projects_with_exception(self, find_mock):
+ find_mock.side_effect = [self.project,
+ exceptions.CommandError]
+ arglist = [
+ self.project.id,
+ 'unexist_project',
+ ]
+ verifylist = [
+ ('projects', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 projects failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.projects_mock, self.project.id)
+ find_mock.assert_any_call(self.projects_mock, 'unexist_project')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.projects_mock.delete.assert_called_once_with(self.project.id)
+
class TestProjectList(TestProject):
diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py
index 448e18d3..c0b68bdf 100644
--- a/openstackclient/tests/unit/identity/v3/test_role.py
+++ b/openstackclient/tests/unit/identity/v3/test_role.py
@@ -14,6 +14,10 @@
#
import copy
+import mock
+
+from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v3 import role
from openstackclient.tests.unit import fakes
@@ -428,6 +432,36 @@ class TestRoleDelete(TestRole):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_roles_with_exception(self, find_mock):
+ find_mock.side_effect = [self.roles_mock.get.return_value,
+ exceptions.CommandError]
+ arglist = [
+ identity_fakes.role_name,
+ 'unexist_role',
+ ]
+ verifylist = [
+ ('roles', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 roles failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.roles_mock,
+ identity_fakes.role_name,
+ domain_id=None)
+ find_mock.assert_any_call(self.roles_mock,
+ 'unexist_role',
+ domain_id=None)
+
+ self.assertEqual(2, find_mock.call_count)
+ self.roles_mock.delete.assert_called_once_with(identity_fakes.role_id)
+
class TestRoleList(TestRole):
diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py
index 4eeb8bfe..93e8f63d 100644
--- a/openstackclient/tests/unit/identity/v3/test_trust.py
+++ b/openstackclient/tests/unit/identity/v3/test_trust.py
@@ -12,6 +12,10 @@
#
import copy
+import mock
+
+from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.identity.v3 import trust
from openstackclient.tests.unit import fakes
@@ -148,6 +152,33 @@ class TestTrustDelete(TestTrust):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_trusts_with_exception(self, find_mock):
+ find_mock.side_effect = [self.trusts_mock.get.return_value,
+ exceptions.CommandError]
+ arglist = [
+ identity_fakes.trust_id,
+ 'unexist_trust',
+ ]
+ verifylist = [
+ ('trust', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 trusts failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.trusts_mock, identity_fakes.trust_id)
+ find_mock.assert_any_call(self.trusts_mock, 'unexist_trust')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.trusts_mock.delete.assert_called_once_with(
+ identity_fakes.trust_id)
+
class TestTrustList(TestTrust):
diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py
index 6150a5f3..3c1f49a6 100644
--- a/openstackclient/tests/unit/identity/v3/test_user.py
+++ b/openstackclient/tests/unit/identity/v3/test_user.py
@@ -16,6 +16,9 @@
import contextlib
import mock
+from osc_lib import exceptions
+from osc_lib import utils
+
from openstackclient.identity.v3 import user
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -465,6 +468,32 @@ class TestUserDelete(TestUser):
)
self.assertIsNone(result)
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_users_with_exception(self, find_mock):
+ find_mock.side_effect = [self.user,
+ exceptions.CommandError]
+ arglist = [
+ self.user.id,
+ 'unexist_user',
+ ]
+ verifylist = [
+ ('users', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 users failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.users_mock, self.user.id)
+ find_mock.assert_any_call(self.users_mock, 'unexist_user')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.users_mock.delete.assert_called_once_with(self.user.id)
+
class TestUserList(TestUser):
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index a054e513..a1513119 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -845,6 +845,39 @@ class TestImageSet(TestImage):
self.assertIsNone(result)
+ self.image_members_mock.update.assert_not_called()
+
+ def test_image_set_membership_option(self):
+ membership = image_fakes.FakeImage.create_one_image_member(
+ attrs={'image_id': image_fakes.image_id,
+ 'member_id': self.project.id}
+ )
+ self.image_members_mock.update.return_value = membership
+
+ for status in ('accept', 'reject', 'pending'):
+ arglist = [
+ '--%s' % status,
+ image_fakes.image_id,
+ ]
+ verifylist = [
+ (status, True),
+ ('image', image_fakes.image_id)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ self.image_members_mock.update.assert_called_once_with(
+ image_fakes.image_id,
+ self.app.client_manager.auth_ref.project_id,
+ status if status == 'pending' else status + 'ed'
+ )
+ self.image_members_mock.update.reset_mock()
+
+ # Assert that the 'update image" route is also called, in addition to
+ # the 'update membership' route.
+ self.images_mock.update.assert_called_with(image_fakes.image_id)
+
def test_image_set_options(self):
arglist = [
'--name', 'new-name',
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index 97d07076..b931cb55 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -297,15 +297,17 @@ class FakeNetwork(object):
'admin_state_up': True,
'shared': False,
'subnets': ['a', 'b'],
- 'provider_network_type': 'vlan',
- 'provider_physical_network': 'physnet1',
- 'provider_segmentation_id': "400",
+ 'provider:network_type': 'vlan',
+ 'provider:physical_network': 'physnet1',
+ 'provider:segmentation_id': "400",
'router:external': True,
'availability_zones': [],
'availability_zone_hints': [],
'is_default': False,
'port_security_enabled': True,
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
+ 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex,
+ 'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex,
}
# Overwrite default attributes.
@@ -317,8 +319,21 @@ class FakeNetwork(object):
# Set attributes with special mapping in OpenStack SDK.
network.project_id = network_attrs['tenant_id']
network.is_router_external = network_attrs['router:external']
+ network.is_admin_state_up = network_attrs['admin_state_up']
network.is_port_security_enabled = \
network_attrs['port_security_enabled']
+ network.subnet_ids = network_attrs['subnets']
+ network.is_shared = network_attrs['shared']
+ network.provider_network_type = \
+ network_attrs['provider:network_type']
+ network.provider_physical_network = \
+ network_attrs['provider:physical_network']
+ network.provider_segmentation_id = \
+ network_attrs['provider:segmentation_id']
+ network.ipv4_address_scope_id = \
+ network_attrs['ipv4_address_scope']
+ network.ipv6_address_scope_id = \
+ network_attrs['ipv6_address_scope']
return network
@@ -461,12 +476,15 @@ class FakePort(object):
loaded=True)
# Set attributes with special mappings in OpenStack SDK.
- port.project_id = port_attrs['tenant_id']
port.binding_host_id = port_attrs['binding:host_id']
port.binding_profile = port_attrs['binding:profile']
port.binding_vif_details = port_attrs['binding:vif_details']
port.binding_vif_type = port_attrs['binding:vif_type']
port.binding_vnic_type = port_attrs['binding:vnic_type']
+ port.is_admin_state_up = port_attrs['admin_state_up']
+ port.is_port_security_enabled = port_attrs['port_security_enabled']
+ port.project_id = port_attrs['tenant_id']
+ port.security_group_ids = port_attrs['security_groups']
return port
@@ -795,6 +813,51 @@ class FakeNetworkQosPolicy(object):
return mock.Mock(side_effect=qos_policies)
+class FakeNetworkQosRuleType(object):
+ """Fake one or more Network QoS rule types."""
+
+ @staticmethod
+ def create_one_qos_rule_type(attrs=None):
+ """Create a fake Network QoS rule type.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, id, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ qos_rule_type_attrs = {
+ 'type': 'rule-type-' + uuid.uuid4().hex,
+ }
+
+ # Overwrite default attributes.
+ qos_rule_type_attrs.update(attrs)
+
+ return fakes.FakeResource(
+ info=copy.deepcopy(qos_rule_type_attrs),
+ loaded=True)
+
+ @staticmethod
+ def create_qos_rule_types(attrs=None, count=2):
+ """Create multiple fake Network QoS rule types.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of QoS rule types to fake
+ :return:
+ A list of FakeResource objects faking the QoS rule types
+ """
+ qos_rule_types = []
+ for i in range(0, count):
+ qos_rule_types.append(
+ FakeNetworkQosRuleType.create_one_qos_rule_type(attrs))
+
+ return qos_rule_types
+
+
class FakeRouter(object):
"""Fake one or more routers."""
@@ -834,6 +897,9 @@ class FakeRouter(object):
# Set attributes with special mapping in OpenStack SDK.
router.project_id = router_attrs['tenant_id']
+ router.is_admin_state_up = router_attrs['admin_state_up']
+ router.is_distributed = router_attrs['distributed']
+ router.is_ha = router_attrs['ha']
return router
@@ -1192,6 +1258,51 @@ class FakeFloatingIP(object):
return mock.Mock(side_effect=floating_ips)
+class FakeNetworkMeter(object):
+ """Fake network meter"""
+
+ @staticmethod
+ def create_one_meter(attrs=None):
+ """Create metering pool"""
+ attrs = attrs or {}
+
+ meter_attrs = {
+ 'id': 'meter-id-' + uuid.uuid4().hex,
+ 'name': 'meter-name-' + uuid.uuid4().hex,
+ 'description': 'meter-description-' + uuid.uuid4().hex,
+ 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'shared': False
+ }
+
+ meter_attrs.update(attrs)
+
+ meter = fakes.FakeResource(
+ info=copy.deepcopy(meter_attrs),
+ loaded=True)
+
+ meter.project_id = meter_attrs['tenant_id']
+
+ return meter
+
+ @staticmethod
+ def create_meter(attrs=None, count=2):
+ """Create multiple meters"""
+
+ meters = []
+ for i in range(0, count):
+ meters.append(FakeNetworkMeter.
+ create_one_meter(attrs))
+ return meters
+
+ @staticmethod
+ def get_meter(meter=None, count=2):
+ """Get a list of meters"""
+ if meter is None:
+ meter = (FakeNetworkMeter.
+ create_meter(count))
+ return mock.Mock(side_effect=meter)
+
+
class FakeSubnetPool(object):
"""Fake one or more subnet pools."""
diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py
index 12c3f1d6..516b8795 100644
--- a/openstackclient/tests/unit/network/v2/test_address_scope.py
+++ b/openstackclient/tests/unit/network/v2/test_address_scope.py
@@ -275,6 +275,104 @@ class TestListAddressScope(TestAddressScope):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_address_scope_list_name(self):
+ arglist = [
+ '--name', self.address_scopes[0].name,
+ ]
+ verifylist = [
+ ('name', self.address_scopes[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.address_scopes.assert_called_once_with(
+ **{'name': self.address_scopes[0].name})
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_address_scope_list_ip_version(self):
+ arglist = [
+ '--ip-version', str(4),
+ ]
+ verifylist = [
+ ('ip_version', 4),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.address_scopes.assert_called_once_with(
+ **{'ip_version': 4})
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_address_scope_list_project(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.address_scopes.assert_called_once_with(
+ **{'tenant_id': project.id, 'project_id': project.id})
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_address_scope_project_domain(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ '--project-domain', project.domain_id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id, 'project_id': project.id}
+
+ self.network.address_scopes.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_address_scope_list_share(self):
+ arglist = [
+ '--share',
+ ]
+ verifylist = [
+ ('share', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.address_scopes.assert_called_once_with(
+ **{'shared': True, 'is_shared': True}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_address_scope_list_no_share(self):
+ arglist = [
+ '--no-share',
+ ]
+ verifylist = [
+ ('no_share', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.address_scopes.assert_called_once_with(
+ **{'shared': False, 'is_shared': False}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestSetAddressScope(TestAddressScope):
diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py
index 4bdbddc4..c7c5a9b4 100644
--- a/openstackclient/tests/unit/network/v2/test_ip_availability.py
+++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py
@@ -118,8 +118,10 @@ class TestListIPAvailability(TestIPAvailability):
class TestShowIPAvailability(TestIPAvailability):
+ _network = network_fakes.FakeNetwork.create_one_network()
_ip_availability = \
- network_fakes.FakeIPAvailability.create_one_ip_availability()
+ network_fakes.FakeIPAvailability.create_one_ip_availability(
+ attrs={'network_id': _network.id})
columns = (
'network_id',
@@ -144,6 +146,8 @@ class TestShowIPAvailability(TestIPAvailability):
self.network.find_network_ip_availability = mock.Mock(
return_value=self._ip_availability)
+ self.network.find_network = mock.Mock(
+ return_value=self._network)
# Get the command object to test
self.cmd = ip_availability.ShowIPAvailability(
@@ -166,8 +170,10 @@ class TestShowIPAvailability(TestIPAvailability):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network.find_network_ip_availability.assert_called_once_with(
+ self._ip_availability.network_id,
+ ignore_missing=False)
+ self.network.find_network.assert_called_once_with(
self._ip_availability.network_name,
ignore_missing=False)
-
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/unit/network/v2/test_meter.py b/openstackclient/tests/unit/network/v2/test_meter.py
new file mode 100644
index 00000000..b393f7fa
--- /dev/null
+++ b/openstackclient/tests/unit/network/v2/test_meter.py
@@ -0,0 +1,304 @@
+# Copyright (c) 2016, Intel Corporation.
+# 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.
+
+import mock
+from mock import call
+
+from osc_lib import exceptions
+
+from openstackclient.network.v2 import meter
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
+from openstackclient.tests.unit import utils as tests_utils
+
+
+class TestMeter(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super(TestMeter, self).setUp()
+ self.network = self.app.client_manager.network
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.domains_mock = self.app.client_manager.identity.domains
+
+
+class TestCreateMeter(TestMeter):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ domain = identity_fakes_v3.FakeDomain.create_one_domain()
+
+ new_meter = (
+ network_fakes.FakeNetworkMeter.
+ create_one_meter()
+ )
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'shared',
+ )
+
+ data = (
+ new_meter.description,
+ new_meter.id,
+ new_meter.name,
+ new_meter.project_id,
+ new_meter.shared,
+ )
+
+ def setUp(self):
+ super(TestCreateMeter, self).setUp()
+ self.network.create_metering_label = mock.Mock(
+ return_value=self.new_meter)
+ self.projects_mock.get.return_value = self.project
+ self.cmd = meter.CreateMeter(self.app, self.namespace)
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_default_options(self):
+ arglist = [
+ self.new_meter.name,
+ ]
+
+ verifylist = [
+ ('name', self.new_meter.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_metering_label.assert_called_once_with(
+ **{'name': self.new_meter.name}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ "--description", self.new_meter.description,
+ "--project", self.new_meter.project_id,
+ "--project-domain", self.domain.name,
+ "--share",
+ self.new_meter.name,
+ ]
+
+ verifylist = [
+ ('description', self.new_meter.description),
+ ('name', self.new_meter.name),
+ ('project', self.new_meter.project_id),
+ ('project_domain', self.domain.name),
+ ('share', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_metering_label.assert_called_once_with(
+ **{'description': self.new_meter.description,
+ 'name': self.new_meter.name,
+ 'tenant_id': self.project.id,
+ 'shared': True, }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestDeleteMeter(TestMeter):
+
+ def setUp(self):
+ super(TestDeleteMeter, self).setUp()
+
+ self.meter_list = \
+ network_fakes.FakeNetworkMeter.create_meter(count=2)
+
+ self.network.delete_metering_label = mock.Mock(return_value=None)
+
+ self.network.find_metering_label = network_fakes \
+ .FakeNetworkMeter.get_meter(
+ meter=self.meter_list
+ )
+
+ self.cmd = meter.DeleteMeter(self.app, self.namespace)
+
+ def test_delete_one_meter(self):
+ arglist = [
+ self.meter_list[0].name,
+ ]
+ verifylist = [
+ ('meter', [self.meter_list[0].name]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.delete_metering_label.assert_called_once_with(
+ self.meter_list[0]
+ )
+ self.assertIsNone(result)
+
+ def test_delete_multiple_meters(self):
+ arglist = []
+ for n in self.meter_list:
+ arglist.append(n.id)
+ verifylist = [
+ ('meter', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for n in self.meter_list:
+ calls.append(call(n))
+ self.network.delete_metering_label.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_delete_multiple_meter_exception(self):
+ arglist = [
+ self.meter_list[0].id,
+ 'xxxx-yyyy-zzzz',
+ self.meter_list[1].id,
+ ]
+ verifylist = [
+ ('meter', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ return_find = [
+ self.meter_list[0],
+ exceptions.NotFound('404'),
+ self.meter_list[1],
+ ]
+ self.network.find_meter = mock.Mock(side_effect=return_find)
+
+ ret_delete = [
+ None,
+ exceptions.NotFound('404'),
+ ]
+ self.network.delete_metering_label = mock.Mock(side_effect=ret_delete)
+
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ calls = [
+ call(self.meter_list[0]),
+ call(self.meter_list[1]),
+ ]
+ self.network.delete_metering_label.assert_has_calls(calls)
+
+
+class TestListMeter(TestMeter):
+
+ meter_list = \
+ network_fakes.FakeNetworkMeter.create_meter(count=2)
+
+ columns = (
+ 'ID',
+ 'Name',
+ 'Description',
+ 'Shared',
+ )
+
+ data = []
+
+ for meters in meter_list:
+ data.append((
+ meters.id,
+ meters.name,
+ meters.description,
+ meters.shared,
+ ))
+
+ def setUp(self):
+ super(TestListMeter, self).setUp()
+
+ self.network.metering_labels = mock.Mock(
+ return_value=self.meter_list
+ )
+
+ self.cmd = meter.ListMeter(self.app, self.namespace)
+
+ def test_meter_list(self):
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.metering_labels.assert_called_with()
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+
+class TestShowMeter(TestMeter):
+ new_meter = (
+ network_fakes.FakeNetworkMeter.
+ create_one_meter()
+ )
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'shared',
+ )
+
+ data = (
+ new_meter.description,
+ new_meter.id,
+ new_meter.name,
+ new_meter.project_id,
+ new_meter.shared,
+ )
+
+ def setUp(self):
+ super(TestShowMeter, self).setUp()
+
+ self.cmd = meter.ShowMeter(self.app, self.namespace)
+
+ self.network.find_metering_label = \
+ mock.Mock(return_value=self.new_meter)
+
+ def test_show_no_options(self):
+ arglist = []
+ verifylist = []
+
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_meter_show_option(self):
+ arglist = [
+ self.new_meter.name,
+ ]
+ verifylist = [
+ ('meter', self.new_meter.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.find_metering_label.assert_called_with(
+ self.new_meter.name, ignore_missing=False
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 96b1b102..c5283443 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -62,13 +62,15 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'availability_zones',
'description',
'id',
+ 'ipv4_address_scope',
+ 'ipv6_address_scope',
'is_default',
'name',
'port_security_enabled',
'project_id',
- 'provider_network_type',
- 'provider_physical_network',
- 'provider_segmentation_id',
+ 'provider:network_type',
+ 'provider:physical_network',
+ 'provider:segmentation_id',
'qos_policy_id',
'router:external',
'shared',
@@ -82,6 +84,8 @@ class TestCreateNetworkIdentityV3(TestNetwork):
utils.format_list(_network.availability_zones),
_network.description,
_network.id,
+ _network.ipv4_address_scope_id,
+ _network.ipv6_address_scope_id,
_network.is_default,
_network.name,
_network.is_port_security_enabled,
@@ -236,13 +240,15 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'availability_zones',
'description',
'id',
+ 'ipv4_address_scope',
+ 'ipv6_address_scope',
'is_default',
'name',
'port_security_enabled',
'project_id',
- 'provider_network_type',
- 'provider_physical_network',
- 'provider_segmentation_id',
+ 'provider:network_type',
+ 'provider:physical_network',
+ 'provider:segmentation_id',
'qos_policy_id',
'router:external',
'shared',
@@ -256,6 +262,8 @@ class TestCreateNetworkIdentityV2(TestNetwork):
utils.format_list(_network.availability_zones),
_network.description,
_network.id,
+ _network.ipv4_address_scope_id,
+ _network.ipv6_address_scope_id,
_network.is_default,
_network.name,
_network.is_port_security_enabled,
@@ -512,7 +520,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'router:external': True}
+ **{'router:external': True, 'is_router_external': True}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -529,7 +537,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'router:external': False}
+ **{'router:external': False, 'is_router_external': False}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -585,7 +593,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'admin_state_up': True}
+ **{'admin_state_up': True, 'is_admin_state_up': True}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -603,7 +611,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'admin_state_up': False}
+ **{'admin_state_up': False, 'is_admin_state_up': False}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -621,7 +629,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'tenant_id': project.id}
+ **{'tenant_id': project.id, 'project_id': project.id}
)
self.assertEqual(self.columns, columns)
@@ -640,7 +648,7 @@ class TestListNetwork(TestNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id}
+ filters = {'tenant_id': project.id, 'project_id': project.id}
self.network.networks.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -658,7 +666,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'shared': True}
+ **{'shared': True, 'is_shared': True}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -675,7 +683,7 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'shared': False}
+ **{'shared': False, 'is_shared': False}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -711,7 +719,8 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'provider:network_type': network_type}
+ **{'provider:network_type': network_type,
+ 'provider_network_type': network_type}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -728,7 +737,8 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'provider:physical_network': physical_network}
+ **{'provider:physical_network': physical_network,
+ 'provider_physical_network': physical_network}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -745,7 +755,8 @@ class TestListNetwork(TestNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.networks.assert_called_once_with(
- **{'provider:segmentation_id': segmentation_id}
+ **{'provider:segmentation_id': segmentation_id,
+ 'provider_segmentation_id': segmentation_id}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -878,13 +889,15 @@ class TestShowNetwork(TestNetwork):
'availability_zones',
'description',
'id',
+ 'ipv4_address_scope',
+ 'ipv6_address_scope',
'is_default',
'name',
'port_security_enabled',
'project_id',
- 'provider_network_type',
- 'provider_physical_network',
- 'provider_segmentation_id',
+ 'provider:network_type',
+ 'provider:physical_network',
+ 'provider:segmentation_id',
'qos_policy_id',
'router:external',
'shared',
@@ -898,6 +911,8 @@ class TestShowNetwork(TestNetwork):
utils.format_list(_network.availability_zones),
_network.description,
_network.id,
+ _network.ipv4_address_scope_id,
+ _network.ipv6_address_scope_id,
_network.is_default,
_network.name,
_network.is_port_security_enabled,
diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py
index 9f5b442a..9fd395b4 100644
--- a/openstackclient/tests/unit/network/v2/test_network_agent.py
+++ b/openstackclient/tests/unit/network/v2/test_network_agent.py
@@ -130,6 +130,7 @@ class TestListNetworkAgent(TestNetworkAgent):
)
data = []
for agent in network_agents:
+ agent.agent_type = 'DHCP agent'
data.append((
agent.id,
agent.agent_type,
@@ -159,6 +160,40 @@ class TestListNetworkAgent(TestNetworkAgent):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_network_agents_list_agent_type(self):
+ arglist = [
+ '--agent-type', 'dhcp',
+ ]
+ verifylist = [
+ ('agent_type', 'dhcp'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.agents.assert_called_once_with(**{
+ 'agent_type': self.network_agents[0].agent_type,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_network_agents_list_host(self):
+ arglist = [
+ '--host', self.network_agents[0].host,
+ ]
+ verifylist = [
+ ('host', self.network_agents[0].host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.agents.assert_called_once_with(**{
+ 'host': self.network_agents[0].host,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestSetNetworkAgent(TestNetworkAgent):
diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py
new file mode 100644
index 00000000..b93abe80
--- /dev/null
+++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2016, Intel Corporation.
+# 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.
+
+import mock
+
+from openstackclient.network.v2 import network_qos_rule_type as _qos_rule_type
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
+
+
+class TestNetworkQosRuleType(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super(TestNetworkQosRuleType, self).setUp()
+ # Get a shortcut to the network client
+ self.network = self.app.client_manager.network
+
+
+class TestListNetworkQosRuleType(TestNetworkQosRuleType):
+
+ # The QoS policies to list up.
+ qos_rule_types = (
+ network_fakes.FakeNetworkQosRuleType.create_qos_rule_types(count=3))
+ columns = (
+ 'Type',
+ )
+ data = []
+ for qos_rule_type in qos_rule_types:
+ data.append((
+ qos_rule_type.type,
+ ))
+
+ def setUp(self):
+ super(TestListNetworkQosRuleType, self).setUp()
+ self.network.qos_rule_types = mock.Mock(
+ return_value=self.qos_rule_types)
+
+ # Get the command object to test
+ self.cmd = _qos_rule_type.ListNetworkQosRuleType(self.app,
+ self.namespace)
+
+ def test_qos_rule_type_list(self):
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.qos_rule_types.assert_called_once_with(**{})
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py
index b884dbc0..935ce075 100644
--- a/openstackclient/tests/unit/network/v2/test_network_rbac.py
+++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py
@@ -327,7 +327,12 @@ class TestListNetworkRABC(TestNetworkRBAC):
'Object Type',
'Object ID',
)
-
+ columns_long = (
+ 'ID',
+ 'Object Type',
+ 'Object ID',
+ 'Action',
+ )
data = []
for r in rbac_policies:
data.append((
@@ -335,6 +340,14 @@ class TestListNetworkRABC(TestNetworkRBAC):
r.object_type,
r.object_id,
))
+ data_long = []
+ for r in rbac_policies:
+ data_long.append((
+ r.id,
+ r.object_type,
+ r.object_id,
+ r.action,
+ ))
def setUp(self):
super(TestListNetworkRABC, self).setUp()
@@ -356,6 +369,55 @@ class TestListNetworkRABC(TestNetworkRBAC):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_network_rbac_list_type_opt(self):
+ arglist = [
+ '--type', self.rbac_policies[0].object_type, ]
+ verifylist = [
+ ('type', self.rbac_policies[0].object_type)]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.rbac_policies.assert_called_with(**{
+ 'object_type': self.rbac_policies[0].object_type
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_network_rbac_list_action_opt(self):
+ arglist = [
+ '--action', self.rbac_policies[0].action, ]
+ verifylist = [
+ ('action', self.rbac_policies[0].action)]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.rbac_policies.assert_called_with(**{
+ 'action': self.rbac_policies[0].action
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_network_rbac_list_with_long(self):
+ arglist = [
+ '--long',
+ ]
+
+ verifylist = [
+ ('long', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.rbac_policies.assert_called_with()
+ self.assertEqual(self.columns_long, columns)
+ self.assertEqual(self.data_long, list(data))
+
class TestSetNetworkRBAC(TestNetworkRBAC):
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index aeb9884a..255e8116 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -324,7 +324,7 @@ class TestCreatePort(TestPort):
self.assertEqual(ref_columns, columns)
self.assertEqual(ref_data, data)
- def test_create_with_no_secuirty_groups(self):
+ def test_create_with_no_security_groups(self):
arglist = [
'--network', self._port.network_id,
'--no-security-group',
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index b0409447..b837afd1 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -459,7 +459,7 @@ class TestListRouter(TestRouter):
columns, data = self.cmd.take_action(parsed_args)
self.network.routers.assert_called_once_with(
- **{'admin_state_up': True}
+ **{'admin_state_up': True, 'is_admin_state_up': True}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -476,7 +476,7 @@ class TestListRouter(TestRouter):
columns, data = self.cmd.take_action(parsed_args)
self.network.routers.assert_called_once_with(
- **{'admin_state_up': False}
+ **{'admin_state_up': False, 'is_admin_state_up': False}
)
self.assertEqual(self.columns, columns)
@@ -494,7 +494,7 @@ class TestListRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id}
+ filters = {'tenant_id': project.id, 'project_id': project.id}
self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -514,7 +514,7 @@ class TestListRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id}
+ filters = {'tenant_id': project.id, 'project_id': project.id}
self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -602,17 +602,19 @@ class TestSetRouter(TestRouter):
# The router to set.
_default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'}
+ _network = network_fakes.FakeNetwork.create_one_network()
+ _subnet = network_fakes.FakeSubnet.create_one_subnet()
_router = network_fakes.FakeRouter.create_one_router(
attrs={'routes': [_default_route]}
)
def setUp(self):
super(TestSetRouter, self).setUp()
-
+ self.network.router_add_gateway = mock.Mock()
self.network.update_router = mock.Mock(return_value=None)
-
self.network.find_router = mock.Mock(return_value=self._router)
-
+ self.network.find_network = mock.Mock(return_value=self._network)
+ self.network.find_subnet = mock.Mock(return_value=self._subnet)
# Get the command object to test
self.cmd = router.SetRouter(self.app, self.namespace)
@@ -702,10 +704,10 @@ class TestSetRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
-
+ routes = [{'destination': '10.20.30.0/24',
+ 'nexthop': '10.20.30.1'}]
attrs = {
- 'routes': self._router.routes + [{'destination': '10.20.30.0/24',
- 'nexthop': '10.20.30.1'}],
+ 'routes': routes + self._router.routes
}
self.network.update_router.assert_called_once_with(
self._router, **attrs)
@@ -731,21 +733,31 @@ class TestSetRouter(TestRouter):
self._router, **attrs)
self.assertIsNone(result)
- def test_set_route_no_route(self):
+ def test_set_route_overwrite_route(self):
+ _testrouter = network_fakes.FakeRouter.create_one_router(
+ {'routes': [{"destination": "10.0.0.2",
+ "nexthop": "1.1.1.1"}]})
+ self.network.find_router = mock.Mock(return_value=_testrouter)
arglist = [
- self._router.name,
+ _testrouter.name,
'--route', 'destination=10.20.30.0/24,gateway=10.20.30.1',
'--no-route',
]
verifylist = [
- ('router', self._router.name),
+ ('router', _testrouter.name),
('routes', [{'destination': '10.20.30.0/24',
'gateway': '10.20.30.1'}]),
('no_route', True),
]
-
- self.assertRaises(tests_utils.ParserException, self.check_parser,
- self.cmd, arglist, verifylist)
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'routes': [{'destination': '10.20.30.0/24',
+ 'nexthop': '10.20.30.1'}]
+ }
+ self.network.update_router.assert_called_once_with(
+ _testrouter, **attrs)
+ self.assertIsNone(result)
def test_set_clear_routes(self):
arglist = [
@@ -767,21 +779,31 @@ class TestSetRouter(TestRouter):
self._router, **attrs)
self.assertIsNone(result)
- def test_set_route_clear_routes(self):
+ def test_overwrite_route_clear_routes(self):
+ _testrouter = network_fakes.FakeRouter.create_one_router(
+ {'routes': [{"destination": "10.0.0.2",
+ "nexthop": "1.1.1.1"}]})
+ self.network.find_router = mock.Mock(return_value=_testrouter)
arglist = [
- self._router.name,
+ _testrouter.name,
'--route', 'destination=10.20.30.0/24,gateway=10.20.30.1',
'--clear-routes',
]
verifylist = [
- ('router', self._router.name),
+ ('router', _testrouter.name),
('routes', [{'destination': '10.20.30.0/24',
'gateway': '10.20.30.1'}]),
('clear_routes', True),
]
-
- self.assertRaises(tests_utils.ParserException, self.check_parser,
- self.cmd, arglist, verifylist)
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'routes': [{'destination': '10.20.30.0/24',
+ 'nexthop': '10.20.30.1'}]
+ }
+ self.network.update_router.assert_called_once_with(
+ _testrouter, **attrs)
+ self.assertIsNone(result)
def test_set_nothing(self):
arglist = [
@@ -799,6 +821,110 @@ class TestSetRouter(TestRouter):
self._router, **attrs)
self.assertIsNone(result)
+ def test_wrong_gateway_params(self):
+ arglist = [
+ "--fixed-ip", "subnet='abc'",
+ self._router.id,
+ ]
+ verifylist = [
+ ('fixed_ip', [{'subnet': "'abc'"}]),
+ ('router', self._router.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action, parsed_args)
+
+ def test_set_gateway_network_only(self):
+ arglist = [
+ "--external-gateway", self._network.id,
+ self._router.id,
+ ]
+ verifylist = [
+ ('external_gateway', self._network.id),
+ ('router', self._router.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.update_router.assert_called_with(
+ self._router, **{'external_gateway_info': {
+ 'network_id': self._network.id}})
+ self.assertIsNone(result)
+
+ def test_set_gateway_options_subnet_only(self):
+ arglist = [
+ "--external-gateway", self._network.id,
+ "--fixed-ip", "subnet='abc'",
+ self._router.id,
+ '--enable-snat',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('external_gateway', self._network.id),
+ ('fixed_ip', [{'subnet': "'abc'"}]),
+ ('enable_snat', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.update_router.assert_called_with(
+ self._router, **{'external_gateway_info': {
+ 'network_id': self._network.id,
+ 'external_fixed_ips': [{
+ 'subnet_id': self._subnet.id, }],
+ 'enable_snat': True, }})
+ self.assertIsNone(result)
+
+ def test_set_gateway_option_ipaddress_only(self):
+ arglist = [
+ "--external-gateway", self._network.id,
+ "--fixed-ip", "ip-address=10.0.1.1",
+ self._router.id,
+ '--enable-snat',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('external_gateway', self._network.id),
+ ('fixed_ip', [{'ip-address': "10.0.1.1"}]),
+ ('enable_snat', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.update_router.assert_called_with(
+ self._router, **{'external_gateway_info': {
+ 'network_id': self._network.id,
+ 'external_fixed_ips': [{
+ 'ip_address': "10.0.1.1", }],
+ 'enable_snat': True, }})
+ self.assertIsNone(result)
+
+ def test_set_gateway_options_subnet_ipaddress(self):
+ arglist = [
+ "--external-gateway", self._network.id,
+ "--fixed-ip", "subnet='abc',ip-address=10.0.1.1",
+ self._router.id,
+ '--enable-snat',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('external_gateway', self._network.id),
+ ('fixed_ip', [{'subnet': "'abc'",
+ 'ip-address': "10.0.1.1"}]),
+ ('enable_snat', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.update_router.assert_called_with(
+ self._router, **{'external_gateway_info': {
+ 'network_id': self._network.id,
+ 'external_fixed_ips': [{
+ 'subnet_id': self._subnet.id,
+ 'ip_address': "10.0.1.1", }],
+ 'enable_snat': True, }})
+ self.assertIsNone(result)
+
class TestShowRouter(TestRouter):
@@ -915,3 +1041,16 @@ class TestUnsetRouter(TestRouter):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exceptions.CommandError,
self.cmd.take_action, parsed_args)
+
+ def test_unset_router_external_gateway(self):
+ arglist = [
+ '--external-gateway',
+ self._testrouter.name,
+ ]
+ verifylist = [('external_gateway', True)]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {'external_gateway_info': {}}
+ self.network.update_router.assert_called_once_with(
+ self._testrouter, **attrs)
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py
index 3d91da9b..b9fac684 100644
--- a/openstackclient/tests/unit/test_shell.py
+++ b/openstackclient/tests/unit/test_shell.py
@@ -422,7 +422,7 @@ class TestShellArgV(TestShell):
Use the argv supplied by the test runner so we get actual Python
runtime behaviour; we only need to check the type of argv[0]
- which will alwyas be present.
+ which will always be present.
"""
with mock.patch(
diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py
index 7a44dea8..6c6d9a1d 100644
--- a/openstackclient/tests/unit/volume/v1/test_volume.py
+++ b/openstackclient/tests/unit/volume/v1/test_volume.py
@@ -431,6 +431,142 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_volume_create_with_bootable_and_readonly(self):
+ arglist = [
+ '--bootable',
+ '--read-only',
+ '--size', str(self.new_volume.size),
+ self.new_volume.display_name,
+ ]
+ verifylist = [
+ ('bootable', True),
+ ('non_bootable', False),
+ ('read_only', True),
+ ('read_write', False),
+ ('size', self.new_volume.size),
+ ('name', self.new_volume.display_name),
+ ]
+
+ parsed_args = self.check_parser(
+ self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.create.assert_called_with(
+ self.new_volume.size,
+ None,
+ None,
+ self.new_volume.display_name,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+ self.volumes_mock.set_bootable.assert_called_with(
+ self.new_volume.id, True)
+ self.volumes_mock.update_readonly_flag.assert_called_with(
+ self.new_volume.id, True)
+
+ def test_volume_create_with_nonbootable_and_readwrite(self):
+ arglist = [
+ '--non-bootable',
+ '--read-write',
+ '--size', str(self.new_volume.size),
+ self.new_volume.display_name,
+ ]
+ verifylist = [
+ ('bootable', False),
+ ('non_bootable', True),
+ ('read_only', False),
+ ('read_write', True),
+ ('size', self.new_volume.size),
+ ('name', self.new_volume.display_name),
+ ]
+
+ parsed_args = self.check_parser(
+ self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.create.assert_called_with(
+ self.new_volume.size,
+ None,
+ None,
+ self.new_volume.display_name,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+ self.volumes_mock.set_bootable.assert_called_with(
+ self.new_volume.id, False)
+ self.volumes_mock.update_readonly_flag.assert_called_with(
+ self.new_volume.id, False)
+
+ @mock.patch.object(volume.LOG, 'error')
+ def test_volume_create_with_bootable_and_readonly_fail(
+ self, mock_error):
+
+ self.volumes_mock.set_bootable.side_effect = (
+ exceptions.CommandError())
+
+ self.volumes_mock.update_readonly_flag.side_effect = (
+ exceptions.CommandError())
+
+ arglist = [
+ '--bootable',
+ '--read-only',
+ '--size', str(self.new_volume.size),
+ self.new_volume.display_name,
+ ]
+ verifylist = [
+ ('bootable', True),
+ ('non_bootable', False),
+ ('read_only', True),
+ ('read_write', False),
+ ('size', self.new_volume.size),
+ ('name', self.new_volume.display_name),
+ ]
+
+ parsed_args = self.check_parser(
+ self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.create.assert_called_with(
+ self.new_volume.size,
+ None,
+ None,
+ self.new_volume.display_name,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ )
+
+ self.assertEqual(2, mock_error.call_count)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+ self.volumes_mock.set_bootable.assert_called_with(
+ self.new_volume.id, True)
+ self.volumes_mock.update_readonly_flag.assert_called_with(
+ self.new_volume.id, True)
+
def test_volume_create_without_size(self):
arglist = [
self.new_volume.display_name,
diff --git a/openstackclient/tests/unit/volume/v2/test_backup.py b/openstackclient/tests/unit/volume/v2/test_backup.py
index 10e7aac5..a8e81c7e 100644
--- a/openstackclient/tests/unit/volume/v2/test_backup.py
+++ b/openstackclient/tests/unit/volume/v2/test_backup.py
@@ -418,6 +418,30 @@ class TestBackupSet(TestBackup):
self.backup.id, **{'name': 'new_name'})
self.assertIsNone(result)
+ def test_backup_set_description(self):
+ arglist = [
+ '--description', 'new_description',
+ self.backup.id,
+ ]
+ verifylist = [
+ ('name', None),
+ ('description', 'new_description'),
+ ('backup', self.backup.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'description': 'new_description'
+ }
+ self.backups_mock.update.assert_called_once_with(
+ self.backup.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
def test_backup_set_state(self):
arglist = [
'--state', 'error',
diff --git a/openstackclient/tests/unit/volume/v2/test_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py
index cedf21a9..8ce356ae 100644
--- a/openstackclient/tests/unit/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v2/test_snapshot.py
@@ -67,6 +67,7 @@ class TestSnapshotCreate(TestSnapshot):
self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.create.return_value = self.new_snapshot
+ self.snapshots_mock.manage.return_value = self.new_snapshot
# Get the command object to test
self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
@@ -152,6 +153,33 @@ class TestSnapshotCreate(TestSnapshot):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_snapshot_create_without_remote_source(self):
+ arglist = [
+ '--remote-source', 'source-name=test_source_name',
+ '--remote-source', 'source-id=test_source_id',
+ '--volume', self.new_snapshot.volume_id,
+ ]
+ ref_dict = {'source-name': 'test_source_name',
+ 'source-id': 'test_source_id'}
+ verifylist = [
+ ('remote_source', ref_dict),
+ ('volume', self.new_snapshot.volume_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.manage.assert_called_with(
+ volume_id=self.new_snapshot.volume_id,
+ ref=ref_dict,
+ name=None,
+ description=None,
+ metadata=None,
+ )
+ self.snapshots_mock.create.assert_not_called()
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestSnapshotDelete(TestSnapshot):
diff --git a/openstackclient/tests/unit/volume/v2/test_type.py b/openstackclient/tests/unit/volume/v2/test_type.py
index 325872d7..0d556e13 100644
--- a/openstackclient/tests/unit/volume/v2/test_type.py
+++ b/openstackclient/tests/unit/volume/v2/test_type.py
@@ -172,7 +172,11 @@ class TestTypeList(TestType):
"Description",
"Properties"
]
-
+ data_with_default_type = [(
+ volume_types[0].id,
+ volume_types[0].name,
+ True
+ )]
data = []
for t in volume_types:
data.append((
@@ -194,6 +198,7 @@ class TestTypeList(TestType):
super(TestTypeList, self).setUp()
self.types_mock.list.return_value = self.volume_types
+ self.types_mock.default.return_value = self.volume_types[0]
# get the command to test
self.cmd = volume_type.ListVolumeType(self.app, None)
@@ -203,6 +208,7 @@ class TestTypeList(TestType):
("long", False),
("private", False),
("public", False),
+ ("default", False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -220,6 +226,7 @@ class TestTypeList(TestType):
("long", True),
("private", False),
("public", True),
+ ("default", False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -236,6 +243,7 @@ class TestTypeList(TestType):
("long", False),
("private", True),
("public", False),
+ ("default", False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -244,6 +252,23 @@ class TestTypeList(TestType):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_type_list_with_default_option(self):
+ arglist = [
+ "--default",
+ ]
+ verifylist = [
+ ("long", False),
+ ("private", False),
+ ("public", False),
+ ("default", True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.types_mock.default.assert_called_once_with()
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data_with_default_type, list(data))
+
class TestTypeSet(TestType):
diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py
index 41728342..4fef9dd9 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume.py
@@ -450,6 +450,154 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_volume_create_with_bootable_and_readonly(self):
+ arglist = [
+ '--bootable',
+ '--read-only',
+ '--size', str(self.new_volume.size),
+ self.new_volume.name,
+ ]
+ verifylist = [
+ ('bootable', True),
+ ('non_bootable', False),
+ ('read_only', True),
+ ('read_write', False),
+ ('size', self.new_volume.size),
+ ('name', self.new_volume.name),
+ ]
+
+ parsed_args = self.check_parser(
+ self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.create.assert_called_with(
+ size=self.new_volume.size,
+ snapshot_id=None,
+ name=self.new_volume.name,
+ description=None,
+ volume_type=None,
+ user_id=None,
+ project_id=None,
+ availability_zone=None,
+ metadata=None,
+ imageRef=None,
+ source_volid=None,
+ consistencygroup_id=None,
+ source_replica=None,
+ multiattach=False,
+ scheduler_hints=None,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+ self.volumes_mock.set_bootable.assert_called_with(
+ self.new_volume.id, True)
+ self.volumes_mock.update_readonly_flag.assert_called_with(
+ self.new_volume.id, True)
+
+ def test_volume_create_with_nonbootable_and_readwrite(self):
+ arglist = [
+ '--non-bootable',
+ '--read-write',
+ '--size', str(self.new_volume.size),
+ self.new_volume.name,
+ ]
+ verifylist = [
+ ('bootable', False),
+ ('non_bootable', True),
+ ('read_only', False),
+ ('read_write', True),
+ ('size', self.new_volume.size),
+ ('name', self.new_volume.name),
+ ]
+
+ parsed_args = self.check_parser(
+ self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.create.assert_called_with(
+ size=self.new_volume.size,
+ snapshot_id=None,
+ name=self.new_volume.name,
+ description=None,
+ volume_type=None,
+ user_id=None,
+ project_id=None,
+ availability_zone=None,
+ metadata=None,
+ imageRef=None,
+ source_volid=None,
+ consistencygroup_id=None,
+ source_replica=None,
+ multiattach=False,
+ scheduler_hints=None,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+ self.volumes_mock.set_bootable.assert_called_with(
+ self.new_volume.id, False)
+ self.volumes_mock.update_readonly_flag.assert_called_with(
+ self.new_volume.id, False)
+
+ @mock.patch.object(volume.LOG, 'error')
+ def test_volume_create_with_bootable_and_readonly_fail(
+ self, mock_error):
+
+ self.volumes_mock.set_bootable.side_effect = (
+ exceptions.CommandError())
+
+ self.volumes_mock.update_readonly_flag.side_effect = (
+ exceptions.CommandError())
+
+ arglist = [
+ '--bootable',
+ '--read-only',
+ '--size', str(self.new_volume.size),
+ self.new_volume.name,
+ ]
+ verifylist = [
+ ('bootable', True),
+ ('non_bootable', False),
+ ('read_only', True),
+ ('read_write', False),
+ ('size', self.new_volume.size),
+ ('name', self.new_volume.name),
+ ]
+
+ parsed_args = self.check_parser(
+ self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.create.assert_called_with(
+ size=self.new_volume.size,
+ snapshot_id=None,
+ name=self.new_volume.name,
+ description=None,
+ volume_type=None,
+ user_id=None,
+ project_id=None,
+ availability_zone=None,
+ metadata=None,
+ imageRef=None,
+ source_volid=None,
+ consistencygroup_id=None,
+ source_replica=None,
+ multiattach=False,
+ scheduler_hints=None,
+ )
+
+ self.assertEqual(2, mock_error.call_count)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+ self.volumes_mock.set_bootable.assert_called_with(
+ self.new_volume.id, True)
+ self.volumes_mock.update_readonly_flag.assert_called_with(
+ self.new_volume.id, True)
+
def test_volume_create_with_source_replicated(self):
self.volumes_mock.get.return_value = self.new_volume
arglist = [
@@ -675,6 +823,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': False,
+ 'project_id': None,
+ 'user_id': None,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -705,6 +866,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': True,
+ 'project_id': self.project.id,
+ 'user_id': None,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -737,6 +911,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': True,
+ 'project_id': self.project.id,
+ 'user_id': None,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -767,6 +954,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': False,
+ 'project_id': None,
+ 'user_id': self.user.id,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
device = self.mock_volume.attachments[0]['device']
@@ -798,6 +998,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': False,
+ 'project_id': None,
+ 'user_id': self.user.id,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -828,6 +1041,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': False,
+ 'project_id': None,
+ 'user_id': None,
+ 'display_name': self.mock_volume.name,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -858,6 +1084,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': False,
+ 'project_id': None,
+ 'user_id': None,
+ 'display_name': None,
+ 'status': self.mock_volume.status,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -888,6 +1127,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': True,
+ 'project_id': None,
+ 'user_id': None,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
self.assertEqual(self.columns, columns)
server = self.mock_volume.attachments[0]['server_id']
@@ -919,6 +1171,19 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
+ search_opts = {
+ 'all_tenants': False,
+ 'project_id': None,
+ 'user_id': None,
+ 'display_name': None,
+ 'status': None,
+ }
+ self.volumes_mock.list.assert_called_once_with(
+ search_opts=search_opts,
+ marker=None,
+ limit=None,
+ )
+
collist = [
'ID',
'Display Name',
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index 0087bad4..739484df 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -114,6 +114,28 @@ class CreateVolume(command.ShowOne):
help=_('Set a property on this volume '
'(repeat option to set multiple properties)'),
)
+ bootable_group = parser.add_mutually_exclusive_group()
+ bootable_group.add_argument(
+ "--bootable",
+ action="store_true",
+ help=_("Mark volume as bootable")
+ )
+ bootable_group.add_argument(
+ "--non-bootable",
+ action="store_true",
+ help=_("Mark volume as non-bootable (default)")
+ )
+ readonly_group = parser.add_mutually_exclusive_group()
+ readonly_group.add_argument(
+ "--read-only",
+ action="store_true",
+ help=_("Set volume to read-only access mode")
+ )
+ readonly_group.add_argument(
+ "--read-write",
+ action="store_true",
+ help=_("Set volume to read-write access mode (default)")
+ )
return parser
@@ -166,6 +188,22 @@ class CreateVolume(command.ShowOne):
parsed_args.property,
image,
)
+
+ if parsed_args.bootable or parsed_args.non_bootable:
+ try:
+ volume_client.volumes.set_bootable(
+ volume.id, parsed_args.bootable)
+ except Exception as e:
+ LOG.error(_("Failed to set volume bootable property: %s"), e)
+ if parsed_args.read_only or parsed_args.read_write:
+ try:
+ volume_client.volumes.update_readonly_flag(
+ volume.id,
+ parsed_args.read_only)
+ except Exception as e:
+ LOG.error(_("Failed to set volume read-only access "
+ "mode flag: %s"), e)
+
# Map 'metadata' column to 'properties'
volume._info.update(
{
diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py
index 77c93ad4..45bd30c0 100644
--- a/openstackclient/volume/v1/volume_snapshot.py
+++ b/openstackclient/volume/v1/volume_snapshot.py
@@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__)
class CreateVolumeSnapshot(command.ShowOne):
- """Create new volume snapshot"""
+ _description = _("Create new volume snapshot")
def get_parser(self, prog_name):
parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
@@ -84,7 +84,7 @@ class CreateVolumeSnapshot(command.ShowOne):
class DeleteVolumeSnapshot(command.Command):
- """Delete volume snapshot(s)"""
+ _description = _("Delete volume snapshot(s)")
def get_parser(self, prog_name):
parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
@@ -119,7 +119,7 @@ class DeleteVolumeSnapshot(command.Command):
class ListVolumeSnapshot(command.Lister):
- """List volume snapshots"""
+ _description = _("List volume snapshots")
def get_parser(self, prog_name):
parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
@@ -220,7 +220,7 @@ class ListVolumeSnapshot(command.Lister):
class SetVolumeSnapshot(command.Command):
- """Set volume snapshot properties"""
+ _description = _("Set volume snapshot properties")
def get_parser(self, prog_name):
parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
@@ -281,7 +281,7 @@ class SetVolumeSnapshot(command.Command):
class ShowVolumeSnapshot(command.ShowOne):
- """Display volume snapshot details"""
+ _description = _("Display volume snapshot details")
def get_parser(self, prog_name):
parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
@@ -305,7 +305,7 @@ class ShowVolumeSnapshot(command.ShowOne):
class UnsetVolumeSnapshot(command.Command):
- """Unset volume snapshot properties"""
+ _description = _("Unset volume snapshot properties")
def get_parser(self, prog_name):
parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index 80abfb55..78db261b 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -132,6 +132,28 @@ class CreateVolume(command.ShowOne):
help=_("Allow volume to be attached more than once "
"(default to False)")
)
+ bootable_group = parser.add_mutually_exclusive_group()
+ bootable_group.add_argument(
+ "--bootable",
+ action="store_true",
+ help=_("Mark volume as bootable")
+ )
+ bootable_group.add_argument(
+ "--non-bootable",
+ action="store_true",
+ help=_("Mark volume as non-bootable (default)")
+ )
+ readonly_group = parser.add_mutually_exclusive_group()
+ readonly_group.add_argument(
+ "--read-only",
+ action="store_true",
+ help=_("Set volume to read-only access mode")
+ )
+ readonly_group.add_argument(
+ "--read-write",
+ action="store_true",
+ help=_("Set volume to read-write access mode (default)")
+ )
return parser
def take_action(self, parsed_args):
@@ -199,6 +221,22 @@ class CreateVolume(command.ShowOne):
multiattach=parsed_args.multi_attach,
scheduler_hints=parsed_args.hint,
)
+
+ if parsed_args.bootable or parsed_args.non_bootable:
+ try:
+ volume_client.volumes.set_bootable(
+ volume.id, parsed_args.bootable)
+ except Exception as e:
+ LOG.error(_("Failed to set volume bootable property: %s"), e)
+ if parsed_args.read_only or parsed_args.read_write:
+ try:
+ volume_client.volumes.update_readonly_flag(
+ volume.id,
+ parsed_args.read_only)
+ except Exception as e:
+ LOG.error(_("Failed to set volume read-only access "
+ "mode flag: %s"), e)
+
# Remove key links from being displayed
volume._info.update(
{
@@ -379,16 +417,19 @@ class ListVolume(command.Lister):
project_id = identity_common.find_project(
identity_client,
parsed_args.project,
- parsed_args.project_domain)
+ parsed_args.project_domain).id
user_id = None
if parsed_args.user:
user_id = identity_common.find_user(identity_client,
parsed_args.user,
- parsed_args.user_domain)
+ parsed_args.user_domain).id
+
+ # set value of 'all_tenants' when using project option
+ all_projects = bool(parsed_args.project) or parsed_args.all_projects
search_opts = {
- 'all_tenants': parsed_args.all_projects,
+ 'all_tenants': all_projects,
'project_id': project_id,
'user_id': user_id,
'display_name': parsed_args.name,
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
index d95a49a4..af29b777 100644
--- a/openstackclient/volume/v2/volume_snapshot.py
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__)
class CreateVolumeSnapshot(command.ShowOne):
- """Create new volume snapshot"""
+ _description = _("Create new volume snapshot")
def get_parser(self, prog_name):
parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
@@ -65,6 +65,15 @@ class CreateVolumeSnapshot(command.ShowOne):
help=_("Set a property to this snapshot "
"(repeat option to set multiple properties)"),
)
+ parser.add_argument(
+ "--remote-source",
+ metavar="<key=value>",
+ action=parseractions.KeyValueAction,
+ help=_("The attribute(s) of the exsiting remote volume snapshot "
+ "(admin required) (repeat option to specify multiple "
+ "attributes) e.g.: '--remote-source source-name=test_name "
+ "--remote-source source-id=test_id'"),
+ )
return parser
def take_action(self, parsed_args):
@@ -74,13 +83,29 @@ class CreateVolumeSnapshot(command.ShowOne):
volume = parsed_args.snapshot_name
volume_id = utils.find_resource(
volume_client.volumes, volume).id
- snapshot = volume_client.volume_snapshots.create(
- volume_id,
- force=parsed_args.force,
- name=parsed_args.snapshot_name,
- description=parsed_args.description,
- metadata=parsed_args.property,
- )
+ if parsed_args.remote_source:
+ # Create a new snapshot from an existing remote snapshot source
+ if parsed_args.force:
+ msg = (_("'--force' option will not work when you create "
+ "new volume snapshot from an existing remote "
+ "volume snapshot"))
+ LOG.warning(msg)
+ snapshot = volume_client.volume_snapshots.manage(
+ volume_id=volume_id,
+ ref=parsed_args.remote_source,
+ name=parsed_args.snapshot_name,
+ description=parsed_args.description,
+ metadata=parsed_args.property,
+ )
+ else:
+ # create a new snapshot from scratch
+ snapshot = volume_client.volume_snapshots.create(
+ volume_id,
+ force=parsed_args.force,
+ name=parsed_args.snapshot_name,
+ description=parsed_args.description,
+ metadata=parsed_args.property,
+ )
snapshot._info.update(
{'properties': utils.format_dict(snapshot._info.pop('metadata'))}
)
@@ -88,7 +113,7 @@ class CreateVolumeSnapshot(command.ShowOne):
class DeleteVolumeSnapshot(command.Command):
- """Delete volume snapshot(s)"""
+ _description = _("Delete volume snapshot(s)")
def get_parser(self, prog_name):
parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
@@ -130,7 +155,7 @@ class DeleteVolumeSnapshot(command.Command):
class ListVolumeSnapshot(command.Lister):
- """List volume snapshots"""
+ _description = _("List volume snapshots")
def get_parser(self, prog_name):
parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
@@ -241,7 +266,7 @@ class ListVolumeSnapshot(command.Lister):
class SetVolumeSnapshot(command.Command):
- """Set volume snapshot properties"""
+ _description = _("Set volume snapshot properties")
def get_parser(self, prog_name):
parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
@@ -322,7 +347,7 @@ class SetVolumeSnapshot(command.Command):
class ShowVolumeSnapshot(command.ShowOne):
- """Display volume snapshot details"""
+ _description = _("Display volume snapshot details")
def get_parser(self, prog_name):
parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
@@ -344,7 +369,7 @@ class ShowVolumeSnapshot(command.ShowOne):
class UnsetVolumeSnapshot(command.Command):
- """Unset volume snapshot properties"""
+ _description = _("Unset volume snapshot properties")
def get_parser(self, prog_name):
parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 42ebb53e..46466783 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -160,9 +160,16 @@ class ListVolumeType(command.Lister):
'--long',
action='store_true',
default=False,
- help=_('List additional fields in output'))
+ help=_('List additional fields in output')
+ )
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
+ "--default",
+ action='store_true',
+ default=False,
+ help=_('List the default volume type')
+ )
+ public_group.add_argument(
"--public",
action="store_true",
help=_("List only public types")
@@ -175,6 +182,7 @@ class ListVolumeType(command.Lister):
return parser
def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
if parsed_args.long:
columns = ['ID', 'Name', 'Is Public', 'Description', 'Extra Specs']
column_headers = [
@@ -182,14 +190,16 @@ class ListVolumeType(command.Lister):
else:
columns = ['ID', 'Name', 'Is Public']
column_headers = columns
-
- is_public = None
- if parsed_args.public:
- is_public = True
- if parsed_args.private:
- is_public = False
- data = self.app.client_manager.volume.volume_types.list(
- is_public=is_public)
+ if parsed_args.default:
+ data = [volume_client.volume_types.default()]
+ else:
+ is_public = None
+ if parsed_args.public:
+ is_public = True
+ if parsed_args.private:
+ is_public = False
+ data = volume_client.volume_types.list(
+ is_public=is_public)
return (column_headers,
(utils.get_item_properties(
s, columns,
@@ -240,7 +250,6 @@ class SetVolumeType(command.Command):
volume_type = utils.find_resource(
volume_client.volume_types, parsed_args.volume_type)
-
result = 0
kwargs = {}
if parsed_args.name: