🚨 Latest Research:Tanstack npm Packages Compromised in Ongoing Mini Shai-Hulud Supply-Chain Attack.Learn More
Socket
Book a DemoSign in
Socket

python-openstackclient

Package Overview
Dependencies
Maintainers
1
Versions
114
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

python-openstackclient - pypi Package Compare versions

Comparing version
8.0.0
to
8.1.0
+10
doc/source/cli/command-objects/console-connection.rst
==================
console connection
==================
Server console connection information
Compute v2
.. autoprogram-cliff:: openstack.compute.v2
:command: console connection show
# 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.
#
"""Compute v2 Console auth token implementations."""
from osc_lib.command import command
from osc_lib import utils
from openstackclient.i18n import _
def _get_console_connection_columns(item):
column_map: dict[str, str] = {}
hidden_columns = ['id', 'location', 'name']
return utils.get_osc_show_columns_for_sdk_resource(
item, column_map, hidden_columns
)
class ShowConsoleConnectionInformation(command.ShowOne):
_description = _("Show server's remote console connection information")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'token',
metavar='<token>',
help=_("Nova console token to lookup"),
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
data = compute_client.validate_console_auth_token(parsed_args.token)
display_columns, columns = _get_console_connection_columns(data)
data = utils.get_dict_properties(data, columns)
return (display_columns, data)
# 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 uuid
from openstack.compute.v2 import console_auth_token as _console_auth_token
from openstack.test import fakes as sdk_fakes
from openstackclient.compute.v2 import console_connection
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
class TestConsoleTokens(compute_fakes.TestComputev2):
def setUp(self):
super().setUp()
self._console_auth_token = sdk_fakes.generate_fake_resource(
_console_auth_token.ConsoleAuthToken,
host='127.0.0.1',
instance_uuid=uuid.uuid4().hex,
internal_access_path=None,
port=5900,
tls_port=5901,
)
self.compute_client.validate_console_auth_token.return_value = (
self._console_auth_token
)
self.columns = (
'host',
'instance_uuid',
'internal_access_path',
'port',
'tls_port',
)
self.data = (
self._console_auth_token.host,
self._console_auth_token.instance_uuid,
self._console_auth_token.internal_access_path,
self._console_auth_token.port,
self._console_auth_token.tls_port,
)
self.cmd = console_connection.ShowConsoleConnectionInformation(
self.app, None
)
def test_console_connection_show(self):
arglist = [
'token',
]
verifylist = [
('token', 'token'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute_client.validate_console_auth_token.assert_called_once_with(
'token'
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
---
features:
- |
Add filters to search for enabled and disabled users and projects.
---
features:
- |
Add support for the new ``spice-direct`` console type, as well as the
exposing the ability for admins to lookup console connection information
via the new ``console connection show`` command.
---
upgrade:
- |
The ``openstack server set`` command has been extended with a new
parameter ``--auto-approve`` and the existing ``--state`` parameter
has been modified to require confirmation before resetting the state.
---
upgrade:
- |
Support for Python 3.9 has been dropped.
---
features:
- |
The ``--network``, ``--port``, and ``--router`` options of the ``floating
ip list`` command can now be specified multiple times.
---
upgrade:
- |
The following commands have been migrated to SDK:
- ``domain create``
- ``domain delete``
- ``domain list``
- ``domain set``
- ``domain show``
---
upgrade:
- |
Migrate ``group`` commands from keystoneclient to SDK.
+1
-1

@@ -18,3 +18,3 @@ ---

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.2
rev: v0.11.8
hooks:

@@ -21,0 +21,0 @@ - id: ruff

@@ -26,4 +26,4 @@ ---

- job:
name: osc-tox-py39-tips
parent: openstack-tox-py39
name: osc-tox-py310-tips
parent: openstack-tox-py310
description: |

@@ -47,4 +47,4 @@ Run unit tests for OpenStackClient with master branch of important libs.

- job:
name: osc-tox-py312-tips
parent: openstack-tox-py312
name: osc-tox-py313-tips
parent: openstack-tox-py313
description: |

@@ -142,18 +142,2 @@ Run unit tests for OpenStackClient with master branch of important libs.

- secret:
name: osc-dockerhub
data:
username: osclientzuul
password: !encrypted/pkcs1-oaep
- LbIZjJiVstRVXMpoLQ3+/JcNB6lKVUWJXXo5+Outf+PKAaO7mNnv8XLiFMKnJ6ftopLyu
hWbX9rA+NddvplLQkf1xxkh7QBBU8PToLr58quI2SENUclt4tpjxbZfZu451kFSNJvNvR
E58cHHpfJZpyRnS2htXmN/Qy24gbV2w7CQxSZD2YhlcrerD8uQ8rWEnlY1wcJEaEGomtS
ZTGxsdK2TsZC2cd4b7TG7+xbl2i+hjADzwSQAgUzlLlwuG71667+IWk4SOZ7OycJTv9NN
ZTak8+CGfiMKdmsxZ1Z8uD7DC+RIklDjMWyly6zuhWzfhOmsmU0CesR50moodRUvbK79p
NZM8u0hBex5cl2EpUEwJL/FSPJXUhDMPoMoTZT/SAuXf25R9eZ9JGrKsIAlmVhpl8ifoE
8TpPyvIHGS3YelTQjhqOX0wGb9T4ZauQCcI5Ajzy9NuCTyD9xxme9OX1zz7gMACRnVHvz
q7U7Ue90MnmGH6E2SgKjIZhyzy9Efwb7JUvH1Zb3hlrjCjEhwi9MV5FnABTEeXyYwE10s
3o/KZg2zvdWkVG6x0dEkjpoQaNuaB7T2Na7Sm421n/z3LCzhiQGuTUjENnL6cMEtuA6Pp
BfI5+Qlg7HMwkBXNB73EPfWHzbCR3VNrzGYTy9FvhGud0/cXsuBXgps4WH63ic=
- job:

@@ -168,35 +152,7 @@ name: osc-build-image

provides: osc-container-image
vars: &osc_image_vars
vars:
docker_images:
- context: .
repository: osclient/python-openstackclient
tags: []
- job:
name: osc-upload-image
parent: opendev-upload-docker-image
description: Build Docker images and upload to Docker Hub.
allowed-projects: openstack/python-openstackclient
requires:
- python-builder-3.11-bookworm-container-image
- python-base-3.11-bookworm-container-image
provides: osc-container-image
secrets:
- name: docker_credentials
secret: osc-dockerhub
pass-to-parent: true
vars: *osc_image_vars
- job:
name: osc-promote-image
parent: opendev-promote-docker-image
allowed-projects: openstack/python-openstackclient
description: Promote previously uploaded Docker images.
secrets:
- name: docker_credentials
secret: osc-dockerhub
pass-to-parent: true
nodeset:
nodes: []
vars: *osc_image_vars
- project-template:

@@ -206,8 +162,8 @@ name: osc-tox-unit-tips

jobs:
- osc-tox-py39-tips
- osc-tox-py312-tips
- osc-tox-py310-tips
- osc-tox-py313-tips
gate:
jobs:
- osc-tox-py39-tips
- osc-tox-py312-tips
- osc-tox-py310-tips
- osc-tox-py313-tips

@@ -234,6 +190,2 @@ - project:

jobs:
- osc-upload-image
- osc-functional-devstack
promote:
jobs:
- osc-promote-image

@@ -0,1 +1,2 @@

0weng <oweng@osuosl.org>
Aaron Rosen <aaronorosen@gmail.com>

@@ -70,2 +71,3 @@ Abhishek Chanda <abhishek@cloudscaling.com>

Cedric Brandily <zzelle@gmail.com>
Chaemin-Lim <antraxmin@naver.com>
Chaozhe.Chen <chaozhe.chen@easystack.cn>

@@ -123,2 +125,3 @@ Chen <dstbtgagt@foxmail.com>

Douglas Mendizábal <dmendiza@redhat.com>
Douglas Viroel <viroel@gmail.com>
Dr. Jens Harbott <harbott@osism.tech>

@@ -199,2 +202,3 @@ Einst Crazy <yu.changcai@99cloud.net>

Jan Hartkopf <jhartkopf@inovex.de>
Jan Ueberacker <jan.ueberacker@inovex.de>
Jas <singhj@us.ibm.com>

@@ -285,2 +289,3 @@ Jaspreet Singh Rawel <jaspreetsinghrawel@gmail.com>

Michael McCune <msm@redhat.com>
Michael Still <mikal@stillhq.com>
Miguel Lavalle <miguel.lavalle@verizonmedia.com>

@@ -472,2 +477,3 @@ Miguel Lavalle <mlavalle@redhat.com>

devMuscle <hongsbien@naver.com>
djp <dimsss0607@gmail.com>
dongwenshuai <dong.wenshuai@zte.com.cn>

@@ -552,2 +558,3 @@ elajkat <lajos.katona@est.tech>

venkatamahesh <venkatamaheshkotha@gmail.com>
waf <ekrmdhsl7@gmail.com>
wanghong <w.wanghong@huawei.com>

@@ -562,2 +569,3 @@ wangxiyuan <wangxiyuan@huawei.com>

wuyuting <wytdahu@gmail.com>
xfrnk2 <xfrnk2@gmail.com>
xiexs <xiexs@cn.fujitsu.com>

@@ -564,0 +572,0 @@ yaeeee <rim0399@naver.com>

@@ -16,3 +16,3 @@ # Copyright (c) 2020 Red Hat, Inc.

FROM docker.io/opendevorg/python-builder:3.11-bookworm as builder
FROM docker.io/opendevorg/python-builder:3.12-bookworm AS builder

@@ -22,4 +22,11 @@ COPY . /tmp/src

FROM docker.io/opendevorg/python-base:3.11-bookworm
FROM docker.io/opendevorg/python-base:3.12-bookworm
LABEL org.opencontainers.image.title="python-openstackclient"
LABEL org.opencontainers.image.description="Client for OpenStack services."
LABEL org.opencontainers.image.licenses="Apache License 2.0"
LABEL org.opencontainers.image.url="https://www.openstack.org/"
LABEL org.opencontainers.image.documentation="https://docs.openstack.org/python-openstackclient/latest/"
LABEL org.opencontainers.image.source="https://opendev.org/openstack/python-openstackclient"
COPY --from=builder /output/ /output

@@ -26,0 +33,0 @@ RUN /output/install-from-bindep

@@ -49,1 +49,6 @@ # Copyright 2012-2013 OpenStack Foundation

return parser
def check_api_version(check_version):
# SDK supports auto-negotiation for us: always return True
return True

@@ -110,2 +110,9 @@ # Copyright 2012-2013 OpenStack Foundation

type_group.add_argument(
'--spice-direct',
dest='url_type',
action='store_const',
const='spice-direct',
help=_("Show SPICE direct protocol native console URL"),
)
type_group.add_argument(
'--rdp',

@@ -112,0 +119,0 @@ dest='url_type',

@@ -303,2 +303,3 @@ # Copyright 2013 OpenStack Foundation

identity_client = self.app.client_manager.identity
identity_sdk_client = self.app.client_manager.sdk_connection.identity

@@ -349,7 +350,13 @@ kwargs = {}

).id
users = identity_client.users.list(tenant_id=project)
assignments = identity_sdk_client.role_assignments(
scope_project_id=project
)
user_ids = set()
for assignment in assignments:
if assignment.user:
user_ids.add(assignment.user['id'])
data = []
for user in users:
kwargs['user_id'] = user.id
for user_id in user_ids:
kwargs['user_id'] = user_id
data.extend(compute_client.keypairs(**kwargs))

@@ -356,0 +363,0 @@ elif parsed_args.user:

@@ -85,3 +85,3 @@ # Copyright 2017 Huawei, Inc. All rights reserved.

def _get_server_event_columns(item, client):
hidden_columns = ['name', 'server_id', 'links', 'location']
hidden_columns = ['name', 'server_id', 'links', 'location', 'finish_time']

@@ -88,0 +88,0 @@ if not sdk_utils.supports_microversion(client, '2.58'):

@@ -192,2 +192,12 @@ # Copyright 2012-2013 OpenStack Foundation

def find_domain_id_sdk(
identity_client, name_or_id, *, validate_actor_existence=True
):
return _find_sdk_id(
identity_client.find_domain,
name_or_id=name_or_id,
validate_actor_existence=validate_actor_existence,
)
def find_group(identity_client, name_or_id, domain_name_or_id=None):

@@ -208,2 +218,29 @@ if domain_name_or_id is None:

def find_group_id_sdk(
identity_client,
name_or_id,
domain_name_or_id=None,
*,
validate_actor_existence=True,
):
if domain_name_or_id is None:
return _find_sdk_id(
identity_client.find_group,
name_or_id=name_or_id,
validate_actor_existence=validate_actor_existence,
)
domain_id = find_domain_id_sdk(
identity_client,
name_or_id=domain_name_or_id,
validate_actor_existence=validate_actor_existence,
)
return _find_sdk_id(
identity_client.find_group,
name_or_id=name_or_id,
validate_actor_existence=validate_actor_existence,
domain_id=domain_id,
)
def find_project(identity_client, name_or_id, domain_name_or_id=None):

@@ -234,2 +271,28 @@ if domain_name_or_id is None:

def find_user_id_sdk(
identity_client,
name_or_id,
domain_name_or_id=None,
*,
validate_actor_existence=True,
):
if domain_name_or_id is None:
return _find_sdk_id(
identity_client.find_user,
name_or_id=name_or_id,
validate_actor_existence=validate_actor_existence,
)
domain_id = find_domain_id_sdk(
identity_client,
name_or_id=domain_name_or_id,
validate_actor_existence=validate_actor_existence,
)
return _find_sdk_id(
identity_client.find_user,
name_or_id=name_or_id,
validate_actor_existence=validate_actor_existence,
domain_id=domain_id,
)
def _find_identity_resource(

@@ -275,2 +338,18 @@ identity_client_manager, name_or_id, resource_type, **kwargs

def _find_sdk_id(
find_command, name_or_id, *, validate_actor_existence=True, **kwargs
):
try:
resource = find_command(
name_or_id=name_or_id, ignore_missing=False, **kwargs
)
except sdk_exceptions.ForbiddenException:
return name_or_id
except sdk_exceptions.ResourceNotFound as exc:
if not validate_actor_existence:
return name_or_id
raise exceptions.CommandError from exc
return resource.id
def get_immutable_options(parsed_args):

@@ -277,0 +356,0 @@ options = {}

@@ -272,5 +272,5 @@ # Copyright 2018 SUSE Linux GmbH

if parsed_args.user:
user_id = common.find_user(
user_id = common.find_user_id_sdk(
identity_client, parsed_args.user, parsed_args.user_domain
).id
)
else:

@@ -277,0 +277,0 @@ conn = self.app.client_manager.sdk_connection

@@ -20,3 +20,3 @@ # Copyright 2012-2013 OpenStack Foundation

from keystoneauth1 import exceptions as ks_exc
from openstack import exceptions as sdk_exceptions
from osc_lib.command import command

@@ -33,2 +33,27 @@ from osc_lib import exceptions

def _format_domain(domain):
columns = (
'id',
'name',
'is_enabled',
'description',
'options',
)
column_headers = (
'id',
'name',
'enabled',
'description',
'options',
)
return (
column_headers,
utils.get_item_properties(
domain,
columns,
),
)
class CreateDomain(command.ShowOne):

@@ -52,3 +77,5 @@ _description = _("Create new domain")

'--enable',
dest='is_enabled',
action='store_true',
default=True,
help=_('Enable domain (default)'),

@@ -58,3 +85,4 @@ )

'--disable',
action='store_true',
dest='is_enabled',
action='store_false',
help=_('Disable domain'),

@@ -71,22 +99,16 @@ )

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
enabled = True
if parsed_args.disable:
enabled = False
options = common.get_immutable_options(parsed_args)
try:
domain = identity_client.domains.create(
domain = identity_client.create_domain(
name=parsed_args.name,
description=parsed_args.description,
options=options,
enabled=enabled,
is_enabled=parsed_args.is_enabled,
)
except ks_exc.Conflict:
except sdk_exceptions.ConflictException:
if parsed_args.or_show:
domain = utils.find_resource(
identity_client.domains, parsed_args.name
)
domain = identity_client.find_domain(parsed_args.name)
LOG.info(_('Returning existing domain %s'), domain.name)

@@ -96,4 +118,3 @@ else:

domain._info.pop('links')
return zip(*sorted(domain._info.items()))
return _format_domain(domain)

@@ -115,8 +136,8 @@

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
result = 0
for i in parsed_args.domain:
try:
domain = utils.find_resource(identity_client.domains, i)
identity_client.domains.delete(domain.id)
domain = identity_client.find_domain(i, ignore_missing=False)
identity_client.delete_domain(domain.id)
except Exception as e:

@@ -153,3 +174,3 @@ result += 1

'--enabled',
dest='enabled',
dest='is_enabled',
action='store_true',

@@ -164,9 +185,13 @@ help=_('The domains that are enabled will be returned'),

kwargs['name'] = parsed_args.name
if parsed_args.enabled:
kwargs['enabled'] = True
if parsed_args.is_enabled:
kwargs['is_enabled'] = True
columns = ('ID', 'Name', 'Enabled', 'Description')
data = self.app.client_manager.identity.domains.list(**kwargs)
columns = ('id', 'name', 'is_enabled', 'description')
column_headers = ('ID', 'Name', 'Enabled', 'Description')
data = self.app.client_manager.sdk_connection.identity.domains(
**kwargs
)
return (
columns,
column_headers,
(

@@ -206,3 +231,5 @@ utils.get_item_properties(

'--enable',
dest='is_enabled',
action='store_true',
default=None,
help=_('Enable domain'),

@@ -212,3 +239,5 @@ )

'--disable',
action='store_true',
dest='is_enabled',
action='store_false',
default=None,
help=_('Disable domain'),

@@ -220,6 +249,4 @@ )

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
domain = utils.find_resource(
identity_client.domains, parsed_args.domain
)
identity_client = self.app.client_manager.sdk_connection.identity
domain = identity_client.find_domain(parsed_args.domain)
kwargs = {}

@@ -231,6 +258,4 @@ if parsed_args.name:

if parsed_args.enable:
kwargs['enabled'] = True
if parsed_args.disable:
kwargs['enabled'] = False
if parsed_args.is_enabled is not None:
kwargs['is_enabled'] = parsed_args.is_enabled

@@ -241,3 +266,3 @@ options = common.get_immutable_options(parsed_args)

identity_client.domains.update(domain.id, **kwargs)
identity_client.update_domain(domain.id, **kwargs)

@@ -258,11 +283,5 @@

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
domain = identity_client.find_domain(parsed_args.domain)
domain_str = common._get_token_resource(
identity_client, 'domain', parsed_args.domain
)
domain = utils.find_resource(identity_client.domains, domain_str)
domain._info.pop('links')
return zip(*sorted(domain._info.items()))
return _format_domain(domain)

@@ -20,3 +20,3 @@ # Copyright 2012-2013 OpenStack Foundation

from keystoneauth1 import exceptions as ks_exc
from openstack import exceptions as sdk_exc
from osc_lib.command import command

@@ -33,2 +33,21 @@ from osc_lib import exceptions

def _format_group(group):
columns = (
'description',
'domain_id',
'id',
'name',
)
column_headers = (
'description',
'domain_id',
'id',
'name',
)
return (
column_headers,
utils.get_item_properties(group, columns),
)
class AddUserToGroup(command.Command):

@@ -58,7 +77,7 @@ _description = _("Add user to group")

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
group_id = common.find_group(
group_id = common.find_group_id_sdk(
identity_client, parsed_args.group, parsed_args.group_domain
).id
)

@@ -68,6 +87,6 @@ result = 0

try:
user_id = common.find_user(
user_id = common.find_user_id_sdk(
identity_client, i, parsed_args.user_domain
).id
identity_client.users.add_to_group(user_id, group_id)
)
identity_client.add_user_to_group(user_id, group_id)
except Exception as e:

@@ -116,23 +135,26 @@ result += 1

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
user_id = common.find_user(
identity_client, parsed_args.user, parsed_args.user_domain
).id
group_id = common.find_group(
identity_client, parsed_args.group, parsed_args.group_domain
).id
user_id = common.find_user_id_sdk(
identity_client,
parsed_args.user,
parsed_args.user_domain,
validate_actor_existence=False,
)
group_id = common.find_group_id_sdk(
identity_client,
parsed_args.group,
parsed_args.group_domain,
validate_actor_existence=False,
)
user_in_group = False
try:
identity_client.users.check_in_group(user_id, group_id)
except ks_exc.http.HTTPClientError as e:
if e.http_status == 403 or e.http_status == 404:
msg = _("%(user)s not in group %(group)s\n") % {
'user': parsed_args.user,
'group': parsed_args.group,
}
self.app.stderr.write(msg)
else:
raise e
else:
user_in_group = identity_client.check_user_in_group(
user_id, group_id
)
except sdk_exc.ForbiddenException:
# Assume False if forbidden
pass
if user_in_group:
msg = _("%(user)s in group %(group)s\n") % {

@@ -143,2 +165,8 @@ 'user': parsed_args.user,

self.app.stdout.write(msg)
else:
msg = _("%(user)s not in group %(group)s\n") % {
'user': parsed_args.user,
'group': parsed_args.group,
}
self.app.stderr.write(msg)

@@ -174,19 +202,24 @@

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
domain = None
kwargs = {}
if parsed_args.name:
kwargs['name'] = parsed_args.name
if parsed_args.description:
kwargs['description'] = parsed_args.description
if parsed_args.domain:
domain = common.find_domain(identity_client, parsed_args.domain).id
kwargs['domain_id'] = common.find_domain_id_sdk(
identity_client, parsed_args.domain
)
try:
group = identity_client.groups.create(
name=parsed_args.name,
domain=domain,
description=parsed_args.description,
)
except ks_exc.Conflict:
group = identity_client.create_group(**kwargs)
except sdk_exc.ConflictException:
if parsed_args.or_show:
group = utils.find_resource(
identity_client.groups, parsed_args.name, domain_id=domain
)
if parsed_args.domain:
group = identity_client.find_group(
parsed_args.name, domain_id=parsed_args.domain
)
else:
group = identity_client.find_group(parsed_args.name)
LOG.info(_('Returning existing group %s'), group.name)

@@ -196,4 +229,3 @@ else:

group._info.pop('links')
return zip(*sorted(group._info.items()))
return _format_group(group)

@@ -220,3 +252,3 @@

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity

@@ -226,6 +258,6 @@ errors = 0

try:
group_obj = common.find_group(
group_id = common.find_group_id_sdk(
identity_client, group, parsed_args.domain
)
identity_client.groups.delete(group_obj.id)
identity_client.delete_group(group_id)
except Exception as e:

@@ -275,16 +307,28 @@ errors += 1

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
domain = None
if parsed_args.domain:
domain = common.find_domain(identity_client, parsed_args.domain).id
domain = common.find_domain_id_sdk(
identity_client, parsed_args.domain
)
data = []
if parsed_args.user:
user = common.find_user(
user = common.find_user_id_sdk(
identity_client,
parsed_args.user,
parsed_args.user_domain,
).id
)
if domain:
# NOTE(0weng): The API doesn't actually support filtering additionally by domain_id,
# so this doesn't really do anything.
data = identity_client.user_groups(user, domain_id=domain)
else:
data = identity_client.user_groups(user)
else:
user = None
if domain:
data = identity_client.groups(domain_id=domain)
else:
data = identity_client.groups()

@@ -295,6 +339,2 @@ # List groups

columns += ('Domain ID', 'Description')
data = identity_client.groups.list(
domain=domain,
user=user,
)

@@ -338,7 +378,7 @@ return (

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
group_id = common.find_group(
group_id = common.find_group_id_sdk(
identity_client, parsed_args.group, parsed_args.group_domain
).id
)

@@ -348,6 +388,6 @@ result = 0

try:
user_id = common.find_user(
user_id = common.find_user_id_sdk(
identity_client, i, parsed_args.user_domain
).id
identity_client.users.remove_from_group(user_id, group_id)
)
identity_client.remove_user_from_group(user_id, group_id)
except Exception as e:

@@ -404,4 +444,4 @@ result += 1

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
group = common.find_group(
identity_client = self.app.client_manager.sdk_connection.identity
group = common.find_group_id_sdk(
identity_client, parsed_args.group, parsed_args.domain

@@ -415,3 +455,3 @@ )

identity_client.groups.update(group.id, **kwargs)
identity_client.update_group(group, **kwargs)

@@ -437,11 +477,16 @@

def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
identity_client = self.app.client_manager.sdk_connection.identity
group = common.find_group(
identity_client,
parsed_args.group,
domain_name_or_id=parsed_args.domain,
)
if parsed_args.domain:
domain = common.find_domain_id_sdk(
identity_client, parsed_args.domain
)
group = identity_client.find_group(
parsed_args.group, domain_id=domain, ignore_missing=False
)
else:
group = identity_client.find_group(
parsed_args.group, ignore_missing=False
)
group._info.pop('links')
return zip(*sorted(group._info.items()))
return _format_group(group)

@@ -243,2 +243,16 @@ # Copyright 2012-2013 OpenStack Foundation

)
parser.add_argument(
'--enabled',
action='store_true',
dest='is_enabled',
default=None,
help=_('List only enabled projects'),
)
parser.add_argument(
'--disabled',
action='store_false',
dest='is_enabled',
default=None,
help=_('List only disabled projects'),
)
tag.add_tag_filtering_option_to_parser(parser, _('projects'))

@@ -281,2 +295,5 @@ return parser

if parsed_args.is_enabled is not None:
kwargs['is_enabled'] = parsed_args.is_enabled
tag.get_tag_filtering_args(parsed_args, kwargs)

@@ -283,0 +300,0 @@

@@ -44,2 +44,3 @@ # Copyright 2012-2013 OpenStack Foundation

'password_expires_at',
'options',
)

@@ -55,2 +56,3 @@ column_headers = (

'password_expires_at',
'options',
)

@@ -294,3 +296,3 @@ return (

if not parsed_args.password:
if not password:
LOG.warning(

@@ -418,2 +420,20 @@ _(

)
parser.add_argument(
'--enabled',
action='store_true',
dest='is_enabled',
default=None,
help=_(
'List only enabled users, does nothing with --project and --group'
),
)
parser.add_argument(
'--disabled',
action='store_false',
dest='is_enabled',
default=None,
help=_(
'List only disabled users, does nothing with --project and --group'
),
)
return parser

@@ -438,2 +458,5 @@

if parsed_args.is_enabled is not None:
enabled = parsed_args.is_enabled
if parsed_args.project:

@@ -477,5 +500,11 @@ if domain is not None:

else:
data = identity_client.users(
domain_id=domain,
)
if parsed_args.is_enabled is not None:
data = identity_client.users(
domain_id=domain,
is_enabled=enabled,
)
else:
data = identity_client.users(
domain_id=domain,
)

@@ -668,2 +697,4 @@ # Column handling

identity_client = self.app.client_manager.sdk_connection.identity
conn = self.app.client_manager.sdk_connection
user_id = conn.config.get_auth().get_user_id(conn.identity)

@@ -711,3 +742,5 @@ # FIXME(gyee): there are two scenarios:

identity_client.update_user(
current_password=current_password, password=password
user=user_id,
current_password=current_password,
password=password,
)

@@ -714,0 +747,0 @@

@@ -49,1 +49,6 @@ # Copyright 2012-2013 OpenStack Foundation

return parser
def check_api_version(check_version):
# SDK supports auto-negotiation for us: always return True
return True

@@ -20,2 +20,3 @@ # Copyright 2012-2013 OpenStack Foundation

from base64 import b64encode
import copy
import logging

@@ -26,5 +27,5 @@ import os

from cinderclient import api_versions
from openstack import exceptions as sdk_exceptions
from openstack.image import image_signer
from openstack import utils as sdk_utils
from osc_lib.api import utils as api_utils

@@ -581,3 +582,3 @@ from osc_lib.cli import format_columns

def _take_action_volume(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume

@@ -613,5 +614,4 @@ unsupported_opts = {

source_volume = utils.find_resource(
volume_client.volumes,
parsed_args.volume,
source_volume = volume_client.find_volume(
parsed_args.volume, ignore_missing=False
)

@@ -622,3 +622,3 @@ kwargs: dict[str, ty.Any] = {

}
if volume_client.api_version < api_versions.APIVersion('3.1'):
if not sdk_utils.supports_microversion(volume_client, '3.1'):
if parsed_args.visibility or parsed_args.is_protected is not None:

@@ -635,11 +635,11 @@ msg = _(

response, body = volume_client.volumes.upload_to_image(
response = volume_client.upload_volume_to_image(
source_volume.id,
parsed_args.force,
parsed_args.name,
parsed_args.container_format,
parsed_args.disk_format,
force=parsed_args.force,
disk_format=parsed_args.disk_format,
container_format=parsed_args.container_format,
**kwargs,
)
info = body['os-volume_upload_image']
info = copy.deepcopy(response)
try:

@@ -646,0 +646,0 @@ info['volume_type'] = info['volume_type']['name']

@@ -30,8 +30,2 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

"""Returns a network proxy"""
# NOTE(dtroyer): As of osc-lib 1.8.0 and OpenStackSDK 0.10.0 the
# old Profile interface and separate client creation
# for each API that uses the SDK is unnecessary. This
# callback remains as a remnant of the original plugin
# interface and to avoid the code churn of changing all
# of the existing references.
LOG.debug(

@@ -38,0 +32,0 @@ 'Network client initialized using OpenStack SDK: %s',

@@ -246,6 +246,9 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

metavar='<network>',
dest='networks',
action='append',
help=self.enhance_help_neutron(
_(
"List floating IP(s) according to "
"given network (name or ID)"
"List floating IP(s) according to given network "
"(name or ID) "
"(repeat option to fiter on multiple networks)"
)

@@ -257,4 +260,9 @@ ),

metavar='<port>',
dest='ports',
action='append',
help=self.enhance_help_neutron(
_("List floating IP(s) according to given port (name or ID)")
_(
"List floating IP(s) according to given port (name or ID) "
"(repeat option to fiter on multiple ports)"
)
),

@@ -277,10 +285,2 @@ )

parser.add_argument(
'--long',
action='store_true',
default=False,
help=self.enhance_help_neutron(
_("List additional fields in output")
),
)
parser.add_argument(
'--status',

@@ -301,4 +301,4 @@ metavar='<status>',

_(
"List floating IP(s) according to given project (name or "
"ID)"
"List floating IP(s) according to given project "
"(name or ID) "
)

@@ -311,4 +311,10 @@ ),

metavar='<router>',
dest='routers',
action='append',
help=self.enhance_help_neutron(
_("List floating IP(s) according to given router (name or ID)")
_(
"List floating IP(s) according to given router "
"(name or ID) "
"(repeat option to fiter on multiple routers)"
)
),

@@ -319,2 +325,10 @@ )

)
parser.add_argument(
'--long',
action='store_true',
default=False,
help=self.enhance_help_neutron(
_("List additional fields in output")
),
)

@@ -363,18 +377,29 @@ return parser

if parsed_args.network is not None:
network = network_client.find_network(
parsed_args.network, ignore_missing=False
)
query['floating_network_id'] = network.id
if parsed_args.port is not None:
port = network_client.find_port(
parsed_args.port, ignore_missing=False
)
query['port_id'] = port.id
if parsed_args.networks is not None:
network_ids = []
for network in parsed_args.networks:
network_id = network_client.find_network(
network, ignore_missing=False
).id
network_ids.append(network_id)
query['floating_network_id'] = network_ids
if parsed_args.ports is not None:
port_ids = []
for port in parsed_args.ports:
port_id = network_client.find_port(
port, ignore_missing=False
).id
port_ids.append(port_id)
query['port_id'] = port_ids
if parsed_args.fixed_ip_address is not None:
query['fixed_ip_address'] = parsed_args.fixed_ip_address
if parsed_args.floating_ip_address is not None:
query['floating_ip_address'] = parsed_args.floating_ip_address
if parsed_args.status:
query['status'] = parsed_args.status
if parsed_args.project is not None:

@@ -387,8 +412,12 @@ project = identity_common.find_project(

query['project_id'] = project.id
if parsed_args.router is not None:
router = network_client.find_router(
parsed_args.router, ignore_missing=False
)
query['router_id'] = router.id
if parsed_args.routers is not None:
router_ids = []
for router in parsed_args.routers:
router_id = network_client.find_router(
router, ignore_missing=False
).id
router_ids.append(router_id)
query['router_id'] = router_ids
_tag.get_tag_filtering_args(parsed_args, query)

@@ -395,0 +424,0 @@

@@ -76,3 +76,3 @@ # Copyright (c) 2016, Intel Corporation.

def _get_columns(item):
hidden_columns = ['location', 'tenant_id']
hidden_columns = ['location', 'name', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(

@@ -152,10 +152,2 @@ item, {}, hidden_columns

def _get_item_properties(item, fields):
"""Return a tuple containing the item properties."""
row = []
for field in fields:
row.append(item.get(field, ''))
return tuple(row)
def _rule_action_call(client, action, rule_type):

@@ -362,6 +354,6 @@ rule_type = rule_type.replace('-', '_')

)
data = qos.rules
return (
column_headers,
(_get_item_properties(s, columns) for s in data),
(utils.get_dict_properties(s, columns) for s in qos.rules),
)

@@ -368,0 +360,0 @@

@@ -79,3 +79,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

column_map['interfaces_info'] = 'interfaces_info'
invisible_columns = ['location']
invisible_columns = ['location', 'tenant_id']
if item.is_ha is None:

@@ -82,0 +82,0 @@ invisible_columns.append('is_ha')

@@ -33,3 +33,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

def _get_columns(item):
hidden_columns = ['location', 'tenant_id']
hidden_columns = ['location', 'name', 'tenant_id', 'tags']
return utils.get_osc_show_columns_for_sdk_resource(

@@ -36,0 +36,0 @@ item, {}, hidden_columns

@@ -92,5 +92,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

'security_group_rules': 'rules',
'tenant_id': 'project_id',
}
hidden_columns = ['location']
hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(

@@ -190,3 +189,4 @@ item, column_map, hidden_columns

)
display_columns, property_columns = _get_columns(obj)
display_columns = ('description', 'id', 'name', 'project_id', 'rules')
property_columns = ('description', 'id', 'name', 'tenant_id', 'rules')
data = utils.get_dict_properties(

@@ -425,3 +425,4 @@ obj, property_columns, formatters=_formatters_compute

obj = compute_v2.find_security_group(client, parsed_args.group)
display_columns, property_columns = _get_columns(obj)
display_columns = ('description', 'id', 'name', 'project_id', 'rules')
property_columns = ('description', 'id', 'name', 'tenant_id', 'rules')
data = utils.get_dict_properties(

@@ -428,0 +429,0 @@ obj, property_columns, formatters=_formatters_compute

@@ -97,3 +97,3 @@ # Copyright 2012-2013 OpenStack Foundation

if mod_versions is not None and not isinstance(
mod_versions, (dict, tuple)
mod_versions, dict | tuple
):

@@ -100,0 +100,0 @@ raise TypeError(

@@ -100,3 +100,7 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

if parse_output:
return json.loads(output)
try:
return json.loads(output)
except json.JSONDecodeError:
print(f'failed to decode: {output}')
raise
else:

@@ -103,0 +107,0 @@ return output

@@ -24,8 +24,14 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

def keypair_create(self, name=data_utils.rand_uuid()):
def keypair_create(self, name=data_utils.rand_uuid(), user=None):
"""Create keypair and add cleanup."""
raw_output = self.openstack('keypair create ' + name)
self.addCleanup(self.keypair_delete, name, True)
cmd = 'keypair create ' + name
if user is not None:
cmd += ' --user ' + user
raw_output = self.openstack(cmd)
self.addCleanup(
self.keypair_delete, name, ignore_exceptions=True, user=user
)
if not raw_output:
self.fail('Keypair has not been created!')
return name

@@ -38,6 +44,9 @@ def keypair_list(self, params=''):

def keypair_delete(self, name, ignore_exceptions=False):
def keypair_delete(self, name, ignore_exceptions=False, user=None):
"""Try to delete keypair by name."""
try:
self.openstack('keypair delete ' + name)
cmd = 'keypair delete ' + name
if user is not None:
cmd += ' --user ' + user
self.openstack(cmd)
except exceptions.CommandFailed:

@@ -205,1 +214,28 @@ if not ignore_exceptions:

self.assertInOutput(self.KPName, raw_output)
def test_keypair_list_by_project(self):
"""Test keypair list by project.
Test steps:
1) Create keypair for admin project in setUp
2) Create a new project
3) Create a new user
4) Associate the new user with the new project
5) Create keypair for the new user
6) List keypairs by the new project
7) Check that only the keypair from step 5 is returned
"""
project_name = data_utils.rand_name('TestProject')
self.openstack(f'project create {project_name}')
self.addCleanup(self.openstack, f'project delete {project_name}')
user_name = data_utils.rand_name('TestUser')
self.openstack(f'user create {user_name}')
self.addCleanup(self.openstack, f'user delete {user_name}')
self.openstack(
f'role add --user {user_name} --project {project_name} member'
)
keypair_name = self.keypair_create(user=user_name)
raw_output = self.openstack(f'keypair list --project {project_name}')
items = self.parse_listing(raw_output)
self.assertEqual(1, len(items))
self.assertEqual(keypair_name, items[0]['Name'])

@@ -16,3 +16,2 @@ # Copyright 2013 Nebula Inc.

import copy
import random

@@ -36,4 +35,3 @@ import re

from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit.image.v2 import fakes as image_fakes

@@ -45,56 +43,2 @@ from openstackclient.tests.unit.network.v2 import fakes as network_fakes

class FakeComputev2Client:
def __init__(self, **kwargs):
self.agents = mock.Mock()
self.agents.resource_class = fakes.FakeResource(None, {})
self.images = mock.Mock()
self.images.resource_class = fakes.FakeResource(None, {})
self.servers = mock.Mock()
self.servers.resource_class = fakes.FakeResource(None, {})
self.services = mock.Mock()
self.services.resource_class = fakes.FakeResource(None, {})
self.extensions = mock.Mock()
self.extensions.resource_class = fakes.FakeResource(None, {})
self.flavors = mock.Mock()
self.flavor_access = mock.Mock()
self.flavor_access.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, {})
self.hypervisors = mock.Mock()
self.hypervisors.resource_class = fakes.FakeResource(None, {})
self.hypervisors_stats = mock.Mock()
self.hypervisors_stats.resource_class = fakes.FakeResource(None, {})
self.keypairs = mock.Mock()
self.keypairs.resource_class = fakes.FakeResource(None, {})
self.server_groups = mock.Mock()
self.server_groups.resource_class = fakes.FakeResource(None, {})
self.server_migrations = mock.Mock()
self.server_migrations.resource_class = fakes.FakeResource(None, {})
self.instance_action = mock.Mock()
self.instance_action.resource_class = fakes.FakeResource(None, {})
self.migrations = mock.Mock()
self.migrations.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
class FakeClientMixin:

@@ -127,6 +71,6 @@ def setUp(self):

class TestComputev2(
identity_fakes.FakeClientMixin,
network_fakes.FakeClientMixin,
image_fakes.FakeClientMixin,
volume_fakes.FakeClientMixin,
identity_fakes.FakeClientMixin,
FakeClientMixin,

@@ -140,6 +84,4 @@ utils.TestCommand,

:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with agent_id, os, and so on
:param dict attrs: A dictionary with all attributes
:return: A dicionarty faking the agent
"""

@@ -150,3 +92,3 @@

# set default attributes.
agent_info = {
agent_attrs = {
'agent_id': 'agent-id-' + uuid.uuid4().hex,

@@ -161,7 +103,8 @@ 'os': 'agent-os-' + uuid.uuid4().hex,

assert not set(attrs) - set(agent_attrs), 'unknown keys'
# Overwrite default attributes.
agent_info.update(attrs)
agent_attrs.update(attrs)
agent = fakes.FakeResource(info=copy.deepcopy(agent_info), loaded=True)
return agent
return agent_attrs

@@ -172,8 +115,5 @@

:param dict attrs:
A dictionary with all attributes
:param int count:
The number of agents to fake
:return:
A list of FakeResource objects faking the agents
:param dict attrs: A dictionary with all attributes
:param int count: The number of agents to fake
:return: A list of dictionaries faking the agents
"""

@@ -191,3 +131,3 @@ agents = []

:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.extension.Extension object
:return: A fake :class:`~openstack.compute.v2.extension.Extension` object
"""

@@ -224,6 +164,4 @@ attrs = attrs or {}

:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with id, name, etc.
:param dict attrs: A dictionary with all attributes
:return: A dictionary faking the security group
"""

@@ -241,2 +179,4 @@ attrs = attrs or {}

assert not set(attrs) - set(security_group_attrs), 'unknown keys'
# Overwrite default attributes.

@@ -250,8 +190,5 @@ security_group_attrs.update(attrs)

:param dict attrs:
A dictionary with all attributes
:param int count:
The number of security groups to fake
:return:
A list of FakeResource objects faking the security groups
:param dict attrs: A dictionary with all attributes
:param int count: The number of security groups to fake
:return: A list of dictionaries faking the security groups
"""

@@ -265,28 +202,7 @@ security_groups = []

def get_security_groups(security_groups=None, count=2):
"""Get an iterable MagicMock with a list of faked security groups.
If security groups list is provided, then initialize the Mock object
with the list. Otherwise create one.
:param List security_groups:
A list of FakeResource objects faking security groups
:param int count:
The number of security groups to fake
:return:
An iterable Mock object with side_effect set to a list of faked
security groups
"""
if security_groups is None:
security_groups = create_security_groups(count)
return mock.Mock(side_effect=security_groups)
def create_one_security_group_rule(attrs=None):
"""Create a fake security group rule.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with id, etc.
:param dict attrs: A dictionary with all attributes
:return: A dictionary faking the security group rule
"""

@@ -306,2 +222,4 @@ attrs = attrs or {}

assert not set(attrs) - set(security_group_rule_attrs), 'unknown keys'
# Overwrite default attributes.

@@ -316,8 +234,5 @@ security_group_rule_attrs.update(attrs)

:param dict attrs:
A dictionary with all attributes
:param int count:
The number of security group rules to fake
:return:
A list of FakeResource objects faking the security group rules
:param dict attrs: A dictionary with all attributes
:param int count: The number of security group rules to fake
:return: A list of dictionaries faking the security group rules
"""

@@ -331,62 +246,7 @@ security_group_rules = []

def create_one_server(attrs=None, methods=None):
"""Create a fake server.
def create_one_server(attrs=None):
"""Create a fake server
:param dict attrs:
A dictionary with all attributes
:param dict methods:
A dictionary with all methods
:return:
A FakeResource object, with id, name, metadata, and so on
"""
attrs = attrs or {}
methods = methods or {}
# Set default attributes.
server_info = {
'id': 'server-id-' + uuid.uuid4().hex,
'name': 'server-name-' + uuid.uuid4().hex,
'metadata': {},
'image': {
'id': 'image-id-' + uuid.uuid4().hex,
},
'flavor': {
'id': 'flavor-id-' + uuid.uuid4().hex,
},
'OS-EXT-STS:power_state': 1,
}
# Overwrite default attributes.
server_info.update(attrs)
server = fakes.FakeResource(
info=copy.deepcopy(server_info), methods=methods, loaded=True
)
return server
def create_servers(attrs=None, methods=None, count=2):
"""Create multiple fake servers.
:param dict attrs:
A dictionary with all attributes
:param dict methods:
A dictionary with all methods
:param int count:
The number of servers to fake
:return:
A list of FakeResource objects faking the servers
"""
servers = []
for i in range(0, count):
servers.append(create_one_server(attrs, methods))
return servers
def create_one_sdk_server(attrs=None):
"""Create a fake server for testing migration to sdk
:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.server.Server object,
:return: A fake :class:`~openstack.compute.v2.server.Server` object,
"""

@@ -419,12 +279,12 @@ attrs = attrs or {}

def create_sdk_servers(attrs=None, count=2):
"""Create multiple fake servers for testing migration to sdk
def create_servers(attrs=None, count=2):
"""Create multiple fake servers
:param dict attrs: A dictionary with all attributes
:param int count: The number of servers to fake
:return: A list of fake openstack.compute.v2.server.Server objects
:return: A list of fake :class:`openstack.compute.v2.server.Server` objects
"""
servers = []
for i in range(0, count):
servers.append(create_one_sdk_server(attrs))
servers.append(create_one_server(attrs))

@@ -434,20 +294,2 @@ return servers

def get_servers(servers=None, count=2):
"""Get an iterable MagicMock object with a list of faked servers.
If servers list is provided, then initialize the Mock object with the
list. Otherwise create one.
:param list servers: A list of fake openstack.compute.v2.server.Server
objects
:param int count:
The number of servers to fake
:return: An iterable Mock object with side_effect set to a list of faked
servers
"""
if servers is None:
servers = create_servers(count)
return mock.Mock(side_effect=servers)
def create_one_server_action(attrs=None):

@@ -457,3 +299,4 @@ """Create a fake server action.

:param attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.server_action.ServerAction object
:return: A fake :class:`~openstack.compute.v2.server_action.ServerAction`
object
"""

@@ -501,3 +344,3 @@ attrs = attrs or {}

:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.flavor.Flavor object
:return: A fake :class:`~openstack.compute.v2.flavor.Flavor` object
"""

@@ -535,3 +378,3 @@ attrs = attrs or {}

:param int count: The number of flavors to fake
:return: A list of fake openstack.compute.v2.flavor.Flavor objects
:return: A list of fake :class:`openstack.compute.v2.flavor.Flavor` objects
"""

@@ -545,26 +388,7 @@ flavors = []

def get_flavors(flavors=None, count=2):
"""Get an iterable MagicMock object with a list of faked flavors.
If flavors list is provided, then initialize the Mock object with the
list. Otherwise create one.
:param list flavors: A list of fake openstack.compute.v2.flavor.Flavor
objects
:param int count: The number of flavors to fake
:return: An iterable Mock object with side_effect set to a list of faked
flavors
"""
if flavors is None:
flavors = create_flavors(count)
return mock.Mock(side_effect=flavors)
def create_one_flavor_access(attrs=None):
"""Create a fake flavor access.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with flavor_id, tenat_id
:param dict attrs: A dictionary with all attributes
:return: A dictionary faking the flavor access
"""

@@ -579,12 +403,10 @@ attrs = attrs or {}

assert not set(attrs) - set(flavor_access_info), 'unknown keys'
# Overwrite default attributes.
flavor_access_info.update(attrs)
flavor_access = fakes.FakeResource(
info=copy.deepcopy(flavor_access_info), loaded=True
)
return flavor_access_info
return flavor_access
def create_one_availability_zone(attrs=None):

@@ -594,4 +416,4 @@ """Create a fake AZ.

:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.availability_zone.AvailabilityZone
object
:return: A fake
:class:`~openstack.compute.v2.availability_zone.AvailabilityZone` object
"""

@@ -643,8 +465,6 @@ attrs = attrs or {}

def create_one_floating_ip(attrs=None):
"""Create a fake floating ip.
"""Create a fake floating IP.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with id, ip, and so on
:param dict attrs: A dictionary with all attributes
:return: A dictionary faking the floating IP
"""

@@ -662,2 +482,4 @@ attrs = attrs or {}

assert not set(attrs) - set(floating_ip_attrs), 'unknown keys'
# Overwrite default attributes.

@@ -670,10 +492,7 @@ floating_ip_attrs.update(attrs)

def create_floating_ips(attrs=None, count=2):
"""Create multiple fake floating ips.
"""Create multiple fake floating IPs.
:param dict attrs:
A dictionary with all attributes
:param int count:
The number of floating ips to fake
:return:
A list of FakeResource objects faking the floating ips
:param dict attrs: A dictionary with all attributes
:param int count: The number of floating IPs to fake
:return: A list of dictionaries faking the floating IPs
"""

@@ -686,28 +505,7 @@ floating_ips = []

def get_floating_ips(floating_ips=None, count=2):
"""Get an iterable MagicMock object with a list of faked floating ips.
If floating_ips list is provided, then initialize the Mock object
with the list. Otherwise create one.
:param List floating_ips:
A list of FakeResource objects faking floating ips
:param int count:
The number of floating ips to fake
:return:
An iterable Mock object with side_effect set to a list of faked
floating ips
"""
if floating_ips is None:
floating_ips = create_floating_ips(count)
return mock.Mock(side_effect=floating_ips)
def create_one_floating_ip_pool(attrs=None):
"""Create a fake floating ip pool.
"""Create a fake floating IP pool.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with name, etc
:param dict attrs: A dictionary with all attributes
:return: A dictionary faking the floating IP pool
"""

@@ -722,2 +520,4 @@ if attrs is None:

assert not set(attrs) - set(floating_ip_pool_attrs), 'unknown keys'
# Overwrite default attributes.

@@ -730,10 +530,7 @@ floating_ip_pool_attrs.update(attrs)

def create_floating_ip_pools(attrs=None, count=2):
"""Create multiple fake floating ip pools.
"""Create multiple fake floating IP pools.
:param dict attrs:
A dictionary with all attributes
:param int count:
The number of floating ip pools to fake
:return:
A list of FakeResource objects faking the floating ip pools
:param dict attrs: A dictionary with all attributes
:param int count: The number of floating IP pools to fake
:return: A list of dictionaries faking the floating IP pools
"""

@@ -749,6 +546,4 @@ floating_ip_pools = []

:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with id, label, cidr and so on
:param dict attrs: A dictionary with all attributes
:return: A dictionary faking the network
"""

@@ -793,2 +588,4 @@ attrs = attrs or {}

assert not set(attrs) - set(network_attrs), 'unknown keys'
# Overwrite default attributes.

@@ -803,8 +600,5 @@ network_attrs.update(attrs)

:param dict attrs:
A dictionary with all attributes
:param int count:
The number of networks to fake
:return:
A list of FakeResource objects faking the networks
:param dict attrs: A dictionary with all attributes
:param int count: The number of networks to fake
:return: A list of dictionaries faking the networks
"""

@@ -818,21 +612,2 @@ networks = []

def get_networks(networks=None, count=2):
"""Get an iterable MagicMock object with a list of faked networks.
If networks list is provided, then initialize the Mock object with the
list. Otherwise create one.
:param List networks:
A list of FakeResource objects faking networks
:param int count:
The number of networks to fake
:return:
An iterable Mock object with side_effect set to a list of faked
networks
"""
if networks is None:
networks = create_networks(count=count)
return mock.Mock(side_effect=networks)
def create_limits(attrs=None):

@@ -903,3 +678,3 @@ """Create a fake limits object."""

:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.migration.Migration object
:return: A fake :class:`~openstack.compute.v2.migration.Migration` object
"""

@@ -940,3 +715,3 @@ attrs = attrs or {}

:param int count: The number of migrations to fake
:return: A list of fake openstack.compute.v2.migration.Migration objects
:return: A list of fake :class:`openstack.compute.v2.migration.Migration` objects
"""

@@ -954,3 +729,4 @@ migrations = []

:param dict attrs: A dictionary with all attributes
:return A fake openstack.compute.v2.server_migration.ServerMigration object
:return: A fake
:class:`~openstack.compute.v2.server_migration.ServerMigration` object
"""

@@ -1011,4 +787,4 @@ attrs = attrs or {}

:param dict attrs: A dictionary with all attributes
:return: A fake openstack.compute.v2.volume_attachment.VolumeAttachment
object
:return: A fake
:class:`~openstack.compute.v2.volume_attachment.VolumeAttachment` object
"""

@@ -1054,8 +830,8 @@ attrs = attrs or {}

def create_one_server_interface(attrs=None):
"""Create a fake SDK ServerInterface.
"""Create a fake ServerInterface.
:param dict attrs: A dictionary with all attributes
:param dict methods: A dictionary with all methods
:return: A fake openstack.compute.v2.server_interface.ServerInterface
object
:return: A fake
:class:`~openstack.compute.v2.server_interface.ServerInterface` object
"""

@@ -1062,0 +838,0 @@ attrs = attrs or {}

@@ -160,3 +160,3 @@ # Copyright 2016 Huawei, Inc. All rights reserved.

def test_console_url_show_with_spice(self):
def test_console_url_show_with_spice_html5(self):
arglist = [

@@ -178,2 +178,19 @@ '--spice',

def test_console_url_show_with_spice_direct(self):
arglist = [
'--spice-direct',
'foo_vm',
]
verifylist = [
('url_type', 'spice-direct'),
('server', 'foo_vm'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute_client.create_console.assert_called_once_with(
self._server.id, console_type='spice-direct'
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
def test_console_url_show_with_rdp(self):

@@ -180,0 +197,0 @@ arglist = [

@@ -1055,3 +1055,3 @@ # Copyright 2015 Symantec Corporation

private_flavor.ephemeral,
[self.flavor_access.tenant_id],
[self.flavor_access['tenant_id']],
private_flavor.description,

@@ -1058,0 +1058,0 @@ private_flavor.disk,

@@ -21,2 +21,3 @@ # Copyright 2016 IBM

from openstack.identity.v3 import project as _project
from openstack.identity.v3 import role_assignment as _role_assignment
from openstack.identity.v3 import user as _user

@@ -533,9 +534,13 @@ from openstack.test import fakes as sdk_fakes

projects_mock = self.identity_client.tenants
projects_mock = self.identity_client.projects
projects_mock.reset_mock()
projects_mock.get.return_value = self._project
users_mock = self.identity_client.users
users_mock.reset_mock()
users_mock.list.return_value = [self._user]
role_assignments_mock = self.identity_sdk_client.role_assignments
role_assignments_mock.reset_mock()
assignment = sdk_fakes.generate_fake_resource(
_role_assignment.RoleAssignment
)
assignment.user = self._user
role_assignments_mock.return_value = [assignment]

@@ -549,3 +554,5 @@ arglist = ['--project', self._project.name]

projects_mock.get.assert_called_with(self._project.name)
users_mock.list.assert_called_with(tenant_id=self._project.id)
role_assignments_mock.assert_called_with(
scope_project_id=self._project.id
)
self.compute_client.keypairs.assert_called_with(

@@ -552,0 +559,0 @@ user_id=self._user.id,

@@ -25,18 +25,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

class TestServerBackup(compute_fakes.TestComputev2):
def setup_servers_mock(self, count):
servers = compute_fakes.create_sdk_servers(
count=count,
)
self.compute_client.find_server = compute_fakes.get_servers(
servers,
0,
)
return servers
class TestServerBackupCreate(TestServerBackup):
# Just return whatever Image is testing with these days
class TestServerBackupCreate(compute_fakes.TestComputev2):
def image_columns(self, image):
# columnlist = tuple(sorted(image.keys()))
columnlist = (

@@ -68,31 +54,16 @@ 'id',

self.server = compute_fakes.create_one_server()
self.compute_client.find_server.return_value = self.server
self.image = image_fakes.create_one_image(
{'name': self.server.name, 'status': 'active'}
)
self.image_client.find_image.return_value = self.image
# Get the command object to test
self.cmd = server_backup.CreateServerBackup(self.app, None)
def setup_images_mock(self, count, servers=None):
if servers:
images = image_fakes.create_images(
attrs={
'name': servers[0].name,
'status': 'active',
},
count=count,
)
else:
images = image_fakes.create_images(
attrs={
'status': 'active',
},
count=count,
)
self.image_client.find_image = mock.Mock(side_effect=images)
return images
def test_server_backup_defaults(self):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
arglist = [
servers[0].id,
self.server.id,
]

@@ -104,3 +75,3 @@ verifylist = [

('wait', False),
('server', servers[0].id),
('server', self.server.id),
]

@@ -115,4 +86,4 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.backup_server.assert_called_with(
servers[0].id,
servers[0].name,
self.server.id,
self.server.name,
'',

@@ -122,9 +93,6 @@ 1,

self.assertEqual(self.image_columns(images[0]), columns)
self.assertCountEqual(self.image_data(images[0]), data)
self.assertEqual(self.image_columns(self.image), columns)
self.assertCountEqual(self.image_data(self.image), data)
def test_server_backup_create_options(self):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
arglist = [

@@ -137,3 +105,3 @@ '--name',

'2',
servers[0].id,
self.server.id,
]

@@ -144,3 +112,3 @@ verifylist = [

('rotate', 2),
('server', servers[0].id),
('server', self.server.id),
]

@@ -155,3 +123,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.backup_server.assert_called_with(
servers[0].id,
self.server.id,
'image',

@@ -162,12 +130,8 @@ 'daily',

self.assertEqual(self.image_columns(images[0]), columns)
self.assertCountEqual(self.image_data(images[0]), data)
self.assertEqual(self.image_columns(self.image), columns)
self.assertCountEqual(self.image_data(self.image), data)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_backup_wait_fail(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
self.image_client.get_image = mock.Mock(
side_effect=images[0],
)
self.image_client.get_image.return_value = self.image

@@ -180,3 +144,3 @@ arglist = [

'--wait',
servers[0].id,
self.server.id,
]

@@ -187,3 +151,3 @@ verifylist = [

('wait', True),
('server', servers[0].id),
('server', self.server.id),
]

@@ -199,3 +163,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.backup_server.assert_called_with(
servers[0].id,
self.server.id,
'image',

@@ -207,3 +171,3 @@ 'daily',

mock_wait_for_status.assert_called_once_with(
self.image_client.get_image, images[0].id, callback=mock.ANY
self.image_client.get_image, self.image.id, callback=mock.ANY
)

@@ -213,7 +177,4 @@

def test_server_backup_wait_ok(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
self.image_client.get_image = mock.Mock(
side_effect=images[0],
side_effect=self.image,
)

@@ -227,3 +188,3 @@

'--wait',
servers[0].id,
self.server.id,
]

@@ -234,3 +195,3 @@ verifylist = [

('wait', True),
('server', servers[0].id),
('server', self.server.id),
]

@@ -245,3 +206,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.backup_server.assert_called_with(
servers[0].id,
self.server.id,
'image',

@@ -253,6 +214,6 @@ 'daily',

mock_wait_for_status.assert_called_once_with(
self.image_client.get_image, images[0].id, callback=mock.ANY
self.image_client.get_image, self.image.id, callback=mock.ANY
)
self.assertEqual(self.image_columns(images[0]), columns)
self.assertCountEqual(self.image_data(images[0]), data)
self.assertEqual(self.image_columns(self.image), columns)
self.assertCountEqual(self.image_data(self.image), data)

@@ -25,3 +25,3 @@ # Copyright 2017 Huawei, Inc. All rights reserved.

class TestListServerEvent(compute_fakes.TestComputev2):
fake_server = compute_fakes.create_one_sdk_server()
fake_server = compute_fakes.create_one_server()
fake_event = compute_fakes.create_one_server_action()

@@ -370,3 +370,3 @@

class TestShowServerEvent(compute_fakes.TestComputev2):
fake_server = compute_fakes.create_one_sdk_server()
fake_server = compute_fakes.create_one_server()
fake_event = compute_fakes.create_one_server_action()

@@ -373,0 +373,0 @@ columns = (

@@ -24,17 +24,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

class TestServerImage(compute_fakes.TestComputev2):
def setup_servers_mock(self, count):
servers = compute_fakes.create_sdk_servers(
count=count,
)
self.compute_client.find_server = compute_fakes.get_servers(
servers,
0,
)
return servers
class TestServerImageCreate(TestServerImage):
class TestServerImageCreate(compute_fakes.TestComputev2):
def image_columns(self, image):
# columnlist = tuple(sorted(image.keys()))
columnlist = (

@@ -66,37 +53,20 @@ 'id',

self.server = compute_fakes.create_one_server()
self.compute_client.find_server.return_value = self.server
self.image = image_fakes.create_one_image(
{'name': self.server.name, 'status': 'active'}
)
self.image_client.find_image.return_value = self.image
self.compute_client.create_server_image.return_value = self.image
# Get the command object to test
self.cmd = server_image.CreateServerImage(self.app, None)
def setup_images_mock(self, count, servers=None):
if servers:
images = image_fakes.create_images(
attrs={
'name': servers[0].name,
'status': 'active',
},
count=count,
)
else:
images = image_fakes.create_images(
attrs={
'status': 'active',
},
count=count,
)
self.image_client.find_image = mock.Mock(side_effect=images)
self.compute_client.create_server_image = mock.Mock(
return_value=images[0],
)
return images
def test_server_image_create_defaults(self):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
arglist = [
servers[0].id,
self.server.id,
]
verifylist = [
('server', servers[0].id),
('server', self.server.id),
]

@@ -111,14 +81,11 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.create_server_image.assert_called_with(
servers[0].id,
servers[0].name,
self.server.id,
self.server.name,
None,
)
self.assertEqual(self.image_columns(images[0]), columns)
self.assertCountEqual(self.image_data(images[0]), data)
self.assertEqual(self.image_columns(self.image), columns)
self.assertCountEqual(self.image_data(self.image), data)
def test_server_image_create_options(self):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
arglist = [

@@ -129,7 +96,7 @@ '--name',

'key=value',
servers[0].id,
self.server.id,
]
verifylist = [
('name', 'img-nam'),
('server', servers[0].id),
('server', self.server.id),
('properties', {'key': 'value'}),

@@ -145,3 +112,3 @@ ]

self.compute_client.create_server_image.assert_called_with(
servers[0].id,
self.server.id,
'img-nam',

@@ -151,17 +118,14 @@ {'key': 'value'},

self.assertEqual(self.image_columns(images[0]), columns)
self.assertCountEqual(self.image_data(images[0]), data)
self.assertEqual(self.image_columns(self.image), columns)
self.assertCountEqual(self.image_data(self.image), data)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_create_image_wait_fail(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
arglist = [
'--wait',
servers[0].id,
self.server.id,
]
verifylist = [
('wait', True),
('server', servers[0].id),
('server', self.server.id),
]

@@ -177,4 +141,4 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.create_server_image.assert_called_with(
servers[0].id,
servers[0].name,
self.server.id,
self.server.name,
None,

@@ -184,3 +148,3 @@ )

mock_wait_for_status.assert_called_once_with(
self.image_client.get_image, images[0].id, callback=mock.ANY
self.image_client.get_image, self.image.id, callback=mock.ANY
)

@@ -190,12 +154,9 @@

def test_server_create_image_wait_ok(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1)
images = self.setup_images_mock(count=1, servers=servers)
arglist = [
'--wait',
servers[0].id,
self.server.id,
]
verifylist = [
('wait', True),
('server', servers[0].id),
('server', self.server.id),
]

@@ -210,4 +171,4 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.compute_client.create_server_image.assert_called_with(
servers[0].id,
servers[0].name,
self.server.id,
self.server.name,
None,

@@ -217,6 +178,6 @@ )

mock_wait_for_status.assert_called_once_with(
self.image_client.get_image, images[0].id, callback=mock.ANY
self.image_client.get_image, self.image.id, callback=mock.ANY
)
self.assertEqual(self.image_columns(images[0]), columns)
self.assertCountEqual(self.image_data(images[0]), data)
self.assertEqual(self.image_columns(self.image), columns)
self.assertCountEqual(self.image_data(self.image), data)

@@ -55,3 +55,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

self.server = compute_fakes.create_one_sdk_server()
self.server = compute_fakes.create_one_server()
self.compute_client.find_server.return_value = self.server

@@ -698,3 +698,3 @@

self.server = compute_fakes.create_one_sdk_server()
self.server = compute_fakes.create_one_server()
self.compute_client.find_server.return_value = self.server

@@ -902,3 +902,3 @@

self.server = compute_fakes.create_one_sdk_server()
self.server = compute_fakes.create_one_server()

@@ -1018,3 +1018,3 @@ # Return value for utils.find_resource for server.

self.server = compute_fakes.create_one_sdk_server()
self.server = compute_fakes.create_one_server()

@@ -1021,0 +1021,0 @@ # Return value for utils.find_resource for server.

@@ -27,2 +27,3 @@ # Copyright 2018 SUSE Linux GmbH

from openstack.identity.v3 import role as _role
from openstack.identity.v3 import user as _user
from openstack.test import fakes as sdk_fakes

@@ -329,3 +330,28 @@ from openstackclient.identity.v3 import application_credential

]
self.user = sdk_fakes.generate_fake_resource(resource_type=_user.User)
self.identity_sdk_client.find_user.return_value = self.user
self.columns = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
)
self.data = (
(
self.application_credential.id,
self.application_credential.name,
self.application_credential.description,
self.application_credential.project_id,
'',
self.application_credential.unrestricted,
self.application_credential.access_rules,
self.application_credential.expires_at,
),
)
# Get the command object to test

@@ -344,7 +370,8 @@ self.cmd = application_credential.ListApplicationCredential(

# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
self.identity_sdk_client.find_user.assert_not_called()
self.identity_sdk_client.application_credentials.assert_called_with(

@@ -354,26 +381,21 @@ user=user_id

collist = (
'ID',
'Name',
'Description',
'Project ID',
'Roles',
'Unrestricted',
'Access Rules',
'Expires At',
def test_application_credential_list_user(self):
arglist = ['--user', self.user.name]
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
conn = self.app.client_manager.sdk_connection
conn.config.get_auth().get_user_id(conn.identity)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
self.identity_sdk_client.find_user.assert_called_once_with(
name_or_id=self.user.name, ignore_missing=False
)
self.assertEqual(collist, columns)
datalist = (
(
self.application_credential.id,
self.application_credential.name,
self.application_credential.description,
self.application_credential.project_id,
self.application_credential.roles,
self.application_credential.unrestricted,
self.application_credential.access_rules,
self.application_credential.expires_at,
),
self.identity_sdk_client.application_credentials.assert_called_with(
user=self.user.id
)
self.assertEqual(datalist, tuple(data))

@@ -380,0 +402,0 @@

@@ -13,2 +13,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

from openstack.identity.v3 import domain as _domain
from openstack.test import fakes as sdk_fakes
from openstackclient.identity.v3 import domain

@@ -18,25 +20,22 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes

class TestDomain(identity_fakes.TestIdentityv3):
def setUp(self):
super().setUp()
class TestDomainCreate(identity_fakes.TestIdentityv3):
columns = (
'id',
'name',
'enabled',
'description',
'options',
)
# Get a shortcut to the DomainManager Mock
self.domains_mock = self.identity_client.domains
self.domains_mock.reset_mock()
class TestDomainCreate(TestDomain):
columns = ('description', 'enabled', 'id', 'name', 'tags')
def setUp(self):
super().setUp()
self.domain = identity_fakes.FakeDomain.create_one_domain()
self.domains_mock.create.return_value = self.domain
self.domain = sdk_fakes.generate_fake_resource(_domain.Domain)
self.identity_sdk_client.create_domain.return_value = self.domain
self.datalist = (
self.domain.description,
True,
self.domain.id,
self.domain.name,
self.domain.tags,
self.domain.is_enabled,
self.domain.description,
self.domain.options,
)

@@ -66,5 +65,5 @@

'options': {},
'enabled': True,
'is_enabled': True,
}
self.domains_mock.create.assert_called_with(**kwargs)
self.identity_sdk_client.create_domain.assert_called_with(**kwargs)

@@ -96,5 +95,5 @@ self.assertEqual(self.columns, columns)

'options': {},
'enabled': True,
'is_enabled': True,
}
self.domains_mock.create.assert_called_with(**kwargs)
self.identity_sdk_client.create_domain.assert_called_with(**kwargs)

@@ -110,3 +109,3 @@ self.assertEqual(self.columns, columns)

verifylist = [
('enable', True),
('is_enabled', True),
('name', self.domain.name),

@@ -126,5 +125,5 @@ ]

'options': {},
'enabled': True,
'is_enabled': True,
}
self.domains_mock.create.assert_called_with(**kwargs)
self.identity_sdk_client.create_domain.assert_called_with(**kwargs)

@@ -140,3 +139,3 @@ self.assertEqual(self.columns, columns)

verifylist = [
('disable', True),
('is_enabled', False),
('name', self.domain.name),

@@ -156,5 +155,5 @@ ]

'options': {},
'enabled': False,
'is_enabled': False,
}
self.domains_mock.create.assert_called_with(**kwargs)
self.identity_sdk_client.create_domain.assert_called_with(**kwargs)

@@ -185,5 +184,5 @@ self.assertEqual(self.columns, columns)

'options': {'immutable': True},
'enabled': True,
'is_enabled': True,
}
self.domains_mock.create.assert_called_with(**kwargs)
self.identity_sdk_client.create_domain.assert_called_with(**kwargs)

@@ -214,5 +213,5 @@ self.assertEqual(self.columns, columns)

'options': {'immutable': False},
'enabled': True,
'is_enabled': True,
}
self.domains_mock.create.assert_called_with(**kwargs)
self.identity_sdk_client.create_domain.assert_called_with(**kwargs)

@@ -223,4 +222,4 @@ self.assertEqual(self.columns, columns)

class TestDomainDelete(TestDomain):
domain = identity_fakes.FakeDomain.create_one_domain()
class TestDomainDelete(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)

@@ -231,4 +230,4 @@ def setUp(self):

# This is the return value for utils.find_resource()
self.domains_mock.get.return_value = self.domain
self.domains_mock.delete.return_value = None
self.identity_sdk_client.find_domain.return_value = self.domain
self.identity_sdk_client.delete_domain.return_value = None

@@ -249,3 +248,3 @@ # Get the command object to test

self.domains_mock.delete.assert_called_with(
self.identity_sdk_client.delete_domain.assert_called_with(
self.domain.id,

@@ -256,4 +255,12 @@ )

class TestDomainList(TestDomain):
domain = identity_fakes.FakeDomain.create_one_domain()
class TestDomainList(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(
resource_type=_domain.Domain, is_enabled=True
)
columns = (
'ID',
'Name',
'Enabled',
'Description',
)

@@ -263,3 +270,11 @@ def setUp(self):

self.domains_mock.list.return_value = [self.domain]
self.identity_sdk_client.domains.return_value = [self.domain]
self.datalist = (
(
self.domain.id,
self.domain.name,
self.domain.is_enabled,
self.domain.description,
),
)

@@ -278,15 +293,6 @@ # Get the command object to test

columns, data = self.cmd.take_action(parsed_args)
self.domains_mock.list.assert_called_with()
self.identity_sdk_client.domains.assert_called_with()
collist = ('ID', 'Name', 'Enabled', 'Description')
self.assertEqual(collist, columns)
datalist = (
(
self.domain.id,
self.domain.name,
True,
self.domain.description,
),
)
self.assertEqual(datalist, tuple(data))
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))

@@ -304,19 +310,10 @@ def test_domain_list_with_option_name(self):

kwargs = {'name': self.domain.name}
self.domains_mock.list.assert_called_with(**kwargs)
self.identity_sdk_client.domains.assert_called_with(**kwargs)
collist = ('ID', 'Name', 'Enabled', 'Description')
self.assertEqual(collist, columns)
datalist = (
(
self.domain.id,
self.domain.name,
True,
self.domain.description,
),
)
self.assertEqual(datalist, tuple(data))
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
def test_domain_list_with_option_enabled(self):
arglist = ['--enabled']
verifylist = [('enabled', True)]
verifylist = [('is_enabled', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)

@@ -329,20 +326,11 @@

kwargs = {'enabled': True}
self.domains_mock.list.assert_called_with(**kwargs)
kwargs = {'is_enabled': True}
self.identity_sdk_client.domains.assert_called_with(**kwargs)
collist = ('ID', 'Name', 'Enabled', 'Description')
self.assertEqual(collist, columns)
datalist = (
(
self.domain.id,
self.domain.name,
True,
self.domain.description,
),
)
self.assertEqual(datalist, tuple(data))
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
class TestDomainSet(TestDomain):
domain = identity_fakes.FakeDomain.create_one_domain()
class TestDomainSet(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)

@@ -352,5 +340,5 @@ def setUp(self):

self.domains_mock.get.return_value = self.domain
self.identity_sdk_client.find_domain.return_value = self.domain
self.domains_mock.update.return_value = self.domain
self.identity_sdk_client.update_domain.return_value = self.domain

@@ -372,3 +360,5 @@ # Get the command object to test

kwargs = {}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)

@@ -394,3 +384,5 @@

}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)

@@ -416,3 +408,5 @@

}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)

@@ -426,3 +420,3 @@

verifylist = [
('enable', True),
('is_enabled', True),
('domain', self.domain.id),

@@ -436,5 +430,7 @@ ]

kwargs = {
'enabled': True,
'is_enabled': True,
}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)

@@ -448,3 +444,3 @@

verifylist = [
('disable', True),
('is_enabled', False),
('domain', self.domain.id),

@@ -458,5 +454,7 @@ ]

kwargs = {
'enabled': False,
'is_enabled': False,
}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)

@@ -481,3 +479,5 @@

}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)

@@ -502,12 +502,30 @@

}
self.domains_mock.update.assert_called_with(self.domain.id, **kwargs)
self.identity_sdk_client.update_domain.assert_called_with(
self.domain.id, **kwargs
)
self.assertIsNone(result)
class TestDomainShow(TestDomain):
class TestDomainShow(identity_fakes.TestIdentityv3):
columns = (
'id',
'name',
'enabled',
'description',
'options',
)
def setUp(self):
super().setUp()
self.domain = identity_fakes.FakeDomain.create_one_domain()
self.domains_mock.get.return_value = self.domain
self.domain = sdk_fakes.generate_fake_resource(_domain.Domain)
self.identity_sdk_client.find_domain.return_value = self.domain
self.datalist = (
self.domain.id,
self.domain.name,
self.domain.is_enabled,
self.domain.description,
self.domain.options,
)
# Get the command object to test

@@ -532,15 +550,7 @@ self.cmd = domain.ShowDomain(self.app, None)

columns, data = self.cmd.take_action(parsed_args)
self.domains_mock.get.assert_called_with(
self.identity_sdk_client.find_domain.assert_called_with(
self.domain.id,
)
collist = ('description', 'enabled', 'id', 'name', 'tags')
self.assertEqual(collist, columns)
datalist = (
self.domain.description,
True,
self.domain.id,
self.domain.name,
self.domain.tags,
)
self.assertEqual(datalist, data)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)

@@ -17,5 +17,8 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

from keystoneauth1 import exceptions as ks_exc
from openstack import exceptions as sdk_exc
from openstack.identity.v3 import domain as _domain
from openstack.identity.v3 import group as _group
from openstack.identity.v3 import user as _user
from openstack.test import fakes as sdk_fakes
from osc_lib import exceptions
from osc_lib import utils

@@ -26,33 +29,18 @@ from openstackclient.identity.v3 import group

class TestGroup(identity_fakes.TestIdentityv3):
class TestGroupAddUser(identity_fakes.TestIdentityv3):
def setUp(self):
super().setUp()
# Get a shortcut to the DomainManager Mock
self.domains_mock = self.identity_client.domains
self.domains_mock.reset_mock()
self._group = sdk_fakes.generate_fake_resource(_group.Group)
self.users = tuple(
sdk_fakes.generate_fake_resources(_user.User, count=2)
)
# Get a shortcut to the GroupManager Mock
self.groups_mock = self.identity_client.groups
self.groups_mock.reset_mock()
self.identity_sdk_client.find_group.return_value = self._group
self.identity_sdk_client.add_user_to_group.return_value = None
# Get a shortcut to the UserManager Mock
self.users_mock = self.identity_client.users
self.users_mock.reset_mock()
class TestGroupAddUser(TestGroup):
_group = identity_fakes.FakeGroup.create_one_group()
users = identity_fakes.FakeUser.create_users(count=2)
def setUp(self):
super().setUp()
self.groups_mock.get.return_value = self._group
self.users_mock.get = identity_fakes.FakeUser.get_users(self.users)
self.users_mock.add_to_group.return_value = None
self.cmd = group.AddUserToGroup(self.app, None)
def test_group_add_user(self):
self.identity_sdk_client.find_user.return_value = self.users[0]
arglist = [

@@ -69,3 +57,3 @@ self._group.name,

result = self.cmd.take_action(parsed_args)
self.users_mock.add_to_group.assert_called_once_with(
self.identity_sdk_client.add_user_to_group.assert_called_once_with(
self.users[0].id, self._group.id

@@ -76,2 +64,6 @@ )

def test_group_add_multi_users(self):
self.identity_sdk_client.find_user.side_effect = [
self.users[0],
self.users[1],
]
arglist = [

@@ -93,3 +85,3 @@ self._group.name,

]
self.users_mock.add_to_group.assert_has_calls(calls)
self.identity_sdk_client.add_user_to_group.assert_has_calls(calls)
self.assertIsNone(result)

@@ -99,4 +91,4 @@

def test_group_add_user_with_error(self, mock_error):
self.users_mock.add_to_group.side_effect = [
exceptions.CommandError(),
self.identity_sdk_client.add_user_to_group.side_effect = [
sdk_exc.ResourceNotFound,
None,

@@ -120,17 +112,17 @@ ]

self.assertEqual(msg, str(e))
msg = f"{self.users[0].name} not added to group {self._group.name}: "
msg = f"{self.users[0].name} not added to group {self._group.name}: {str(sdk_exc.ResourceNotFound())}"
mock_error.assert_called_once_with(msg)
class TestGroupCheckUser(TestGroup):
group = identity_fakes.FakeGroup.create_one_group()
user = identity_fakes.FakeUser.create_one_user()
class TestGroupCheckUser(identity_fakes.TestIdentityv3):
def setUp(self):
super().setUp()
self.groups_mock.get.return_value = self.group
self.users_mock.get.return_value = self.user
self.users_mock.check_in_group.return_value = None
self.group = sdk_fakes.generate_fake_resource(_group.Group)
self.user = sdk_fakes.generate_fake_resource(_user.User)
self.identity_sdk_client.find_group.return_value = self.group
self.identity_sdk_client.find_user.return_value = self.user
self.identity_sdk_client.check_user_in_group.return_value = True
self.cmd = group.CheckUserInGroup(self.app, None)

@@ -150,3 +142,3 @@

result = self.cmd.take_action(parsed_args)
self.users_mock.check_in_group.assert_called_once_with(
self.identity_sdk_client.check_user_in_group.assert_called_once_with(
self.user.id, self.group.id

@@ -157,6 +149,5 @@ )

def test_group_check_user_server_error(self):
def server_error(*args):
raise ks_exc.http.InternalServerError
self.users_mock.check_in_group.side_effect = server_error
self.identity_sdk_client.check_user_in_group.side_effect = (
sdk_exc.SDKException
)
arglist = [

@@ -173,8 +164,8 @@ self.group.name,

self.assertRaises(
ks_exc.http.InternalServerError, self.cmd.take_action, parsed_args
sdk_exc.SDKException, self.cmd.take_action, parsed_args
)
class TestGroupCreate(TestGroup):
domain = identity_fakes.FakeDomain.create_one_domain()
class TestGroupCreate(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)

@@ -190,15 +181,11 @@ columns = (

super().setUp()
self.group = identity_fakes.FakeGroup.create_one_group(
attrs={'domain_id': self.domain.id}
self.group = sdk_fakes.generate_fake_resource(
_group.Group, description=None, domain_id=None
)
self.data = (
self.group.description,
self.group.domain_id,
self.group.id,
self.group.name,
self.group_with_options = sdk_fakes.generate_fake_resource(
_group.Group, domain_id=self.domain.id
)
self.groups_mock.create.return_value = self.group
self.groups_mock.get.return_value = self.group
self.domains_mock.get.return_value = self.domain
self.identity_sdk_client.find_group.return_value = self.group
self.identity_sdk_client.find_domain.return_value = self.domain

@@ -208,2 +195,3 @@ self.cmd = group.CreateGroup(self.app, None)

def test_group_create(self):
self.identity_sdk_client.create_group.return_value = self.group
arglist = [

@@ -218,11 +206,18 @@ self.group.name,

columns, data = self.cmd.take_action(parsed_args)
self.groups_mock.create.assert_called_once_with(
self.identity_sdk_client.create_group.assert_called_once_with(
name=self.group.name,
domain=None,
description=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
datalist = (
self.group.description,
None,
self.group.id,
self.group.name,
)
self.assertEqual(datalist, data)
def test_group_create_with_options(self):
self.identity_sdk_client.create_group.return_value = (
self.group_with_options
)
arglist = [

@@ -232,9 +227,9 @@ '--domain',

'--description',
self.group.description,
self.group.name,
self.group_with_options.description,
self.group_with_options.name,
]
verifylist = [
('domain', self.domain.name),
('description', self.group.description),
('name', self.group.name),
('description', self.group_with_options.description),
('name', self.group_with_options.name),
]

@@ -244,12 +239,21 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

columns, data = self.cmd.take_action(parsed_args)
self.groups_mock.create.assert_called_once_with(
name=self.group.name,
domain=self.domain.id,
description=self.group.description,
self.identity_sdk_client.create_group.assert_called_once_with(
name=self.group_with_options.name,
domain_id=self.domain.id,
description=self.group_with_options.description,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
datalist = (
self.group_with_options.description,
self.domain.id,
self.group_with_options.id,
self.group_with_options.name,
)
self.assertEqual(datalist, data)
def test_group_create_or_show(self):
self.groups_mock.create.side_effect = ks_exc.Conflict()
self.identity_sdk_client.find_group.return_value = self.group
self.identity_sdk_client.create_group.side_effect = (
sdk_exc.ConflictException
)
arglist = [

@@ -266,19 +270,65 @@ '--or-show',

columns, data = self.cmd.take_action(parsed_args)
self.groups_mock.get.assert_called_once_with(self.group.name)
self.identity_sdk_client.find_group.assert_called_once_with(
self.group.name
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
datalist = (
self.group.description,
None,
self.group.id,
self.group.name,
)
self.assertEqual(datalist, data)
def test_group_create_or_show_with_domain(self):
self.identity_sdk_client.find_group.return_value = (
self.group_with_options
)
self.identity_sdk_client.create_group.side_effect = (
sdk_exc.ConflictException
)
arglist = [
'--or-show',
self.group_with_options.name,
'--domain',
self.domain.id,
]
verifylist = [
('or_show', True),
('name', self.group_with_options.name),
('domain', self.domain.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
class TestGroupDelete(TestGroup):
domain = identity_fakes.FakeDomain.create_one_domain()
groups = identity_fakes.FakeGroup.create_groups(
attrs={'domain_id': domain.id}, count=2
)
columns, data = self.cmd.take_action(parsed_args)
self.identity_sdk_client.find_group.assert_called_once_with(
self.group_with_options.name, domain_id=self.domain.id
)
self.assertEqual(self.columns, columns)
datalist = (
self.group_with_options.description,
self.domain.id,
self.group_with_options.id,
self.group_with_options.name,
)
self.assertEqual(datalist, data)
class TestGroupDelete(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)
def setUp(self):
super().setUp()
self.groups_mock.get = identity_fakes.FakeGroup.get_groups(self.groups)
self.groups_mock.delete.return_value = None
self.domains_mock.get.return_value = self.domain
self.group = sdk_fakes.generate_fake_resource(
_group.Group,
domain_id=None,
)
self.group_with_domain = sdk_fakes.generate_fake_resource(
_group.Group,
name=self.group.name,
domain_id=self.domain.id,
)
self.identity_sdk_client.delete_group.return_value = None
self.identity_sdk_client.find_domain.return_value = self.domain

@@ -288,7 +338,8 @@ self.cmd = group.DeleteGroup(self.app, None)

def test_group_delete(self):
self.identity_sdk_client.find_group.return_value = self.group
arglist = [
self.groups[0].id,
self.group.id,
]
verifylist = [
('groups', [self.groups[0].id]),
('groups', [self.group.id]),
]

@@ -298,12 +349,16 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

result = self.cmd.take_action(parsed_args)
self.groups_mock.get.assert_called_once_with(self.groups[0].id)
self.groups_mock.delete.assert_called_once_with(self.groups[0].id)
self.identity_sdk_client.find_group.assert_called_once_with(
name_or_id=self.group.id, ignore_missing=False
)
self.identity_sdk_client.delete_group.assert_called_once_with(
self.group.id
)
self.assertIsNone(result)
def test_group_multi_delete(self):
arglist = []
verifylist = []
for g in self.groups:
arglist.append(g.id)
self.identity_sdk_client.find_group.side_effect = [
self.group,
self.group_with_domain,
]
arglist = [self.group.id, self.group_with_domain.id]
verifylist = [

@@ -316,20 +371,23 @@ ('groups', arglist),

calls = []
for g in self.groups:
calls.append(call(g.id))
self.groups_mock.delete.assert_has_calls(calls)
self.identity_sdk_client.delete_group.assert_has_calls(
[mock.call(self.group.id), mock.call(self.group_with_domain.id)]
)
self.assertIsNone(result)
def test_group_delete_with_domain(self):
get_mock_result = [exceptions.CommandError, self.groups[0]]
self.groups_mock.get = mock.Mock(side_effect=get_mock_result)
self.identity_sdk_client.find_domain.side_effect = [
sdk_exc.ForbiddenException
]
self.identity_sdk_client.find_group.return_value = (
self.group_with_domain
)
arglist = [
'--domain',
self.domain.id,
self.groups[0].id,
self.group_with_domain.domain_id,
self.group_with_domain.name,
]
verifylist = [
('domain', self.groups[0].domain_id),
('groups', [self.groups[0].id]),
('domain', self.domain.id),
('groups', [self.group_with_domain.name]),
]

@@ -339,13 +397,21 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

result = self.cmd.take_action(parsed_args)
self.groups_mock.get.assert_any_call(
self.groups[0].id, domain_id=self.domain.id
self.identity_sdk_client.find_group.assert_called_with(
name_or_id=self.group_with_domain.name,
ignore_missing=False,
domain_id=self.domain.id,
)
self.groups_mock.delete.assert_called_once_with(self.groups[0].id)
self.identity_sdk_client.delete_group.assert_called_once_with(
self.group_with_domain.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]
def test_delete_multi_groups_with_exception(self):
self.identity_sdk_client.find_group.side_effect = [
self.group,
self.group_with_domain,
exceptions.CommandError,
]
arglist = [
self.groups[0].id,
self.group.id,
self.group_with_domain.id,
'unexist_group',

@@ -362,15 +428,25 @@ ]

except exceptions.CommandError as e:
self.assertEqual('1 of 2 groups failed to delete.', str(e))
self.assertEqual('1 of 3 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.identity_sdk_client.find_group.assert_has_calls(
[
mock.call(name_or_id=self.group.id, ignore_missing=False),
mock.call(
name_or_id=self.group_with_domain.id, ignore_missing=False
),
mock.call(name_or_id='unexist_group', ignore_missing=False),
]
)
self.assertEqual(2, find_mock.call_count)
self.groups_mock.delete.assert_called_once_with(self.groups[0].id)
self.assertEqual(3, self.identity_sdk_client.find_group.call_count)
self.identity_sdk_client.delete_group.assert_has_calls(
[
mock.call(self.group.id),
mock.call(self.group_with_domain.id),
]
)
class TestGroupList(TestGroup):
domain = identity_fakes.FakeDomain.create_one_domain()
group = identity_fakes.FakeGroup.create_one_group()
user = identity_fakes.FakeUser.create_one_user()
class TestGroupList(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)

@@ -381,8 +457,2 @@ columns = (

)
datalist = (
(
group.id,
group.name,
),
)

@@ -392,9 +462,13 @@ def setUp(self):

self.groups_mock.get.return_value = self.group
self.groups_mock.list.return_value = [self.group]
self.group = sdk_fakes.generate_fake_resource(
_group.Group, description=None, domain_id=None
)
self.group_with_domain = sdk_fakes.generate_fake_resource(
_group.Group, domain_id=self.domain.id
)
self.user = sdk_fakes.generate_fake_resource(_user.User)
self.domains_mock.get.return_value = self.domain
self.identity_sdk_client.find_user.return_value = self.user
self.identity_sdk_client.find_domain.return_value = self.domain
self.users_mock.get.return_value = self.user
# Get the command object to test

@@ -404,2 +478,6 @@ self.cmd = group.ListGroup(self.app, None)

def test_group_list_no_options(self):
self.identity_sdk_client.groups.return_value = [
self.group,
self.group_with_domain,
]
arglist = []

@@ -414,14 +492,19 @@ verifylist = []

# Set expected values
kwargs = {
'domain': None,
'user': None,
}
self.identity_sdk_client.groups.assert_called_with()
self.groups_mock.list.assert_called_with(**kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
datalist = (
(
self.group.id,
self.group.name,
),
(
self.group_with_domain.id,
self.group_with_domain.name,
),
)
self.assertEqual(datalist, tuple(data))
def test_group_list_domain(self):
self.identity_sdk_client.groups.return_value = [self.group_with_domain]
arglist = [

@@ -443,12 +526,13 @@ '--domain',

kwargs = {
'domain': self.domain.id,
'user': None,
'domain_id': self.domain.id,
}
self.groups_mock.list.assert_called_with(**kwargs)
self.identity_sdk_client.groups.assert_called_with(**kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
datalist = ((self.group_with_domain.id, self.group_with_domain.name),)
self.assertEqual(datalist, tuple(data))
def test_group_list_user(self):
self.identity_sdk_client.user_groups.return_value = [self.group]
arglist = [

@@ -468,14 +552,49 @@ '--user',

self.identity_sdk_client.user_groups.assert_called_with(self.user.id)
self.assertEqual(self.columns, columns)
datalist = ((self.group.id, self.group.name),)
self.assertEqual(datalist, tuple(data))
def test_group_list_user_domain(self):
self.identity_sdk_client.user_groups.return_value = [
self.group_with_domain
]
arglist = [
'--user',
self.user.name,
'--domain',
self.domain.name,
]
verifylist = [
('user', self.user.name),
('domain', self.domain.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'domain': None,
'user': self.user.id,
'domain_id': self.domain.id,
}
self.groups_mock.list.assert_called_with(**kwargs)
self.identity_sdk_client.user_groups.assert_called_with(
self.user.id, **kwargs
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
datalist = ((self.group_with_domain.id, self.group_with_domain.name),)
self.assertEqual(datalist, tuple(data))
def test_group_list_long(self):
self.identity_sdk_client.groups.return_value = [
self.group,
self.group_with_domain,
]
arglist = [

@@ -494,11 +613,5 @@ '--long',

# Set expected values
kwargs = {
'domain': None,
'user': None,
}
self.identity_sdk_client.groups.assert_called_with()
self.groups_mock.list.assert_called_with(**kwargs)
columns = self.columns + (
long_columns = self.columns + (
'Domain ID',

@@ -514,21 +627,29 @@ 'Description',

),
(
self.group_with_domain.id,
self.group_with_domain.name,
self.group_with_domain.domain_id,
self.group_with_domain.description,
),
)
self.assertEqual(columns, columns)
self.assertEqual(long_columns, columns)
self.assertEqual(datalist, tuple(data))
class TestGroupRemoveUser(TestGroup):
_group = identity_fakes.FakeGroup.create_one_group()
users = identity_fakes.FakeUser.create_users(count=2)
class TestGroupRemoveUser(identity_fakes.TestIdentityv3):
def setUp(self):
super().setUp()
self.groups_mock.get.return_value = self._group
self.users_mock.get = identity_fakes.FakeUser.get_users(self.users)
self.users_mock.remove_from_group.return_value = None
self._group = sdk_fakes.generate_fake_resource(_group.Group)
self.users = tuple(
sdk_fakes.generate_fake_resources(_user.User, count=2)
)
self.identity_sdk_client.find_group.return_value = self._group
self.identity_sdk_client.remove_user_from_group.return_value = None
self.cmd = group.RemoveUserFromGroup(self.app, None)
def test_group_remove_user(self):
self.identity_sdk_client.find_user.return_value = self.users[0]
arglist = [

@@ -545,3 +666,3 @@ self._group.id,

result = self.cmd.take_action(parsed_args)
self.users_mock.remove_from_group.assert_called_once_with(
self.identity_sdk_client.remove_user_from_group.assert_called_once_with(
self.users[0].id, self._group.id

@@ -552,2 +673,6 @@ )

def test_group_remove_multi_users(self):
self.identity_sdk_client.find_user.side_effect = [
self.users[0],
self.users[1],
]
arglist = [

@@ -569,3 +694,3 @@ self._group.name,

]
self.users_mock.remove_from_group.assert_has_calls(calls)
self.identity_sdk_client.remove_user_from_group.assert_has_calls(calls)
self.assertIsNone(result)

@@ -575,4 +700,4 @@

def test_group_remove_user_with_error(self, mock_error):
self.users_mock.remove_from_group.side_effect = [
exceptions.CommandError(),
self.identity_sdk_client.remove_user_from_group.side_effect = [
sdk_exc.ResourceNotFound(),
None,

@@ -596,18 +721,20 @@ ]

self.assertEqual(msg, str(e))
msg = f"{self.users[0].id} not removed from group {self._group.id}: "
msg = f"{self.users[0].id} not removed from group {self._group.id}: {str(sdk_exc.ResourceNotFound())}"
mock_error.assert_called_once_with(msg)
class TestGroupSet(TestGroup):
domain = identity_fakes.FakeDomain.create_one_domain()
group = identity_fakes.FakeGroup.create_one_group(
attrs={'domain_id': domain.id}
)
class TestGroupSet(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)
def setUp(self):
super().setUp()
self.group = sdk_fakes.generate_fake_resource(
_group.Group, domain_id=self.domain.id
)
self.group_with_domain = sdk_fakes.generate_fake_resource(
_group.Group, name=self.group.name, domain_id=self.domain.id
)
self.groups_mock.get.return_value = self.group
self.domains_mock.get.return_value = self.domain
self.groups_mock.update.return_value = None
self.identity_sdk_client.find_group.return_value = self.group
self.identity_sdk_client.find_domain.return_value = self.domain

@@ -617,2 +744,3 @@ self.cmd = group.SetGroup(self.app, None)

def test_group_set_nothing(self):
self.identity_sdk_client.update_group.return_value = self.group
arglist = [

@@ -627,6 +755,9 @@ self.group.id,

result = self.cmd.take_action(parsed_args)
self.groups_mock.update.assert_called_once_with(self.group.id)
self.identity_sdk_client.update_group.assert_called_once_with(
self.group.id
)
self.assertIsNone(result)
def test_group_set_name_and_description(self):
self.identity_sdk_client.update_group.return_value = self.group
arglist = [

@@ -651,3 +782,3 @@ '--name',

}
self.groups_mock.update.assert_called_once_with(
self.identity_sdk_client.update_group.assert_called_once_with(
self.group.id, **kwargs

@@ -658,13 +789,16 @@ )

def test_group_set_with_domain(self):
get_mock_result = [exceptions.CommandError, self.group]
self.groups_mock.get = mock.Mock(side_effect=get_mock_result)
self.identity_sdk_client.find_domain.side_effect = [
sdk_exc.ForbiddenException
]
self.identity_sdk_client.find_group.return_value = (
self.group_with_domain
)
arglist = [
'--domain',
self.domain.id,
self.group.id,
self.group_with_domain.name,
]
verifylist = [
('domain', self.domain.id),
('group', self.group.id),
('group', self.group_with_domain.name),
]

@@ -674,11 +808,15 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

result = self.cmd.take_action(parsed_args)
self.groups_mock.get.assert_any_call(
self.group.id, domain_id=self.domain.id
self.identity_sdk_client.find_group.assert_called_once_with(
name_or_id=self.group_with_domain.name,
ignore_missing=False,
domain_id=self.domain.id,
)
self.groups_mock.update.assert_called_once_with(self.group.id)
self.identity_sdk_client.update_group.assert_called_once_with(
self.group_with_domain.id
)
self.assertIsNone(result)
class TestGroupShow(TestGroup):
domain = identity_fakes.FakeDomain.create_one_domain()
class TestGroupShow(identity_fakes.TestIdentityv3):
domain = sdk_fakes.generate_fake_resource(_domain.Domain)

@@ -694,14 +832,10 @@ columns = (

super().setUp()
self.group = identity_fakes.FakeGroup.create_one_group(
attrs={'domain_id': self.domain.id}
self.group = sdk_fakes.generate_fake_resource(
_group.Group, description=None, domain_id=None
)
self.data = (
self.group.description,
self.group.domain_id,
self.group.id,
self.group.name,
self.group_with_domain = sdk_fakes.generate_fake_resource(
_group.Group, name=self.group.name, domain_id=self.domain.id
)
self.groups_mock.get.return_value = self.group
self.domains_mock.get.return_value = self.domain
self.identity_sdk_client.find_domain.return_value = self.domain

@@ -711,2 +845,3 @@ self.cmd = group.ShowGroup(self.app, None)

def test_group_show(self):
self.identity_sdk_client.find_group.return_value = self.group
arglist = [

@@ -721,18 +856,26 @@ self.group.id,

columns, data = self.cmd.take_action(parsed_args)
self.groups_mock.get.assert_called_once_with(self.group.id)
self.identity_sdk_client.find_group.assert_called_once_with(
self.group.id, ignore_missing=False
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
datalist = (
None,
None,
self.group.id,
self.group.name,
)
self.assertEqual(datalist, data)
def test_group_show_with_domain(self):
get_mock_result = [exceptions.CommandError, self.group]
self.groups_mock.get = mock.Mock(side_effect=get_mock_result)
self.identity_sdk_client.find_group.return_value = (
self.group_with_domain
)
arglist = [
'--domain',
self.domain.id,
self.group.id,
self.group_with_domain.name,
]
verifylist = [
('domain', self.domain.id),
('group', self.group.id),
('group', self.group_with_domain.name),
]

@@ -742,6 +885,14 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

columns, data = self.cmd.take_action(parsed_args)
self.groups_mock.get.assert_any_call(
self.group.id, domain_id=self.domain.id
self.identity_sdk_client.find_group.assert_called_once_with(
self.group_with_domain.name,
domain_id=self.domain.id,
ignore_missing=False,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
datalist = (
self.group_with_domain.description,
self.domain.id,
self.group_with_domain.id,
self.group_with_domain.name,
)
self.assertEqual(datalist, data)

@@ -944,3 +944,19 @@ # Copyright 2013 Nebula Inc.

def test_project_list_with_option_enabled(self):
arglist = ['--enabled']
verifylist = [('is_enabled', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
kwargs = {'is_enabled': True}
self.projects_mock.list.assert_called_with(**kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
class TestProjectSet(TestProject):

@@ -947,0 +963,0 @@ domain = identity_fakes.FakeDomain.create_one_domain()

@@ -47,2 +47,3 @@ # Copyright 2013 Nebula Inc.

'password_expires_at',
'options',
)

@@ -67,2 +68,3 @@

self.user.password_expires_at,
getattr(self.user, 'options', {}),
)

@@ -168,2 +170,49 @@

def test_user_create_password_prompt_no_warning(self):
arglist = [
'--password-prompt',
self.user.name,
]
verifylist = [
('password', None),
('password_prompt', True),
('enable', False),
('disable', False),
('name', self.user.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
import logging
# Mock the password prompt
mocker = mock.Mock()
mocker.return_value = 'abc123'
# Use assertLogs to verify no warnings are logged
logger = 'openstackclient.identity.v3.user'
with mock.patch("osc_lib.utils.get_password", mocker):
with self.assertLogs(logger, level='WARNING') as log_ctx:
logging.getLogger(logger).warning(
"Dummy warning for test setup"
)
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(1, len(log_ctx.records))
self.assertIn(
"Dummy warning for test setup", log_ctx.output[0]
)
self.assertNotIn(
"No password was supplied", ''.join(log_ctx.output)
)
# Set expected values
kwargs = {
'name': self.user.name,
'is_enabled': True,
'password': 'abc123',
}
self.identity_sdk_client.create_user.assert_called_with(**kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
def test_user_create_email(self):

@@ -238,2 +287,3 @@ arglist = [

self.user.password_expires_at,
getattr(self.user, 'options', {}),
)

@@ -286,2 +336,3 @@ self.assertEqual(datalist, data)

self.user.password_expires_at,
getattr(self.user, 'options', {}),
)

@@ -996,3 +1047,21 @@ self.assertEqual(datalist, data)

def test_user_list_with_option_enabled(self):
arglist = ['--enabled']
verifylist = [('is_enabled', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
kwargs = {'domain_id': None, 'is_enabled': True}
self.identity_sdk_client.users.assert_called_with(**kwargs)
self.identity_sdk_client.find_user.assert_not_called()
self.identity_sdk_client.group_users.assert_not_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
class TestUserSet(identity_fakes.TestIdentityv3):

@@ -1695,7 +1764,10 @@ project = sdk_fakes.generate_fake_resource(_project.Project)

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
conn = self.app.client_manager.sdk_connection
user_id = conn.config.get_auth().get_user_id(conn.identity)
self.identity_sdk_client.update_user.assert_called_with(
current_password=current_pass, password=new_pass
user=user_id, current_password=current_pass, password=new_pass
)
self.assertIsNone(result)

@@ -1710,7 +1782,10 @@ def test_user_create_password_prompt(self):

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
conn = self.app.client_manager.sdk_connection
user_id = conn.config.get_auth().get_user_id(conn.identity)
self.identity_sdk_client.update_user.assert_called_with(
current_password=current_pass, password=new_pass
user=user_id, current_password=current_pass, password=new_pass
)
self.assertIsNone(result)

@@ -1733,7 +1808,10 @@ def test_user_password_change_no_prompt(self):

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
conn = self.app.client_manager.sdk_connection
user_id = conn.config.get_auth().get_user_id(conn.identity)
self.identity_sdk_client.update_user.assert_called_with(
current_password=current_pass, password=new_pass
user=user_id, current_password=current_pass, password=new_pass
)
self.assertIsNone(result)

@@ -1791,2 +1869,3 @@

'password_expires_at',
'options',
)

@@ -1803,2 +1882,3 @@ self.assertEqual(collist, columns)

self.user.password_expires_at,
getattr(self.user, 'options', {}),
)

@@ -1805,0 +1885,0 @@ self.assertEqual(datalist, data)

@@ -57,5 +57,5 @@ # Copyright 2013 Nebula Inc.

self.image_client.create_image = mock.Mock(return_value=self.new_image)
self.image_client.find_image = mock.Mock(return_value=self.new_image)
self.image_client.update_image = mock.Mock(return_image=self.new_image)
self.image_client.create_image.return_value = self.new_image
self.image_client.find_image.return_value = self.new_image
self.image_client.update_image.return_value = self.new_image

@@ -216,4 +216,4 @@ # Get the command object to test

# This is the return value for utils.find_resource()
self.image_client.find_image = mock.Mock(return_value=self._image)
self.image_client.delete_image = mock.Mock(return_value=None)
self.image_client.find_image.return_value = self._image
self.image_client.delete_image.return_value = None

@@ -266,3 +266,2 @@ # Get the command object to test

self.image_client.images = mock.Mock()
self.image_client.images.side_effect = [

@@ -447,4 +446,4 @@ [self._image],

# This is the return value for utils.find_resource()
self.image_client.find_image = mock.Mock(return_value=self._image)
self.image_client.update_image = mock.Mock(return_value=self._image)
self.image_client.find_image.return_value = self._image
self.image_client.update_image.return_value = self._image

@@ -719,3 +718,3 @@ # Get the command object to test

self.image_client.find_image = mock.Mock(return_value=self._image)
self.image_client.find_image.return_value = self._image

@@ -722,0 +721,0 @@ # Get the command object to test

@@ -237,6 +237,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

def test_create_floating_ip_with_qos(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
self.network_client.find_qos_policy = mock.Mock(
return_value=qos_policy
)
qos_policy = network_fakes.create_one_qos_policy()
self.network_client.find_qos_policy.return_value = qos_policy
arglist = [

@@ -420,3 +418,3 @@ '--qos-policy',

)
fake_router = network_fakes.FakeRouter.create_one_router(
fake_router = network_fakes.create_one_router(
{

@@ -506,3 +504,3 @@ 'id': 'fake_router_id',

verifylist = [
('network', 'fake_network_id'),
('networks', ['fake_network_id']),
]

@@ -515,3 +513,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

**{
'floating_network_id': 'fake_network_id',
'floating_network_id': ['fake_network_id'],
}

@@ -528,3 +526,3 @@ )

verifylist = [
('port', 'fake_port_id'),
('ports', ['fake_port_id']),
]

@@ -537,3 +535,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

**{
'port_id': 'fake_port_id',
'port_id': ['fake_port_id'],
}

@@ -671,3 +669,3 @@ )

verifylist = [
('router', 'fake_router_id'),
('routers', ['fake_router_id']),
]

@@ -680,3 +678,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

**{
'router_id': 'fake_router_id',
'router_id': ['fake_router_id'],
}

@@ -904,6 +902,4 @@ )

def test_qos_policy_option(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
self.network_client.find_qos_policy = mock.Mock(
return_value=qos_policy
)
qos_policy = network_fakes.create_one_qos_policy()
self.network_client.find_qos_policy.return_value = qos_policy
arglist = [

@@ -934,6 +930,4 @@ "--qos-policy",

def test_port_and_qos_policy_option(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
self.network_client.find_qos_policy = mock.Mock(
return_value=qos_policy
)
qos_policy = network_fakes.create_one_qos_policy()
self.network_client.find_qos_policy.return_value = qos_policy
arglist = [

@@ -940,0 +934,0 @@ "--qos-policy",

@@ -27,4 +27,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

self.router = network_fakes.FakeRouter.create_one_router()
self.network_client.find_router = mock.Mock(return_value=self.router)
self.router = network_fakes.create_one_router()
self.network_client.find_router.return_value = self.router

@@ -31,0 +31,0 @@

@@ -33,5 +33,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

self.router = network_fakes.FakeRouter.create_one_router(
{'id': 'fake-router-id'}
)
self.router = network_fakes.create_one_router({'id': 'fake-router-id'})
self.network_client.find_router = mock.Mock(return_value=self.router)

@@ -38,0 +36,0 @@ self.port = network_fakes.create_one_port()

@@ -73,3 +73,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

class TestAddRouterAgent(TestNetworkAgent):
_router = network_fakes.FakeRouter.create_one_router()
_router = network_fakes.create_one_router()
_agent = network_fakes.create_one_network_agent()

@@ -223,27 +223,21 @@

super().setUp()
self.network_client.agents = mock.Mock(
return_value=self.network_agents
self.network_client.agents.return_value = self.network_agents
self.network_client.routers_hosting_l3_agents.return_value = (
self.network_agents
)
_testagent = network_fakes.create_one_network_agent()
self.network_client.get_agent = mock.Mock(return_value=_testagent)
self.network_client.get_agent.return_value = _testagent
self.network_client.get_agent.return_value = _testagent
self._testnetwork = network_fakes.create_one_network()
self.network_client.find_network = mock.Mock(
return_value=self._testnetwork
self.network_client.find_network.return_value = self._testnetwork
self.network_client.network_hosting_dhcp_agents.return_value = (
self.network_agents
)
self.network_client.network_hosting_dhcp_agents = mock.Mock(
return_value=self.network_agents
)
self.network_client.get_agent = mock.Mock(return_value=_testagent)
self._testrouter = network_fakes.create_one_router()
self.network_client.find_router.return_value = self._testrouter
self._testrouter = network_fakes.FakeRouter.create_one_router()
self.network_client.find_router = mock.Mock(
return_value=self._testrouter
)
self.network_client.routers_hosting_l3_agents = mock.Mock(
return_value=self.network_agents
)
# Get the command object to test

@@ -328,6 +322,2 @@ self.cmd = network_agent.ListNetworkAgent(self.app, None)

attrs = {
self._testrouter,
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)

@@ -337,3 +327,3 @@ columns, data = self.cmd.take_action(parsed_args)

self.network_client.routers_hosting_l3_agents.assert_called_once_with(
*attrs
self._testrouter
)

@@ -352,6 +342,2 @@

attrs = {
self._testrouter,
}
parsed_args = self.check_parser(self.cmd, arglist, verifylist)

@@ -361,3 +347,3 @@ columns, data = self.cmd.take_action(parsed_args)

self.network_client.routers_hosting_l3_agents.assert_called_once_with(
*attrs
self._testrouter
)

@@ -431,3 +417,3 @@

class TestRemoveRouterAgent(TestNetworkAgent):
_router = network_fakes.FakeRouter.create_one_router()
_router = network_fakes.create_one_router()
_agent = network_fakes.create_one_network_agent()

@@ -434,0 +420,0 @@

@@ -38,7 +38,6 @@ # Copyright (c) 2016, Intel Corporation.

# The new qos policy created.
new_qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy(
attrs={
'project_id': project.id,
}
new_qos_policy = network_fakes.create_one_qos_policy(
attrs={'project_id': project.id}
)
columns = (

@@ -52,2 +51,3 @@ 'description',

'shared',
'tags',
)

@@ -62,3 +62,4 @@

new_qos_policy.rules,
new_qos_policy.shared,
new_qos_policy.is_shared,
new_qos_policy.tags,
)

@@ -164,5 +165,3 @@

# The address scope to delete.
_qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies(
count=2
)
_qos_policies = network_fakes.create_qos_policies(count=2)

@@ -172,6 +171,4 @@ def setUp(self):

self.network_client.delete_qos_policy = mock.Mock(return_value=None)
self.network_client.find_qos_policy = (
network_fakes.FakeNetworkQosPolicy.get_qos_policies(
qos_policies=self._qos_policies
)
self.network_client.find_qos_policy = network_fakes.get_qos_policies(
qos_policies=self._qos_policies
)

@@ -253,5 +250,3 @@

# The QoS policies to list up.
qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies(
count=3
)
qos_policies = network_fakes.create_qos_policies(count=3)
columns = (

@@ -270,3 +265,3 @@ 'ID',

qos_policy.name,
qos_policy.shared,
qos_policy.is_shared,
qos_policy.is_default,

@@ -355,3 +350,3 @@ qos_policy.project_id,

# The QoS policy to set.
_qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
_qos_policy = network_fakes.create_one_qos_policy()

@@ -439,3 +434,3 @@ def setUp(self):

# The QoS policy to show.
_qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
_qos_policy = network_fakes.create_one_qos_policy()
columns = (

@@ -449,2 +444,3 @@ 'description',

'shared',
'tags',
)

@@ -458,3 +454,4 @@ data = (

network_qos_policy.RulesColumn(_qos_policy.rules),
_qos_policy.shared,
_qos_policy.is_shared,
_qos_policy.tags,
)

@@ -461,0 +458,0 @@

@@ -31,6 +31,5 @@ # Copyright (c) 2016, Intel Corporation.

# The QoS policies to show.
qos_rule_type = (
network_fakes.FakeNetworkQosRuleType.create_one_qos_rule_type(attrs)
)
qos_rule_type = network_fakes.create_one_qos_rule_type(attrs)
columns = ('drivers', 'rule_type_name')
columns = ('drivers', 'rule_type_name')
data = [qos_rule_type.drivers, qos_rule_type.type]

@@ -80,5 +79,4 @@

# The QoS policies to list up.
qos_rule_types = (
network_fakes.FakeNetworkQosRuleType.create_qos_rule_types(count=3)
)
qos_rule_types = network_fakes.create_qos_rule_types(count=3)
columns = ('Type',)

@@ -85,0 +83,0 @@ data = []

@@ -17,2 +17,3 @@ # Copyright (c) 2016, Intel Corporation.

from unittest import mock
import uuid

@@ -58,5 +59,3 @@ from osc_lib import exceptions

super().setUp()
self.qos_policy = (
network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
)
self.qos_policy = network_fakes.create_one_qos_policy()
self.network_client.find_qos_policy = mock.Mock(

@@ -77,5 +76,3 @@ return_value=self.qos_policy

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.columns = (

@@ -86,3 +83,2 @@ 'direction',

'project_id',
'qos_policy_id',
'type',

@@ -96,3 +92,2 @@ )

self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -187,5 +182,3 @@ )

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.columns = (

@@ -196,3 +189,2 @@ 'direction',

'project_id',
'qos_policy_id',
'type',

@@ -206,3 +198,2 @@ )

self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -297,5 +288,3 @@ )

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.columns = (

@@ -305,3 +294,2 @@ 'dscp_mark',

'project_id',
'qos_policy_id',
'type',

@@ -314,3 +302,2 @@ )

self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -398,12 +385,9 @@ )

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.columns = (
'direction',
'id',
'max_burst_kbits',
'max_burst_kbps',
'max_kbps',
'project_id',
'qos_policy_id',
'type',

@@ -415,6 +399,5 @@ )

self.new_rule.id,
self.new_rule.max_burst_kbits,
self.new_rule.max_burst_kbps,
self.new_rule.max_kbps,
self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -459,3 +442,3 @@ )

rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
rule = network_fakes.create_one_qos_rule(
{

@@ -466,10 +449,9 @@ 'qos_policy_id': self.qos_policy.id,

)
rule.max_burst_kbits = 0
rule.max_burst_kbps = 0
expected_data = (
rule.direction,
rule.id,
rule.max_burst_kbits,
rule.max_burst_kbps,
rule.max_kbps,
rule.project_id,
rule.qos_policy_id,
rule.type,

@@ -503,3 +485,3 @@ )

'--max-burst-kbits',
str(self.new_rule.max_burst_kbits),
str(self.new_rule.max_burst_kbps),
'--egress',

@@ -512,3 +494,3 @@ self.new_rule.qos_policy_id,

('max_kbps', self.new_rule.max_kbps),
('max_burst_kbits', self.new_rule.max_burst_kbits),
('max_burst_kbits', self.new_rule.max_burst_kbps),
('egress', True),

@@ -525,3 +507,3 @@ ('qos_policy', self.new_rule.qos_policy_id),

'max_kbps': self.new_rule.max_kbps,
'max_burst_kbps': self.new_rule.max_burst_kbits,
'max_burst_kbps': self.new_rule.max_burst_kbps,
'direction': self.new_rule.direction,

@@ -566,5 +548,3 @@ },

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -575,5 +555,3 @@ self.network_client.delete_qos_minimum_bandwidth_rule = mock.Mock(

self.network_client.find_qos_minimum_bandwidth_rule = (
network_fakes.FakeNetworkQosRule.get_qos_rules(
qos_rules=self.new_rule
)
network_fakes.get_qos_rules(qos_rules=self.new_rule)
)

@@ -635,5 +613,3 @@

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -644,5 +620,3 @@ self.network_client.delete_qos_minimum_packet_rate_rule = mock.Mock(

self.network_client.find_qos_minimum_packet_rate_rule = (
network_fakes.FakeNetworkQosRule.get_qos_rules(
qos_rules=self.new_rule
)
network_fakes.get_qos_rules(qos_rules=self.new_rule)
)

@@ -704,5 +678,3 @@

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -713,5 +685,3 @@ self.network_client.delete_qos_dscp_marking_rule = mock.Mock(

self.network_client.find_qos_dscp_marking_rule = (
network_fakes.FakeNetworkQosRule.get_qos_rules(
qos_rules=self.new_rule
)
network_fakes.get_qos_rules(qos_rules=self.new_rule)
)

@@ -773,5 +743,3 @@

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -782,5 +750,3 @@ self.network_client.delete_qos_bandwidth_limit_rule = mock.Mock(

self.network_client.find_qos_bandwidth_limit_rule = (
network_fakes.FakeNetworkQosRule.get_qos_rules(
qos_rules=self.new_rule
)
network_fakes.get_qos_rules(qos_rules=self.new_rule)
)

@@ -842,5 +808,3 @@

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs=attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -947,5 +911,3 @@ self.network_client.update_qos_minimum_bandwidth_rule = mock.Mock(

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs=attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -1052,5 +1014,3 @@ self.network_client.update_qos_minimum_packet_rate_rule = mock.Mock(

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs=attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -1156,17 +1116,10 @@ self.network_client.update_qos_dscp_marking_rule = mock.Mock(

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs=attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]
self.network_client.update_qos_bandwidth_limit_rule = mock.Mock(
return_value=None
self.network_client.update_qos_bandwidth_limit_rule.return_value = None
self.network_client.find_qos_bandwidth_limit_rule.return_value = (
self.new_rule
)
self.network_client.find_qos_bandwidth_limit_rule = mock.Mock(
return_value=self.new_rule
)
self.network_client.find_qos_policy = mock.Mock(
return_value=self.qos_policy
)
self.network_client.find_qos_policy.return_value = self.qos_policy
# Get the command object to test
self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None)

@@ -1236,3 +1189,3 @@

def _reset_max_burst_kbits(self, max_burst_kbits):
self.new_rule.max_burst_kbits = max_burst_kbits
self.new_rule.max_burst_kbps = max_burst_kbits

@@ -1242,9 +1195,9 @@ def _set_max_burst_kbits(self, max_burst_kbits=None):

self.addCleanup(
self._reset_max_burst_kbits, self.new_rule.max_burst_kbits
self._reset_max_burst_kbits, self.new_rule.max_burst_kbps
)
self.new_rule.max_burst_kbits = max_burst_kbits
self.new_rule.max_burst_kbps = max_burst_kbits
arglist = [
'--max-burst-kbits',
str(self.new_rule.max_burst_kbits),
str(self.new_rule.max_burst_kbps),
self.new_rule.qos_policy_id,

@@ -1254,3 +1207,3 @@ self.new_rule.id,

verifylist = [
('max_burst_kbits', self.new_rule.max_burst_kbits),
('max_burst_kbits', self.new_rule.max_burst_kbps),
('qos_policy', self.new_rule.qos_policy_id),

@@ -1264,3 +1217,3 @@ ('id', self.new_rule.id),

attrs = {
'max_burst_kbps': self.new_rule.max_burst_kbits,
'max_burst_kbps': self.new_rule.max_burst_kbps,
}

@@ -1334,39 +1287,32 @@ self.network_client.update_qos_bandwidth_limit_rule.assert_called_with(

super().setUp()
attrs = {
'qos_policy_id': self.qos_policy.id,
'type': RULE_TYPE_MINIMUM_BANDWIDTH,
}
self.new_rule_min_bw = (
network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs)
)
attrs['type'] = RULE_TYPE_MINIMUM_PACKET_RATE
self.new_rule_min_pps = (
network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs)
)
attrs['type'] = RULE_TYPE_DSCP_MARKING
self.new_rule_dscp_mark = (
network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs)
)
attrs['type'] = RULE_TYPE_BANDWIDTH_LIMIT
self.new_rule_max_bw = (
network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs)
)
self.qos_policy.rules = [
self.new_rule_min_bw,
self.new_rule_min_pps,
self.new_rule_dscp_mark,
self.new_rule_max_bw,
{
'max_kbps': 1024,
'max_burst_kbps': 1024,
'direction': 'egress',
'id': 'qos-rule-id-' + uuid.uuid4().hex,
'qos_policy_id': self.qos_policy.id,
'type': 'bandwidth_limit',
},
{
'dscp_mark': 0,
'id': 'qos-rule-id-' + uuid.uuid4().hex,
'qos_policy_id': self.qos_policy.id,
'type': 'dscp_marking',
},
{
'min_kbps': 1024,
'direction': 'egress',
'id': 'qos-rule-id-' + uuid.uuid4().hex,
'qos_policy_id': self.qos_policy.id,
'type': 'minimum_bandwidth',
},
{
'min_kpps': 2800,
'direction': 'egress',
'id': 'qos-rule-id-' + uuid.uuid4().hex,
'qos_policy_id': self.qos_policy.id,
'type': 'minimum_packet_rate',
},
]
self.network_client.find_qos_minimum_bandwidth_rule = mock.Mock(
return_value=self.new_rule_min_bw
)
self.network_client.find_qos_minimum_packet_rate_rule = mock.Mock(
return_value=self.new_rule_min_pps
)
self.network_client.find_qos_dscp_marking_rule = mock.Mock(
return_value=self.new_rule_dscp_mark
)
self.network_client.find_qos_bandwidth_limit_rule = mock.Mock(
return_value=self.new_rule_max_bw
)
self.columns = (

@@ -1387,16 +1333,13 @@ 'ID',

(
self.qos_policy.rules[index].id,
self.qos_policy.rules[index].qos_policy_id,
self.qos_policy.rules[index].type,
getattr(self.qos_policy.rules[index], 'max_kbps', ''),
getattr(
self.qos_policy.rules[index], 'max_burst_kbps', ''
),
getattr(self.qos_policy.rules[index], 'min_kbps', ''),
getattr(self.qos_policy.rules[index], 'min_kpps', ''),
getattr(self.qos_policy.rules[index], 'dscp_mark', ''),
getattr(self.qos_policy.rules[index], 'direction', ''),
self.qos_policy.rules[index]['id'],
self.qos_policy.id,
self.qos_policy.rules[index]['type'],
self.qos_policy.rules[index].get('max_kbps', ''),
self.qos_policy.rules[index].get('max_burst_kbps', ''),
self.qos_policy.rules[index].get('min_kbps', ''),
self.qos_policy.rules[index].get('min_kpps', ''),
self.qos_policy.rules[index].get('dscp_mark', ''),
self.qos_policy.rules[index].get('direction', ''),
)
)
# Get the command object to test
self.cmd = network_qos_rule.ListNetworkQosRule(self.app, None)

@@ -1430,5 +1373,3 @@

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -1440,3 +1381,2 @@ self.columns = (

'project_id',
'qos_policy_id',
'type',

@@ -1449,3 +1389,2 @@ )

self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -1501,5 +1440,3 @@ )

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -1511,3 +1448,2 @@ self.columns = (

'project_id',
'qos_policy_id',
'type',

@@ -1520,3 +1456,2 @@ )

self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -1572,5 +1507,3 @@ )

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -1581,3 +1514,2 @@ self.columns = (

'project_id',
'qos_policy_id',
'type',

@@ -1589,3 +1521,2 @@ )

self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -1641,5 +1572,3 @@ )

}
self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
attrs
)
self.new_rule = network_fakes.create_one_qos_rule(attrs)
self.qos_policy.rules = [self.new_rule]

@@ -1649,6 +1578,5 @@ self.columns = (

'id',
'max_burst_kbits',
'max_burst_kbps',
'max_kbps',
'project_id',
'qos_policy_id',
'type',

@@ -1659,6 +1587,5 @@ )

self.new_rule.id,
self.new_rule.max_burst_kbits,
self.new_rule.max_burst_kbps,
self.new_rule.max_kbps,
self.new_rule.project_id,
self.new_rule.qos_policy_id,
self.new_rule.type,

@@ -1665,0 +1592,0 @@ )

@@ -37,4 +37,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

network_object = network_fakes.create_one_network()
qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group()
qos_object = network_fakes.create_one_qos_policy()
sg_object = network_fakes.create_one_security_group()
as_object = network_fakes.create_one_address_scope()

@@ -41,0 +41,0 @@ snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool()

@@ -50,3 +50,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

)
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy(
qos_policy = network_fakes.create_one_qos_policy(
attrs={'id': _network.qos_policy_id}

@@ -626,3 +626,3 @@ )

self._agent = network_fakes.create_one_network_agent()
self.network_client.get_agent = mock.Mock(return_value=self._agent)
self.network_client.get_agent.return_value = self._agent

@@ -972,3 +972,3 @@ self.network_client.dhcp_agent_hosting_networks = mock.Mock(

_network = network_fakes.create_one_network({'tags': ['green', 'red']})
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy(
qos_policy = network_fakes.create_one_qos_policy(
attrs={'id': _network.qos_policy_id}

@@ -1272,3 +1272,3 @@ )

_network = network_fakes.create_one_network({'tags': ['green', 'red']})
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy(
qos_policy = network_fakes.create_one_qos_policy(
attrs={'id': _network.qos_policy_id}

@@ -1275,0 +1275,0 @@ )

@@ -39,7 +39,6 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

# The security group to be created.
_security_group = (
network_fakes.FakeSecurityGroup.create_one_security_group()
)
_security_group = network_fakes.create_one_security_group()
columns = (
'created_at',
'description',

@@ -49,8 +48,11 @@ 'id',

'project_id',
'revision_number',
'rules',
'stateful',
'tags',
'updated_at',
)
data = (
_security_group.created_at,
_security_group.description,

@@ -60,5 +62,7 @@ _security_group.id,

_security_group.project_id,
_security_group.revision_number,
security_group.NetworkSecurityGroupRulesColumn([]),
_security_group.stateful,
_security_group.tags,
_security_group.updated_at,
)

@@ -169,3 +173,3 @@

self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
self.assertEqual(self.data, data)

@@ -181,3 +185,3 @@ def test_create_with_tags(self):

# The security groups to be deleted.
_security_groups = network_fakes.FakeSecurityGroup.create_security_groups()
_security_groups = network_fakes.create_security_groups()

@@ -192,5 +196,3 @@ def setUp(self):

self.network_client.find_security_group = (
network_fakes.FakeSecurityGroup.get_security_groups(
self._security_groups
)
network_fakes.get_security_groups(self._security_groups)
)

@@ -273,5 +275,3 @@

# The security group to be listed.
_security_groups = network_fakes.FakeSecurityGroup.create_security_groups(
count=3
)
_security_groups = network_fakes.create_security_groups(count=3)

@@ -422,6 +422,4 @@ columns = (

# The security group to be set.
_security_group = (
network_fakes.FakeSecurityGroup.create_one_security_group(
attrs={'tags': ['green', 'red']}
)
_security_group = network_fakes.create_one_security_group(
attrs={'tags': ['green', 'red']}
)

@@ -526,14 +524,11 @@

# The security group rule to be shown with the group.
_security_group_rule = (
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
)
_security_group_rule = network_fakes.create_one_security_group_rule()
# The security group to be shown.
_security_group = (
network_fakes.FakeSecurityGroup.create_one_security_group(
attrs={'security_group_rules': [_security_group_rule._info]}
)
_security_group = network_fakes.create_one_security_group(
attrs={'security_group_rules': [dict(_security_group_rule)]}
)
columns = (
'created_at',
'description',

@@ -543,8 +538,11 @@ 'id',

'project_id',
'revision_number',
'rules',
'stateful',
'tags',
'updated_at',
)
data = (
_security_group.created_at,
_security_group.description,

@@ -554,7 +552,9 @@ _security_group.id,

_security_group.project_id,
_security_group.revision_number,
security_group.NetworkSecurityGroupRulesColumn(
[_security_group_rule._info]
[dict(_security_group_rule)]
),
_security_group.stateful,
_security_group.tags,
_security_group.updated_at,
)

@@ -597,6 +597,4 @@

# The security group to be unset.
_security_group = (
network_fakes.FakeSecurityGroup.create_one_security_group(
attrs={'tags': ['green', 'red']}
)
_security_group = network_fakes.create_one_security_group(
attrs={'tags': ['green', 'red']}
)

@@ -603,0 +601,0 @@

@@ -386,3 +386,2 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

'ip_protocol': 'tcp',
'ethertype': 'IPv4',
'from_port': 80,

@@ -396,3 +395,2 @@ 'to_port': 80,

'ip_protocol': 'icmp',
'ethertype': 'IPv4',
'from_port': -1,

@@ -431,3 +429,3 @@ 'to_port': -1,

rule['ip_protocol'],
rule['ethertype'],
'', # ethertype is a neutron-only thing
rule['ip_range'],

@@ -434,0 +432,0 @@ rule['port_range'],

@@ -43,5 +43,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

# The security group that will contain the rule created.
_security_group = (
network_fakes.FakeSecurityGroup.create_one_security_group()
)
_security_group = network_fakes.create_one_security_group()

@@ -52,2 +50,3 @@ # The address group to be used in security group rules

expected_columns = (
'created_at',
'description',

@@ -64,3 +63,5 @@ 'direction',

'remote_ip_prefix',
'revision_number',
'security_group_id',
'updated_at',
)

@@ -72,5 +73,3 @@

self._security_group_rule = (
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
attrs
)
network_fakes.create_one_security_group_rule(attrs)
)

@@ -81,2 +80,3 @@ self.network_client.create_security_group_rule = mock.Mock(

self.expected_data = (
self._security_group_rule.created_at,
self._security_group_rule.description,

@@ -93,3 +93,5 @@ self._security_group_rule.direction,

self._security_group_rule.remote_ip_prefix,
self._security_group_rule.revision_number,
self._security_group_rule.security_group_id,
self._security_group_rule.updated_at,
)

@@ -972,7 +974,3 @@

# The security group rules to be deleted.
_security_group_rules = (
network_fakes.FakeSecurityGroupRule.create_security_group_rules(
count=2
)
)
_security_group_rules = network_fakes.create_security_group_rules(count=2)

@@ -987,5 +985,3 @@ def setUp(self):

self.network_client.find_security_group_rule = (
network_fakes.FakeSecurityGroupRule.get_security_group_rules(
self._security_group_rules
)
network_fakes.get_security_group_rules(self._security_group_rules)
)

@@ -1068,29 +1064,23 @@

# The security group to hold the rules.
_security_group = (
network_fakes.FakeSecurityGroup.create_one_security_group()
)
_security_group = network_fakes.create_one_security_group()
# The security group rule to be listed.
_security_group_rule_tcp = (
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
{
'protocol': 'tcp',
'port_range_max': 80,
'port_range_min': 80,
'security_group_id': _security_group.id,
}
)
_security_group_rule_tcp = network_fakes.create_one_security_group_rule(
{
'protocol': 'tcp',
'port_range_max': 80,
'port_range_min': 80,
'security_group_id': _security_group.id,
}
)
_security_group_rule_icmp = (
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
{
'protocol': 'icmp',
'remote_ip_prefix': '10.0.2.0/24',
'security_group_id': _security_group.id,
}
)
_security_group_rule_icmp = network_fakes.create_one_security_group_rule(
{
'protocol': 'icmp',
'remote_ip_prefix': '10.0.2.0/24',
'security_group_id': _security_group.id,
}
)
_security_group.security_group_rules = [
_security_group_rule_tcp._info,
_security_group_rule_icmp._info,
dict(_security_group_rule_tcp),
dict(_security_group_rule_icmp),
]

@@ -1276,7 +1266,6 @@ _security_group_rules = [

# The security group rule to be shown.
_security_group_rule = (
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
)
_security_group_rule = network_fakes.create_one_security_group_rule()
columns = (
'created_at',
'description',

@@ -1293,6 +1282,9 @@ 'direction',

'remote_ip_prefix',
'revision_number',
'security_group_id',
'updated_at',
)
data = (
_security_group_rule.created_at,
_security_group_rule.description,

@@ -1309,3 +1301,5 @@ _security_group_rule.direction,

_security_group_rule.remote_ip_prefix,
_security_group_rule.revision_number,
_security_group_rule.security_group_id,
_security_group_rule.updated_at,
)

@@ -1312,0 +1306,0 @@

@@ -17,3 +17,2 @@ #

import random
import typing as ty
from unittest import mock

@@ -96,3 +95,3 @@ import uuid

def set_volume_api_version(self, version: ty.Optional[str] = None):
def set_volume_api_version(self, version: str | None = None):
"""Set a fake block storage API version.

@@ -99,0 +98,0 @@

@@ -15,2 +15,6 @@ #

from unittest import mock
from openstack.block_storage.v2 import service as _service
from openstack.test import fakes as sdk_fakes
from osc_lib import exceptions

@@ -22,21 +26,9 @@

class TestService(volume_fakes.TestVolume):
class TestServiceList(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
# Get a shortcut to the ServiceManager Mock
self.service_mock = self.volume_client.services
self.service_mock.reset_mock()
self.service = sdk_fakes.generate_fake_resource(_service.Service)
self.volume_sdk_client.services.return_value = [self.service]
class TestServiceList(TestService):
# The service to be listed
services = volume_fakes.create_one_service()
def setUp(self):
super().setUp()
self.service_mock.list.return_value = [self.services]
# Get the command object to test
self.cmd = service.ListService(self.app, None)

@@ -47,18 +39,15 @@

'--host',
self.services.host,
self.service.host,
'--service',
self.services.binary,
self.service.binary,
]
verifylist = [
('host', self.services.host),
('service', self.services.binary),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
expected_columns = [
expected_columns = (
'Binary',

@@ -70,42 +59,31 @@ 'Host',

'Updated At',
]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
)
datalist = (
(
self.services.binary,
self.services.host,
self.services.zone,
self.services.status,
self.services.state,
self.services.updated_at,
self.service.binary,
self.service.host,
self.service.availability_zone,
self.service.status,
self.service.state,
self.service.updated_at,
),
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
# checking if proper call was made to list services
self.service_mock.list.assert_called_with(
self.services.host,
self.services.binary,
self.volume_sdk_client.services.assert_called_with(
host=self.service.host,
binary=self.service.binary,
)
# checking if prohibited columns are present in output
self.assertNotIn("Disabled Reason", columns)
self.assertNotIn(self.services.disabled_reason, tuple(data))
def test_service_list_with_long_option(self):
arglist = [
'--host',
self.services.host,
self.service.host,
'--service',
self.services.binary,
self.service.binary,
'--long',
]
verifylist = [
('host', self.services.host),
('service', self.services.binary),
('host', self.service.host),
('service', self.service.binary),
('long', True),

@@ -115,8 +93,5 @@ ]

# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
expected_columns = [
expected_columns = (
'Binary',

@@ -129,37 +104,30 @@ 'Host',

'Disabled Reason',
]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
)
datalist = (
(
self.services.binary,
self.services.host,
self.services.zone,
self.services.status,
self.services.state,
self.services.updated_at,
self.services.disabled_reason,
self.service.binary,
self.service.host,
self.service.availability_zone,
self.service.status,
self.service.state,
self.service.updated_at,
self.service.disabled_reason,
),
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
self.service_mock.list.assert_called_with(
self.services.host,
self.services.binary,
self.volume_sdk_client.services.assert_called_with(
host=self.service.host,
binary=self.service.binary,
)
class TestServiceSet(TestService):
service = volume_fakes.create_one_service()
class TestServiceSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.service_mock.enable.return_value = self.service
self.service_mock.disable.return_value = self.service
self.service_mock.disable_log_reason.return_value = self.service
self.service = sdk_fakes.generate_fake_resource(_service.Service)
self.service.enable = mock.Mock(autospec=True)
self.service.disable = mock.Mock(autospec=True)
self.volume_sdk_client.find_service.return_value = self.service

@@ -180,5 +148,4 @@ self.cmd = service.SetService(self.app, None)

self.service_mock.enable.assert_not_called()
self.service_mock.disable.assert_not_called()
self.service_mock.disable_log_reason.assert_not_called()
self.service.enable.assert_not_called()
self.service.disable.assert_not_called()
self.assertIsNone(result)

@@ -201,7 +168,4 @@

self.service_mock.enable.assert_called_with(
self.service.host, self.service.binary
)
self.service_mock.disable.assert_not_called()
self.service_mock.disable_log_reason.assert_not_called()
self.service.enable.assert_called_with(self.volume_sdk_client)
self.service.disable.assert_not_called()
self.assertIsNone(result)

@@ -224,7 +188,6 @@

self.service_mock.disable.assert_called_with(
self.service.host, self.service.binary
self.service.enable.assert_not_called()
self.service.disable.assert_called_with(
self.volume_sdk_client, reason=None
)
self.service_mock.enable.assert_not_called()
self.service_mock.disable_log_reason.assert_not_called()
self.assertIsNone(result)

@@ -251,4 +214,5 @@

self.service_mock.disable_log_reason.assert_called_with(
self.service.host, self.service.binary, reason
self.service.enable.assert_not_called()
self.service.disable.assert_called_with(
self.volume_sdk_client, reason=reason
)

@@ -271,2 +235,3 @@ self.assertIsNone(result)

parsed_args = self.check_parser(self.cmd, arglist, verifylist)
try:

@@ -298,2 +263,3 @@ self.cmd.take_action(parsed_args)

parsed_args = self.check_parser(self.cmd, arglist, verifylist)
try:

@@ -300,0 +266,0 @@ self.cmd.take_action(parsed_args)

@@ -16,2 +16,7 @@ #

from openstack.block_storage.v2 import backup as _backup
from openstack.block_storage.v2 import snapshot as _snapshot
from openstack.block_storage.v2 import volume as _volume
from openstack import exceptions as sdk_exceptions
from openstack.test import fakes as sdk_fakes
from osc_lib import exceptions

@@ -23,23 +28,3 @@

class TestBackupLegacy(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.backups_mock = self.volume_client.backups
self.backups_mock.reset_mock()
self.volumes_mock = self.volume_client.volumes
self.volumes_mock.reset_mock()
self.snapshots_mock = self.volume_client.volume_snapshots
self.snapshots_mock.reset_mock()
self.restores_mock = self.volume_client.restores
self.restores_mock.reset_mock()
class TestBackupCreate(volume_fakes.TestVolume):
volume = volume_fakes.create_one_volume()
snapshot = volume_fakes.create_one_snapshot()
new_backup = volume_fakes.create_one_backup(
attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id}
)
columns = (

@@ -50,7 +35,2 @@ 'id',

)
data = (
new_backup.id,
new_backup.name,
new_backup.volume_id,
)

@@ -60,7 +40,19 @@ def setUp(self):

self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.volume_sdk_client.create_backup.return_value = self.new_backup
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup,
volume_id=self.volume.id,
snapshot_id=self.snapshot.id,
)
self.volume_sdk_client.create_backup.return_value = self.backup
# Get the command object to test
self.data = (
self.backup.id,
self.backup.name,
self.backup.volume_id,
)
self.cmd = volume_backup.CreateVolumeBackup(self.app, None)

@@ -71,21 +63,21 @@

"--name",
self.new_backup.name,
self.backup.name,
"--description",
self.new_backup.description,
self.backup.description,
"--container",
self.new_backup.container,
self.backup.container,
"--force",
"--incremental",
"--snapshot",
self.new_backup.snapshot_id,
self.new_backup.volume_id,
self.backup.snapshot_id,
self.backup.volume_id,
]
verifylist = [
("name", self.new_backup.name),
("description", self.new_backup.description),
("container", self.new_backup.container),
("name", self.backup.name),
("description", self.backup.description),
("container", self.backup.container),
("force", True),
("incremental", True),
("snapshot", self.new_backup.snapshot_id),
("volume", self.new_backup.volume_id),
("snapshot", self.backup.snapshot_id),
("volume", self.backup.volume_id),
]

@@ -97,9 +89,9 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volume_sdk_client.create_backup.assert_called_with(
volume_id=self.new_backup.volume_id,
container=self.new_backup.container,
name=self.new_backup.name,
description=self.new_backup.description,
volume_id=self.backup.volume_id,
container=self.backup.container,
name=self.backup.name,
description=self.backup.description,
force=True,
is_incremental=True,
snapshot_id=self.new_backup.snapshot_id,
snapshot_id=self.backup.snapshot_id,
)

@@ -112,11 +104,11 @@ self.assertEqual(self.columns, columns)

"--description",
self.new_backup.description,
self.backup.description,
"--container",
self.new_backup.container,
self.new_backup.volume_id,
self.backup.container,
self.backup.volume_id,
]
verifylist = [
("description", self.new_backup.description),
("container", self.new_backup.container),
("volume", self.new_backup.volume_id),
("description", self.backup.description),
("container", self.backup.container),
("volume", self.backup.volume_id),
]

@@ -128,6 +120,6 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volume_sdk_client.create_backup.assert_called_with(
volume_id=self.new_backup.volume_id,
container=self.new_backup.container,
volume_id=self.backup.volume_id,
container=self.backup.container,
name=None,
description=self.new_backup.description,
description=self.backup.description,
force=False,

@@ -141,13 +133,9 @@ is_incremental=False,

class TestBackupDelete(volume_fakes.TestVolume):
backups = volume_fakes.create_backups(count=2)
def setUp(self):
super().setUp()
self.volume_sdk_client.find_backup = volume_fakes.get_backups(
self.backups
)
self.backups = list(sdk_fakes.generate_fake_resources(_backup.Backup))
self.volume_sdk_client.find_backup.side_effect = self.backups
self.volume_sdk_client.delete_backup.return_value = None
# Get the command object to mock
self.cmd = volume_backup.DeleteVolumeBackup(self.app, None)

@@ -235,7 +223,2 @@

class TestBackupList(volume_fakes.TestVolume):
volume = volume_fakes.create_one_volume()
backups = volume_fakes.create_backups(
attrs={'volume_id': volume.name}, count=3
)
columns = (

@@ -256,41 +239,47 @@ 'ID',

data = []
for b in backups:
data.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
)
)
data_long = []
for b in backups:
data_long.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
b.availability_zone,
volume_backup.VolumeIdColumn(b.volume_id),
b.container,
)
)
def setUp(self):
super().setUp()
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.volumes.return_value = [self.volume]
self.backups = list(
sdk_fakes.generate_fake_resources(
_backup.Backup,
attrs={'volume_id': self.volume.id},
)
)
self.volume_sdk_client.backups.return_value = self.backups
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.find_backup.return_value = self.backups[0]
# Get the command to test
self.data = []
for b in self.backups:
self.data.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
)
)
self.data_long = []
for b in self.backups:
self.data_long.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
b.availability_zone,
volume_backup.VolumeIdColumn(b.volume_id),
b.container,
)
)
self.cmd = volume_backup.ListVolumeBackup(self.app, None)

@@ -373,7 +362,2 @@

class TestBackupRestore(volume_fakes.TestVolume):
volume = volume_fakes.create_one_volume()
backup = volume_fakes.create_one_backup(
attrs={'volume_id': volume.id},
)
columns = (

@@ -385,13 +369,11 @@ "id",

data = (
backup.id,
volume.id,
volume.name,
)
def setUp(self):
super().setUp()
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup, volume_id=self.volume.id
)
self.volume_sdk_client.find_backup.return_value = self.backup
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.restore_backup.return_value = {

@@ -403,3 +385,8 @@ 'id': self.backup['id'],

# Get the command object to mock
self.data = (
self.backup.id,
self.volume.id,
self.volume.name,
)
self.cmd = volume_backup.RestoreVolumeBackup(self.app, None)

@@ -493,13 +480,11 @@

class TestBackupSet(TestBackupLegacy):
backup = volume_fakes.create_one_backup(
attrs={'metadata': {'wow': 'cool'}},
)
class TestBackupSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.backups_mock.get.return_value = self.backup
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup, metadata={'wow': 'cool'}
)
self.volume_sdk_client.find_backup.return_value = self.backup
# Get the command object to test
self.cmd = volume_backup.SetVolumeBackup(self.app, None)

@@ -514,9 +499,16 @@

result = self.cmd.take_action(parsed_args)
self.backups_mock.reset_state.assert_called_once_with(
self.backup.id, 'error'
)
self.assertIsNone(result)
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.volume_sdk_client.reset_backup_status.assert_called_with(
self.backup, status='error'
)
def test_backup_set_state_failed(self):
self.backups_mock.reset_state.side_effect = exceptions.CommandError()
self.volume_sdk_client.reset_backup_status.side_effect = (
sdk_exceptions.NotFoundException('foo')
)
arglist = ['--state', 'error', self.backup.id]

@@ -526,17 +518,16 @@ verifylist = [('state', 'error'), ('backup', self.backup.id)]

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(
'One or more of the set operations failed', str(e)
)
self.backups_mock.reset_state.assert_called_with(
self.backup.id, 'error'
exc = self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args
)
self.assertEqual('One or more of the set operations failed', str(exc))
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.volume_sdk_client.reset_backup_status.assert_called_with(
self.backup, status='error'
)
class TestBackupShow(volume_fakes.TestVolume):
backup = volume_fakes.create_one_backup()
columns = (

@@ -560,20 +551,2 @@ "availability_zone",

)
data = (
backup.availability_zone,
backup.container,
backup.created_at,
backup.data_timestamp,
backup.description,
backup.fail_reason,
backup.has_dependent_backups,
backup.id,
backup.is_incremental,
backup.name,
backup.object_count,
backup.size,
backup.snapshot_id,
backup.status,
backup.updated_at,
backup.volume_id,
)

@@ -583,4 +556,24 @@ def setUp(self):

self.backup = sdk_fakes.generate_fake_resource(_backup.Backup)
self.volume_sdk_client.find_backup.return_value = self.backup
# Get the command object to test
self.data = (
self.backup.availability_zone,
self.backup.container,
self.backup.created_at,
self.backup.data_timestamp,
self.backup.description,
self.backup.fail_reason,
self.backup.has_dependent_backups,
self.backup.id,
self.backup.is_incremental,
self.backup.name,
self.backup.object_count,
self.backup.size,
self.backup.snapshot_id,
self.backup.status,
self.backup.updated_at,
self.backup.volume_id,
)
self.cmd = volume_backup.ShowVolumeBackup(self.app, None)

@@ -587,0 +580,0 @@

@@ -16,5 +16,8 @@ #

from openstack.block_storage.v2 import snapshot as _snapshot
from openstack.block_storage.v3 import volume as _volume
from openstack import exceptions as sdk_exceptions
from openstack.test import fakes as sdk_fakes
from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils

@@ -27,15 +30,3 @@ from openstackclient.tests.unit.identity.v3 import fakes as project_fakes

class TestVolumeSnapshot(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshots_mock = self.volume_client.volume_snapshots
self.snapshots_mock.reset_mock()
self.volumes_mock = self.volume_client.volumes
self.volumes_mock.reset_mock()
self.project_mock = self.identity_client.projects
self.project_mock.reset_mock()
class TestVolumeSnapshotCreate(TestVolumeSnapshot):
class TestVolumeSnapshotCreate(volume_fakes.TestVolume):
columns = (

@@ -55,22 +46,21 @@ 'created_at',

self.volume = volume_fakes.create_one_volume()
self.new_snapshot = volume_fakes.create_one_snapshot(
attrs={'volume_id': self.volume.id}
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.snapshot = sdk_fakes.generate_fake_resource(
_snapshot.Snapshot, volume_id=self.volume.id
)
self.volume_sdk_client.create_snapshot.return_value = self.snapshot
self.volume_sdk_client.manage_snapshot.return_value = self.snapshot
self.data = (
self.new_snapshot.created_at,
self.new_snapshot.description,
self.new_snapshot.id,
self.new_snapshot.name,
format_columns.DictColumn(self.new_snapshot.metadata),
self.new_snapshot.size,
self.new_snapshot.status,
self.new_snapshot.volume_id,
self.snapshot.created_at,
self.snapshot.description,
self.snapshot.id,
self.snapshot.name,
format_columns.DictColumn(self.snapshot.metadata),
self.snapshot.size,
self.snapshot.status,
self.snapshot.volume_id,
)
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)

@@ -81,5 +71,5 @@

"--volume",
self.new_snapshot.volume_id,
self.snapshot.volume_id,
"--description",
self.new_snapshot.description,
self.snapshot.description,
"--force",

@@ -90,10 +80,10 @@ '--property',

'Beta=b',
self.new_snapshot.name,
self.snapshot.name,
]
verifylist = [
("volume", self.new_snapshot.volume_id),
("description", self.new_snapshot.description),
("volume", self.snapshot.volume_id),
("description", self.snapshot.description),
("force", True),
('property', {'Alpha': 'a', 'Beta': 'b'}),
("snapshot_name", self.new_snapshot.name),
('properties', {'Alpha': 'a', 'Beta': 'b'}),
("snapshot_name", self.snapshot.name),
]

@@ -104,11 +94,14 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.snapshots_mock.create.assert_called_with(
self.new_snapshot.volume_id,
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.snapshot.volume_id, ignore_missing=False
)
self.volume_sdk_client.create_snapshot.assert_called_with(
volume_id=self.snapshot.volume_id,
force=True,
name=self.new_snapshot.name,
description=self.new_snapshot.description,
name=self.snapshot.name,
description=self.snapshot.description,
metadata={'Alpha': 'a', 'Beta': 'b'},
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)

@@ -118,6 +111,6 @@ def test_snapshot_create_without_name(self):

"--volume",
self.new_snapshot.volume_id,
self.snapshot.volume_id,
]
verifylist = [
("volume", self.new_snapshot.volume_id),
("volume", self.snapshot.volume_id),
]

@@ -135,10 +128,10 @@ self.assertRaises(

"--description",
self.new_snapshot.description,
self.snapshot.description,
"--force",
self.new_snapshot.name,
self.snapshot.name,
]
verifylist = [
("description", self.new_snapshot.description),
("description", self.snapshot.description),
("force", True),
("snapshot_name", self.new_snapshot.name),
("snapshot_name", self.snapshot.name),
]

@@ -149,12 +142,14 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volumes_mock.get.assert_called_once_with(self.new_snapshot.name)
self.snapshots_mock.create.assert_called_once_with(
self.new_snapshot.volume_id,
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.snapshot.name, ignore_missing=False
)
self.volume_sdk_client.create_snapshot.assert_called_once_with(
volume_id=self.snapshot.volume_id,
force=True,
name=self.new_snapshot.name,
description=self.new_snapshot.description,
name=self.snapshot.name,
description=self.snapshot.description,
metadata=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)

@@ -168,4 +163,4 @@ def test_snapshot_create_with_remote_source(self):

'--volume',
self.new_snapshot.volume_id,
self.new_snapshot.name,
self.snapshot.volume_id,
self.snapshot.name,
]

@@ -178,4 +173,4 @@ ref_dict = {

('remote_source', ref_dict),
('volume', self.new_snapshot.volume_id),
("snapshot_name", self.new_snapshot.name),
('volume', self.snapshot.volume_id),
("snapshot_name", self.snapshot.name),
]

@@ -186,24 +181,27 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.snapshots_mock.manage.assert_called_with(
volume_id=self.new_snapshot.volume_id,
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.snapshot.volume_id, ignore_missing=False
)
self.volume_sdk_client.manage_snapshot.assert_called_with(
volume_id=self.snapshot.volume_id,
ref=ref_dict,
name=self.new_snapshot.name,
name=self.snapshot.name,
description=None,
metadata=None,
)
self.snapshots_mock.create.assert_not_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.volume_sdk_client.create_snapshot.assert_not_called()
class TestVolumeSnapshotDelete(TestVolumeSnapshot):
snapshots = volume_fakes.create_snapshots(count=2)
class TestVolumeSnapshotDelete(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshots_mock.get = volume_fakes.get_snapshots(self.snapshots)
self.snapshots_mock.delete.return_value = None
self.snapshots = list(
sdk_fakes.generate_fake_resources(_snapshot.Snapshot)
)
self.volume_sdk_client.find_snapshot.side_effect = self.snapshots
self.volume_sdk_client.delete_snapshot.return_value = None
# Get the command object to mock
self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)

@@ -217,7 +215,10 @@

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.snapshots_mock.delete.assert_called_with(
self.snapshots[0].id, False
self.volume_sdk_client.find_snapshot.assert_called_once_with(
self.snapshots[0].id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_snapshot.assert_called_once_with(
self.snapshots[0].id, force=False
)

@@ -230,7 +231,10 @@ def test_snapshot_delete_with_force(self):

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.snapshots_mock.delete.assert_called_with(
self.snapshots[0].id, True
self.volume_sdk_client.find_snapshot.assert_called_once_with(
self.snapshots[0].id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_snapshot.assert_called_once_with(
self.snapshots[0].id, force=True
)

@@ -244,13 +248,20 @@ def test_delete_multiple_snapshots(self):

]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
calls = []
for s in self.snapshots:
calls.append(mock.call(s.id, False))
self.snapshots_mock.delete.assert_has_calls(calls)
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_has_calls(
[mock.call(x.id, ignore_missing=False) for x in self.snapshots]
)
self.volume_sdk_client.delete_snapshot.assert_has_calls(
[mock.call(x.id, force=False) for x in self.snapshots]
)
def test_delete_multiple_snapshots_with_exception(self):
self.volume_sdk_client.find_snapshot.side_effect = [
self.snapshots[0],
sdk_exceptions.NotFoundException(),
]
arglist = [

@@ -266,69 +277,73 @@ self.snapshots[0].id,

find_mock_result = [self.snapshots[0], exceptions.CommandError]
with mock.patch.object(
utils, 'find_resource', side_effect=find_mock_result
) as find_mock:
try:
self.cmd.take_action(parsed_args)
self.fail('CommandError should be raised.')
except exceptions.CommandError as e:
self.assertEqual('1 of 2 snapshots failed to delete.', str(e))
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertEqual('1 of 2 snapshots failed to delete.', str(exc))
find_mock.assert_any_call(
self.snapshots_mock, self.snapshots[0].id
)
find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot')
self.volume_sdk_client.find_snapshot.assert_has_calls(
[
mock.call(self.snapshots[0].id, ignore_missing=False),
mock.call('unexist_snapshot', ignore_missing=False),
]
)
self.volume_sdk_client.delete_snapshot.assert_has_calls(
[
mock.call(self.snapshots[0].id, force=False),
]
)
self.assertEqual(2, find_mock.call_count)
self.snapshots_mock.delete.assert_called_once_with(
self.snapshots[0].id, False
)
class TestVolumeSnapshotList(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
class TestVolumeSnapshotList(TestVolumeSnapshot):
volume = volume_fakes.create_one_volume()
project = project_fakes.FakeProject.create_one_project()
snapshots = volume_fakes.create_snapshots(
attrs={'volume_id': volume.name}, count=3
)
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.snapshots = list(
sdk_fakes.generate_fake_resources(
_snapshot.Snapshot, attrs={'volume_id': self.volume.name}
)
)
self.project = project_fakes.FakeProject.create_one_project()
self.volume_sdk_client.volumes.return_value = [self.volume]
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.snapshots.return_value = self.snapshots
self.project_mock = self.identity_client.projects
self.project_mock.get.return_value = self.project
columns = ["ID", "Name", "Description", "Status", "Size"]
columns_long = columns + ["Created At", "Volume", "Properties"]
self.columns = ("ID", "Name", "Description", "Status", "Size")
self.columns_long = self.columns + (
"Created At",
"Volume",
"Properties",
)
data = []
for s in snapshots:
data.append(
(
s.id,
s.name,
s.description,
s.status,
s.size,
self.data = []
self.data_long = []
for s in self.snapshots:
self.data.append(
(
s.id,
s.name,
s.description,
s.status,
s.size,
)
)
)
data_long = []
for s in snapshots:
data_long.append(
(
s.id,
s.name,
s.description,
s.status,
s.size,
s.created_at,
volume_snapshot.VolumeIdColumn(
s.volume_id, volume_cache={volume.id: volume}
),
format_columns.DictColumn(s.metadata),
self.data_long.append(
(
s.id,
s.name,
s.description,
s.status,
s.size,
s.created_at,
volume_snapshot.VolumeIdColumn(
s.volume_id, volume_cache={self.volume.id: self.volume}
),
format_columns.DictColumn(s.metadata),
)
)
)
def setUp(self):
super().setUp()
self.volumes_mock.list.return_value = [self.volume]
self.volumes_mock.get.return_value = self.volume
self.project_mock.get.return_value = self.project
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)

@@ -343,12 +358,10 @@

self.snapshots_mock.list.assert_called_once_with(
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
search_opts={
'all_tenants': False,
'name': None,
'status': None,
'project_id': None,
'volume_id': None,
},
all_projects=False,
name=None,
status=None,
project_id=None,
volume_id=None,
)

@@ -379,12 +392,10 @@ self.assertEqual(self.columns, columns)

self.snapshots_mock.list.assert_called_once_with(
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=2,
marker=self.snapshots[0].id,
search_opts={
'all_tenants': True,
'project_id': self.project.id,
'name': None,
'status': None,
'volume_id': None,
},
all_projects=True,
project_id=self.project.id,
name=None,
status=None,
volume_id=None,
)

@@ -403,12 +414,10 @@ self.assertEqual(self.columns_long, columns)

self.snapshots_mock.list.assert_called_once_with(
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
search_opts={
'all_tenants': True,
'name': None,
'status': None,
'project_id': None,
'volume_id': None,
},
all_projects=True,
name=None,
status=None,
project_id=None,
volume_id=None,
)

@@ -432,12 +441,10 @@ self.assertEqual(self.columns, columns)

self.snapshots_mock.list.assert_called_once_with(
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
search_opts={
'all_tenants': False,
'name': self.snapshots[0].name,
'status': None,
'project_id': None,
'volume_id': None,
},
all_projects=False,
name=self.snapshots[0].name,
status=None,
project_id=None,
volume_id=None,
)

@@ -450,3 +457,3 @@ self.assertEqual(self.columns, columns)

'--status',
self.snapshots[0].status,
'available',
]

@@ -456,3 +463,3 @@ verifylist = [

('long', False),
('status', self.snapshots[0].status),
('status', 'available'),
]

@@ -463,12 +470,10 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.snapshots_mock.list.assert_called_once_with(
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
search_opts={
'all_tenants': False,
'name': None,
'status': self.snapshots[0].status,
'project_id': None,
'volume_id': None,
},
all_projects=False,
name=None,
status='available',
project_id=None,
volume_id=None,
)

@@ -492,12 +497,10 @@ self.assertEqual(self.columns, columns)

self.snapshots_mock.list.assert_called_once_with(
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
search_opts={
'all_tenants': False,
'name': None,
'status': None,
'project_id': None,
'volume_id': self.volume.id,
},
all_projects=False,
name=None,
status=None,
project_id=None,
volume_id=self.volume.id,
)

@@ -524,12 +527,14 @@ self.assertEqual(self.columns, columns)

class TestVolumeSnapshotSet(TestVolumeSnapshot):
snapshot = volume_fakes.create_one_snapshot()
class TestVolumeSnapshotSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.set_metadata.return_value = None
self.snapshots_mock.update.return_value = None
# Get the command object to mock
self.snapshot = sdk_fakes.generate_fake_resource(
_snapshot.Snapshot, metadata={'foo': 'bar'}
)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.volume_sdk_client.delete_snapshot_metadata.return_value = None
self.volume_sdk_client.set_snapshot_metadata.return_value = None
self.volume_sdk_client.update_snapshot.return_value = None
self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)

@@ -547,7 +552,10 @@

result = self.cmd.take_action(parsed_args)
self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot)
self.assertNotCalled(self.snapshots_mock.reset_state)
self.assertNotCalled(self.snapshots_mock.update)
self.assertNotCalled(self.snapshots_mock.set_metadata)
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_called_once_with(
parsed_args.snapshot, ignore_missing=False
)
self.volume_sdk_client.reset_snapshot_status.assert_not_called()
self.volume_sdk_client.update_snapshot.assert_not_called()
self.volume_sdk_client.set_snapshot_metadata.assert_not_called()

@@ -564,6 +572,5 @@ def test_snapshot_set_name_and_property(self):

]
new_property = {"x": "y", "foo": "foo"}
verifylist = [
("name", "new_snapshot"),
("property", new_property),
("properties", {"x": "y", "foo": "foo"}),
("snapshot", self.snapshot.id),

@@ -575,12 +582,9 @@ ]

kwargs = {
"name": "new_snapshot",
}
self.snapshots_mock.update.assert_called_with(
self.snapshot.id, **kwargs
self.assertIsNone(result)
self.volume_sdk_client.update_snapshot.assert_called_with(
self.snapshot.id, name="new_snapshot"
)
self.snapshots_mock.set_metadata.assert_called_with(
self.snapshot.id, new_property
self.volume_sdk_client.set_snapshot_metadata.assert_called_with(
self.snapshot.id, x="y", foo="foo"
)
self.assertIsNone(result)

@@ -599,10 +603,13 @@ def test_snapshot_set_with_no_property(self):

result = self.cmd.take_action(parsed_args)
self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot)
self.assertNotCalled(self.snapshots_mock.reset_state)
self.assertNotCalled(self.snapshots_mock.update)
self.assertNotCalled(self.snapshots_mock.set_metadata)
self.snapshots_mock.delete_metadata.assert_called_with(
self.snapshot.id, ["foo"]
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_called_once_with(
parsed_args.snapshot, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.reset_snapshot_status.assert_not_called()
self.volume_sdk_client.update_snapshot.assert_not_called()
self.volume_sdk_client.set_snapshot_metadata.assert_not_called()
self.volume_sdk_client.delete_snapshot_metadata.assert_called_with(
self.snapshot.id, keys=["foo"]
)

@@ -618,3 +625,3 @@ def test_snapshot_set_with_no_property_and_property(self):

("no_property", True),
("property", {"foo_1": "bar_1"}),
("properties", {"foo_1": "bar_1"}),
("snapshot", self.snapshot.id),

@@ -625,12 +632,16 @@ ]

result = self.cmd.take_action(parsed_args)
self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot)
self.assertNotCalled(self.snapshots_mock.reset_state)
self.assertNotCalled(self.snapshots_mock.update)
self.snapshots_mock.delete_metadata.assert_called_with(
self.snapshot.id, ["foo"]
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_called_once_with(
parsed_args.snapshot, ignore_missing=False
)
self.snapshots_mock.set_metadata.assert_called_once_with(
self.snapshot.id, {"foo_1": "bar_1"}
self.volume_sdk_client.reset_snapshot_status.assert_not_called()
self.volume_sdk_client.update_snapshot.assert_not_called()
self.volume_sdk_client.delete_snapshot_metadata.assert_called_with(
self.snapshot.id, keys=["foo"]
)
self.assertIsNone(result)
self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with(
self.snapshot.id,
foo_1="bar_1",
)

@@ -644,9 +655,11 @@ def test_snapshot_set_state_to_error(self):

self.snapshots_mock.reset_state.assert_called_with(
self.assertIsNone(result)
self.volume_sdk_client.reset_snapshot_status.assert_called_with(
self.snapshot.id, "error"
)
self.assertIsNone(result)
def test_volume_set_state_failed(self):
self.snapshots_mock.reset_state.side_effect = exceptions.CommandError()
self.volume_sdk_client.reset_snapshot_status.side_effect = (
exceptions.CommandError()
)
arglist = ['--state', 'error', self.snapshot.id]

@@ -656,10 +669,8 @@ verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)]

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(
'One or more of the set operations failed', str(e)
)
self.snapshots_mock.reset_state.assert_called_once_with(
exc = self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args
)
self.assertEqual('One or more of the set operations failed', str(exc))
self.volume_sdk_client.reset_snapshot_status.assert_called_once_with(
self.snapshot.id, 'error'

@@ -669,3 +680,5 @@ )

def test_volume_set_name_and_state_failed(self):
self.snapshots_mock.reset_state.side_effect = exceptions.CommandError()
self.volume_sdk_client.reset_snapshot_status.side_effect = (
exceptions.CommandError()
)
arglist = [

@@ -683,18 +696,15 @@ '--state',

]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
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(
'One or more of the set operations failed', str(e)
)
kwargs = {
"name": "new_snapshot",
}
self.snapshots_mock.update.assert_called_once_with(
self.snapshot.id, **kwargs
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.snapshots_mock.reset_state.assert_called_once_with(
self.assertEqual('One or more of the set operations failed', str(exc))
self.volume_sdk_client.update_snapshot.assert_called_once_with(
self.snapshot.id, name="new_snapshot"
)
self.volume_sdk_client.reset_snapshot_status.assert_called_once_with(
self.snapshot.id, 'error'

@@ -704,19 +714,18 @@ )

class TestVolumeSnapshotShow(TestVolumeSnapshot):
columns = (
'created_at',
'description',
'id',
'name',
'properties',
'size',
'status',
'volume_id',
)
class TestVolumeSnapshotShow(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshot = volume_fakes.create_one_snapshot()
self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot)
self.columns = (
'created_at',
'description',
'id',
'name',
'properties',
'size',
'status',
'volume_id',
)
self.data = (

@@ -733,4 +742,4 @@ self.snapshot.created_at,

self.snapshots_mock.get.return_value = self.snapshot
# Get the command object to test
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)

@@ -744,3 +753,5 @@

columns, data = self.cmd.take_action(parsed_args)
self.snapshots_mock.get.assert_called_with(self.snapshot.id)
self.volume_sdk_client.find_snapshot.assert_called_with(
self.snapshot.id, ignore_missing=False
)

@@ -751,11 +762,10 @@ self.assertEqual(self.columns, columns)

class TestVolumeSnapshotUnset(TestVolumeSnapshot):
snapshot = volume_fakes.create_one_snapshot()
class TestVolumeSnapshotUnset(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.delete_metadata.return_value = None
# Get the command object to mock
self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.volume_sdk_client.delete_snapshot_metadata.return_value = None
self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)

@@ -770,3 +780,3 @@

verifylist = [
("property", ["foo"]),
("properties", ["foo"]),
("snapshot", self.snapshot.id),

@@ -779,5 +789,5 @@ ]

self.snapshots_mock.delete_metadata.assert_called_with(
self.snapshot.id, ["foo"]
self.assertIsNone(result)
self.volume_sdk_client.delete_snapshot_metadata.assert_called_with(
self.snapshot.id, keys=["foo"]
)
self.assertIsNone(result)

@@ -16,2 +16,5 @@ #

from openstack.block_storage.v2 import volume as _volume
from openstack import exceptions as sdk_exceptions
from openstack.test import fakes as sdk_fakes
from osc_lib.cli import format_columns

@@ -50,9 +53,3 @@ from osc_lib import exceptions

def setup_volumes_mock(self, count):
volumes = volume_fakes.create_volumes(count=count)
self.volumes_mock.get = volume_fakes.get_volumes(volumes, 0)
return volumes
class TestVolumeCreate(TestVolume):

@@ -193,3 +190,3 @@ project = identity_fakes.FakeProject.create_one_project()

verifylist = [
('property', {'Alpha': 'a', 'Beta': 'b'}),
('properties', {'Alpha': 'a', 'Beta': 'b'}),
('size', self.new_volume.size),

@@ -388,5 +385,3 @@ ('name', self.new_volume.name),

('bootable', True),
('non_bootable', False),
('read_only', True),
('read_write', False),
('size', self.new_volume.size),

@@ -434,5 +429,3 @@ ('name', self.new_volume.name),

('bootable', False),
('non_bootable', True),
('read_only', False),
('read_write', True),
('size', self.new_volume.size),

@@ -489,5 +482,3 @@ ('name', self.new_volume.name),

('bootable', True),
('non_bootable', False),
('read_only', True),
('read_write', False),
('size', self.new_volume.size),

@@ -541,5 +532,3 @@ ('name', self.new_volume.name),

('bootable', False),
('non_bootable', True),
('read_only', True),
('read_write', False),
('size', self.new_volume.size),

@@ -680,19 +669,18 @@ ('name', self.new_volume.name),

class TestVolumeDelete(TestVolume):
class TestVolumeDelete(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.volumes_mock.delete.return_value = None
self.volumes = list(sdk_fakes.generate_fake_resources(_volume.Volume))
self.volume_sdk_client.find_volume.side_effect = self.volumes
self.volume_sdk_client.delete_volume.return_value = None
# Get the command object to mock
self.cmd = volume.DeleteVolume(self.app, None)
def test_volume_delete_one_volume(self):
volumes = self.setup_volumes_mock(count=1)
arglist = [volumes[0].id]
arglist = [self.volumes[0].id]
verifylist = [
("force", False),
("purge", False),
("volumes", [volumes[0].id]),
("volumes", [self.volumes[0].id]),
]

@@ -702,12 +690,13 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volumes_mock.delete.assert_called_once_with(
volumes[0].id, cascade=False
self.volume_sdk_client.find_volume.assert_called_once_with(
self.volumes[0].id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_volume.assert_called_once_with(
self.volumes[0].id, cascade=False, force=False
)
def test_volume_delete_multi_volumes(self):
volumes = self.setup_volumes_mock(count=3)
arglist = [v.id for v in volumes]
arglist = [v.id for v in self.volumes]
verifylist = [

@@ -721,12 +710,19 @@ ('force', False),

result = self.cmd.take_action(parsed_args)
calls = [mock.call(v.id, cascade=False) for v in volumes]
self.volumes_mock.delete.assert_has_calls(calls)
self.assertIsNone(result)
self.volume_sdk_client.find_volume.assert_has_calls(
[mock.call(v.id, ignore_missing=False) for v in self.volumes]
)
self.volume_sdk_client.delete_volume.assert_has_calls(
[mock.call(v.id, cascade=False, force=False) for v in self.volumes]
)
def test_volume_delete_multi_volumes_with_exception(self):
volumes = self.setup_volumes_mock(count=2)
self.volume_sdk_client.find_volume.side_effect = [
self.volumes[0],
sdk_exceptions.NotFoundException(),
]
arglist = [
volumes[0].id,
self.volumes[0].id,
'unexist_volume',

@@ -737,30 +733,29 @@ ]

('purge', False),
('volumes', arglist),
('volumes', [self.volumes[0].id, 'unexist_volume']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
find_mock_result = [volumes[0], exceptions.CommandError]
with mock.patch.object(
utils, 'find_resource', side_effect=find_mock_result
) as find_mock:
try:
self.cmd.take_action(parsed_args)
self.fail('CommandError should be raised.')
except exceptions.CommandError as e:
self.assertEqual('1 of 2 volumes failed to delete.', str(e))
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertEqual('1 of 2 volumes failed to delete.', str(exc))
find_mock.assert_any_call(self.volumes_mock, volumes[0].id)
find_mock.assert_any_call(self.volumes_mock, 'unexist_volume')
self.volume_sdk_client.find_volume.assert_has_calls(
[
mock.call(self.volumes[0].id, ignore_missing=False),
mock.call('unexist_volume', ignore_missing=False),
]
)
self.volume_sdk_client.delete_volume.assert_has_calls(
[
mock.call(self.volumes[0].id, cascade=False, force=False),
]
)
self.assertEqual(2, find_mock.call_count)
self.volumes_mock.delete.assert_called_once_with(
volumes[0].id, cascade=False
)
def test_volume_delete_with_purge(self):
volumes = self.setup_volumes_mock(count=1)
arglist = [
'--purge',
volumes[0].id,
self.volumes[0].id,
]

@@ -770,3 +765,3 @@ verifylist = [

('purge', True),
('volumes', [volumes[0].id]),
('volumes', [self.volumes[0].id]),
]

@@ -776,14 +771,15 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volumes_mock.delete.assert_called_once_with(
volumes[0].id, cascade=True
self.volume_sdk_client.find_volume.assert_called_once_with(
self.volumes[0].id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_volume.assert_called_once_with(
self.volumes[0].id, cascade=True, force=False
)
def test_volume_delete_with_force(self):
volumes = self.setup_volumes_mock(count=1)
arglist = [
'--force',
volumes[0].id,
self.volumes[0].id,
]

@@ -793,3 +789,3 @@ verifylist = [

('purge', False),
('volumes', [volumes[0].id]),
('volumes', [self.volumes[0].id]),
]

@@ -799,7 +795,12 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

result = self.cmd.take_action(parsed_args)
self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id)
self.assertIsNone(result)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.volumes[0].id, ignore_missing=False
)
self.volume_sdk_client.delete_volume.assert_called_once_with(
self.volumes[0].id, cascade=False, force=True
)
class TestVolumeList(TestVolume):

@@ -1425,6 +1426,6 @@ project = identity_fakes.FakeProject.create_one_project()

verifylist = [
('property', {'a': 'b', 'c': 'd'}),
('properties', {'a': 'b', 'c': 'd'}),
('volume', self.new_volume.id),
('bootable', False),
('non_bootable', False),
('bootable', None),
('read_only', None),
]

@@ -1435,3 +1436,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volumes_mock.set_metadata.assert_called_with(
self.new_volume.id, parsed_args.property
self.new_volume.id, parsed_args.properties
)

@@ -1448,6 +1449,6 @@

verifylist = [
('image_property', {'Alpha': 'a', 'Beta': 'b'}),
('image_properties', {'Alpha': 'a', 'Beta': 'b'}),
('volume', self.new_volume.id),
('bootable', False),
('non_bootable', False),
('bootable', None),
('read_only', None),
]

@@ -1460,3 +1461,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volumes_mock.set_image_metadata.assert_called_with(
self.new_volume.id, parsed_args.image_property
self.new_volume.id, parsed_args.image_properties
)

@@ -1467,4 +1468,3 @@

verifylist = [
('read_only', False),
('read_write', False),
('read_only', None),
('state', 'error'),

@@ -1534,32 +1534,36 @@ ('volume', self.new_volume.id),

arglist = [
['--bootable', self.new_volume.id],
['--non-bootable', self.new_volume.id],
'--bootable',
self.new_volume.id,
]
verifylist = [
[
('bootable', True),
('non_bootable', False),
('volume', self.new_volume.id),
],
[
('bootable', False),
('non_bootable', True),
('volume', self.new_volume.id),
],
('bootable', True),
('volume', self.new_volume.id),
]
for index in range(len(arglist)):
parsed_args = self.check_parser(
self.cmd, arglist[index], verifylist[index]
)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, verifylist[index][0][1]
)
self.cmd.take_action(parsed_args)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, verifylist[0][1]
)
def test_volume_set_readonly(self):
def test_volume_set_non_bootable(self):
arglist = [
'--non-bootable',
self.new_volume.id,
]
verifylist = [
('bootable', False),
('volume', self.new_volume.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, verifylist[0][1]
)
def test_volume_set_read_only(self):
arglist = ['--read-only', self.new_volume.id]
verifylist = [
('read_only', True),
('read_write', False),
('volume', self.new_volume.id),

@@ -1580,3 +1584,2 @@ ]

('read_only', False),
('read_write', True),
('volume', self.new_volume.id),

@@ -1711,3 +1714,3 @@ ]

verifylist = [
('image_property', {'Alpha': 'a', 'Beta': 'b'}),
('image_properties', {'Alpha': 'a', 'Beta': 'b'}),
('volume', self.new_volume.id),

@@ -1728,3 +1731,3 @@ ]

verifylist_unset = [
('image_property', ['Alpha']),
('image_properties', ['Alpha']),
('volume', self.new_volume.id),

@@ -1741,3 +1744,3 @@ ]

self.volumes_mock.delete_image_metadata.assert_called_with(
self.new_volume.id, parsed_args_unset.image_property
self.new_volume.id, parsed_args_unset.image_properties
)

@@ -1757,4 +1760,4 @@

verifylist = [
('image_property', ['Alpha']),
('property', ['Beta']),
('image_properties', ['Alpha']),
('properties', ['Beta']),
('volume', self.new_volume.id),

@@ -1772,6 +1775,6 @@ ]

self.volumes_mock.delete_image_metadata.assert_called_with(
self.new_volume.id, parsed_args.image_property
self.new_volume.id, parsed_args.image_properties
)
self.volumes_mock.delete_metadata.assert_called_with(
self.new_volume.id, parsed_args.property
self.new_volume.id, parsed_args.properties
)

@@ -1778,0 +1781,0 @@

@@ -13,5 +13,6 @@ #

# under the License.
#
import ddt
from openstack.block_storage.v3 import service as _service
from openstack.test import fakes as sdk_fakes
from osc_lib import exceptions

@@ -24,20 +25,13 @@

class TestService(volume_fakes.TestVolume):
class TestBlockStorageLogLevelList(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
# Get a shortcut to the ServiceManager Mock
self.service_mock = self.volume_client.services
self.service_mock.reset_mock()
self.log_level = sdk_fakes.generate_fake_resource(
_service.LogLevel, binary='cinder-scheduler'
)
self.volume_sdk_client.get_service_log_levels.return_value = [
self.log_level
]
class TestBlockStorageLogLevelList(TestService):
service_log = volume_fakes.create_service_log_level_entry()
def setUp(self):
super().setUp()
self.service_mock.get_log_levels.return_value = [self.service_log]
# Get the command object to test
self.cmd = service.BlockStorageLogLevelList(self.app, None)

@@ -50,12 +44,12 @@

'--host',
self.service_log.host,
self.log_level.host,
'--service',
self.service_log.binary,
self.log_level.binary,
'--log-prefix',
self.service_log.prefix,
'cinder.',
]
verifylist = [
('host', self.service_log.host),
('service', self.service_log.binary),
('log_prefix', self.service_log.prefix),
('host', self.log_level.host),
('service', self.log_level.binary),
('log_prefix', 'cinder.'),
]

@@ -72,23 +66,18 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
datalist = (
datalist = tuple(
(
self.service_log.binary,
self.service_log.host,
self.service_log.prefix,
self.service_log.level,
),
self.log_level.binary,
self.log_level.host,
prefix,
level,
)
for prefix, level in self.log_level.levels.values()
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
# checking if proper call was made to get log level of services
self.service_mock.get_log_levels.assert_called_with(
server=self.service_log.host,
binary=self.service_log.binary,
prefix=self.service_log.prefix,
self.volume_sdk_client.get_service_log_levels.assert_called_with(
server=self.log_level.host,
binary=self.log_level.binary,
prefix='cinder.',
)

@@ -99,3 +88,3 @@

'--host',
self.service_log.host,
self.log_level.host,
'--service',

@@ -107,3 +96,3 @@ 'cinder-api',

verifylist = [
('host', self.service_log.host),
('host', self.log_level.host),
('service', 'cinder-api'),

@@ -126,3 +115,3 @@ ('log_prefix', 'cinder_test.api.common'),

'--host',
self.service_log.host,
self.log_level.host,
'--service',

@@ -134,3 +123,3 @@ 'nova-api',

verifylist = [
('host', self.service_log.host),
('host', self.log_level.host),
('service', 'nova-api'),

@@ -150,9 +139,11 @@ ('log_prefix', 'cinder_test.api.common'),

@ddt.ddt
class TestBlockStorageLogLevelSet(TestService):
service_log = volume_fakes.create_service_log_level_entry()
class TestBlockStorageLogLevelSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
# Get the command object to test
self.log_level = sdk_fakes.generate_fake_resource(
_service.LogLevel, binary='cinder-api'
)
self.volume_sdk_client.set_service_log_levels.return_value = None
self.cmd = service.BlockStorageLogLevelSet(self.app, None)

@@ -166,24 +157,24 @@

'--host',
self.service_log.host,
self.log_level.host,
'--service',
self.service_log.binary,
self.log_level.binary,
'--log-prefix',
self.service_log.prefix,
'cinder.api.common',
]
verifylist = [
('level', 'ERROR'),
('host', self.service_log.host),
('service', self.service_log.binary),
('log_prefix', self.service_log.prefix),
('host', self.log_level.host),
('service', self.log_level.binary),
('log_prefix', 'cinder.api.common'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
ret = self.cmd.take_action(parsed_args)
# checking if proper call was made to set log level of services
self.service_mock.set_log_levels.assert_called_with(
self.assertIsNone(ret)
self.volume_sdk_client.set_service_log_levels.assert_called_with(
level='ERROR',
server=self.service_log.host,
binary=self.service_log.binary,
prefix=self.service_log.prefix,
server=self.log_level.host,
binary=self.log_level.binary,
prefix='cinder.api.common',
)

@@ -195,13 +186,13 @@

'--host',
self.service_log.host,
self.log_level.host,
'--service',
'cinder-api',
'--log-prefix',
'cinder_test.api.common',
'cinder.api.common',
]
verifylist = [
('level', 'ERROR'),
('host', self.service_log.host),
('host', self.log_level.host),
('service', 'cinder-api'),
('log_prefix', 'cinder_test.api.common'),
('log_prefix', 'cinder.api.common'),
]

@@ -223,3 +214,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

'--host',
self.service_log.host,
self.log_level.host,
'--service',

@@ -232,3 +223,3 @@ 'nova-api',

('level', 'ERROR'),
('host', self.service_log.host),
('host', self.log_level.host),
('service', 'nova-api'),

@@ -253,3 +244,3 @@ ('log_prefix', 'cinder.api.common'),

'--host',
self.service_log.host,
self.log_level.host,
'--service',

@@ -262,3 +253,3 @@ 'cinder-api',

('level', log_level.upper()),
('host', self.service_log.host),
('host', self.log_level.host),
('service', 'cinder-api'),

@@ -281,8 +272,7 @@ ('log_prefix', 'cinder.api.common'),

# checking if proper call was made to set log level of services
self.service_mock.set_log_levels.assert_called_with(
self.volume_sdk_client.set_service_log_levels.assert_called_with(
level=log_level.upper(),
server=self.service_log.host,
binary=self.service_log.binary,
prefix=self.service_log.prefix,
server=self.log_level.host,
binary=self.log_level.binary,
prefix='cinder.api.common',
)

@@ -15,4 +15,8 @@ #

from cinderclient import api_versions
from unittest import mock
from openstack.block_storage.v3 import service as _service
from openstack.test import fakes as sdk_fakes
from osc_lib import exceptions
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes

@@ -22,21 +26,9 @@ from openstackclient.volume.v3 import service

class TestService(volume_fakes.TestVolume):
class TestServiceList(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
# Get a shortcut to the ServiceManager Mock
self.service_mock = self.volume_client.services
self.service_mock.reset_mock()
self.service = sdk_fakes.generate_fake_resource(_service.Service)
self.volume_sdk_client.services.return_value = [self.service]
class TestServiceList(TestService):
# The service to be listed
services = volume_fakes.create_one_service()
def setUp(self):
super().setUp()
self.service_mock.list.return_value = [self.services]
# Get the command object to test
self.cmd = service.ListService(self.app, None)

@@ -47,18 +39,15 @@

'--host',
self.services.host,
self.service.host,
'--service',
self.services.binary,
self.service.binary,
]
verifylist = [
('host', self.services.host),
('service', self.services.binary),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
expected_columns = [
expected_columns = (
'Binary',

@@ -70,42 +59,31 @@ 'Host',

'Updated At',
]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
)
datalist = (
(
self.services.binary,
self.services.host,
self.services.zone,
self.services.status,
self.services.state,
self.services.updated_at,
self.service.binary,
self.service.host,
self.service.availability_zone,
self.service.status,
self.service.state,
self.service.updated_at,
),
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
# checking if proper call was made to list services
self.service_mock.list.assert_called_with(
self.services.host,
self.services.binary,
self.volume_sdk_client.services.assert_called_with(
host=self.service.host,
binary=self.service.binary,
)
# checking if prohibited columns are present in output
self.assertNotIn("Disabled Reason", columns)
self.assertNotIn(self.services.disabled_reason, tuple(data))
def test_service_list_with_long_option(self):
arglist = [
'--host',
self.services.host,
self.service.host,
'--service',
self.services.binary,
self.service.binary,
'--long',
]
verifylist = [
('host', self.services.host),
('service', self.services.binary),
('host', self.service.host),
('service', self.service.binary),
('long', True),

@@ -115,8 +93,5 @@ ]

# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
expected_columns = [
expected_columns = (
'Binary',

@@ -129,51 +104,39 @@ 'Host',

'Disabled Reason',
]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
)
datalist = (
(
self.services.binary,
self.services.host,
self.services.zone,
self.services.status,
self.services.state,
self.services.updated_at,
self.services.disabled_reason,
self.service.binary,
self.service.host,
self.service.availability_zone,
self.service.status,
self.service.state,
self.service.updated_at,
self.service.disabled_reason,
),
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
self.service_mock.list.assert_called_with(
self.services.host,
self.services.binary,
self.volume_sdk_client.services.assert_called_with(
host=self.service.host,
binary=self.service.binary,
)
def test_service_list_with_cluster(self):
self.volume_client.api_version = api_versions.APIVersion('3.7')
cluster = {'cluster': 'fake-cluster'}
cluster_service = volume_fakes.create_one_service(attrs=cluster)
self.service_mock.list.return_value = [cluster_service]
self.set_volume_api_version('3.7')
arglist = [
'--host',
cluster_service.host,
self.service.host,
'--service',
cluster_service.binary,
self.service.binary,
]
verifylist = [
('host', cluster_service.host),
('service', cluster_service.binary),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
expected_columns = [
expected_columns = (
'Binary',

@@ -186,56 +149,39 @@ 'Host',

'Cluster',
]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
)
datalist = (
(
cluster_service.binary,
cluster_service.host,
cluster_service.zone,
cluster_service.status,
cluster_service.state,
cluster_service.updated_at,
cluster_service.cluster,
self.service.binary,
self.service.host,
self.service.availability_zone,
self.service.status,
self.service.state,
self.service.updated_at,
self.service.cluster,
),
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
# checking if proper call was made to list services
self.service_mock.list.assert_called_with(
cluster_service.host,
cluster_service.binary,
self.volume_sdk_client.services.assert_called_with(
host=self.service.host,
binary=self.service.binary,
)
# checking if prohibited columns are present in output
self.assertNotIn("Disabled Reason", columns)
self.assertNotIn(cluster_service.disabled_reason, tuple(data))
def test_service_list_with_backend_state(self):
self.volume_client.api_version = api_versions.APIVersion('3.49')
backend_state = {'cluster': 'fake-cluster', 'backend_state': 'up'}
backend_service = volume_fakes.create_one_service(attrs=backend_state)
self.service_mock.list.return_value = [backend_service]
self.set_volume_api_version('3.49')
arglist = [
'--host',
backend_service.host,
self.service.host,
'--service',
backend_service.binary,
self.service.binary,
]
verifylist = [
('host', backend_service.host),
('service', backend_service.binary),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class Lister in cliff, abstract method take_action()
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
expected_columns = [
expected_columns = (
'Binary',

@@ -249,31 +195,165 @@ 'Host',

'Backend State',
]
# confirming if all expected columns are present in the result.
self.assertEqual(expected_columns, columns)
)
datalist = (
(
backend_service.binary,
backend_service.host,
backend_service.zone,
backend_service.status,
backend_service.state,
backend_service.updated_at,
backend_service.cluster,
backend_service.backend_state,
self.service.binary,
self.service.host,
self.service.availability_zone,
self.service.status,
self.service.state,
self.service.updated_at,
self.service.cluster,
self.service.backend_state,
),
)
# confirming if all expected values are present in the result.
self.assertEqual(expected_columns, columns)
self.assertEqual(datalist, tuple(data))
self.volume_sdk_client.services.assert_called_with(
host=self.service.host,
binary=self.service.binary,
)
# checking if proper call was made to list services
self.service_mock.list.assert_called_with(
backend_service.host,
backend_service.binary,
class TestServiceSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.service = sdk_fakes.generate_fake_resource(_service.Service)
self.service.enable = mock.Mock(autospec=True)
self.service.disable = mock.Mock(autospec=True)
self.volume_sdk_client.find_service.return_value = self.service
self.cmd = service.SetService(self.app, None)
def test_service_set_nothing(self):
arglist = [
self.service.host,
self.service.binary,
]
verifylist = [
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.service.enable.assert_not_called()
self.service.disable.assert_not_called()
self.assertIsNone(result)
def test_service_set_enable(self):
arglist = [
'--enable',
self.service.host,
self.service.binary,
]
verifylist = [
('enable', True),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.service.enable.assert_called_with(self.volume_sdk_client)
self.service.disable.assert_not_called()
self.assertIsNone(result)
def test_service_set_disable(self):
arglist = [
'--disable',
self.service.host,
self.service.binary,
]
verifylist = [
('disable', True),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.service.enable.assert_not_called()
self.service.disable.assert_called_with(
self.volume_sdk_client, reason=None
)
self.assertIsNone(result)
# checking if prohibited columns are present in output
self.assertNotIn("Disabled Reason", columns)
self.assertNotIn(backend_service.disabled_reason, tuple(data))
def test_service_set_disable_with_reason(self):
reason = 'earthquake'
arglist = [
'--disable',
'--disable-reason',
reason,
self.service.host,
self.service.binary,
]
verifylist = [
('disable', True),
('disable_reason', reason),
('host', self.service.host),
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.service.enable.assert_not_called()
self.service.disable.assert_called_with(
self.volume_sdk_client, reason=reason
)
self.assertIsNone(result)
def test_service_set_only_with_disable_reason(self):
reason = 'earthquake'
arglist = [
'--disable-reason',
reason,
self.service.host,
self.service.binary,
]
verifylist = [
('disable_reason', reason),
('host', self.service.host),
('service', self.service.binary),
]
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(
"Cannot specify option --disable-reason without "
"--disable specified.",
str(e),
)
def test_service_set_enable_with_disable_reason(self):
reason = 'earthquake'
arglist = [
'--enable',
'--disable-reason',
reason,
self.service.host,
self.service.binary,
]
verifylist = [
('enable', True),
('disable_reason', reason),
('host', self.service.host),
('service', self.service.binary),
]
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(
"Cannot specify option --disable-reason without "
"--disable specified.",
str(e),
)

@@ -31,3 +31,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may

volume = volume_fakes.create_one_volume()
server = compute_fakes.create_one_sdk_server()
server = compute_fakes.create_one_server()
volume_attachment = volume_fakes.create_one_volume_attachment(

@@ -34,0 +34,0 @@ attrs={'instance': server.id, 'volume_id': volume.id},

@@ -14,4 +14,9 @@ #

from unittest.mock import call
from unittest import mock
from openstack.block_storage.v3 import backup as _backup
from openstack.block_storage.v3 import snapshot as _snapshot
from openstack.block_storage.v3 import volume as _volume
from openstack import exceptions as sdk_exceptions
from openstack.test import fakes as sdk_fakes
from osc_lib import exceptions

@@ -23,23 +28,3 @@

class TestBackupLegacy(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.backups_mock = self.volume_client.backups
self.backups_mock.reset_mock()
self.volumes_mock = self.volume_client.volumes
self.volumes_mock.reset_mock()
self.snapshots_mock = self.volume_client.volume_snapshots
self.snapshots_mock.reset_mock()
self.restores_mock = self.volume_client.restores
self.restores_mock.reset_mock()
class TestBackupCreate(volume_fakes.TestVolume):
volume = volume_fakes.create_one_volume()
snapshot = volume_fakes.create_one_snapshot()
new_backup = volume_fakes.create_one_backup(
attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id}
)
columns = (

@@ -50,7 +35,2 @@ 'id',

)
data = (
new_backup.id,
new_backup.name,
new_backup.volume_id,
)

@@ -60,7 +40,19 @@ def setUp(self):

self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.volume_sdk_client.create_backup.return_value = self.new_backup
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup,
volume_id=self.volume.id,
snapshot_id=self.snapshot.id,
)
self.volume_sdk_client.create_backup.return_value = self.backup
# Get the command object to test
self.data = (
self.backup.id,
self.backup.name,
self.backup.volume_id,
)
self.cmd = volume_backup.CreateVolumeBackup(self.app, None)

@@ -71,21 +63,21 @@

"--name",
self.new_backup.name,
self.backup.name,
"--description",
self.new_backup.description,
self.backup.description,
"--container",
self.new_backup.container,
self.backup.container,
"--force",
"--incremental",
"--snapshot",
self.new_backup.snapshot_id,
self.new_backup.volume_id,
self.backup.snapshot_id,
self.backup.volume_id,
]
verifylist = [
("name", self.new_backup.name),
("description", self.new_backup.description),
("container", self.new_backup.container),
("name", self.backup.name),
("description", self.backup.description),
("container", self.backup.container),
("force", True),
("incremental", True),
("snapshot", self.new_backup.snapshot_id),
("volume", self.new_backup.volume_id),
("snapshot", self.backup.snapshot_id),
("volume", self.backup.volume_id),
]

@@ -97,9 +89,9 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volume_sdk_client.create_backup.assert_called_with(
volume_id=self.new_backup.volume_id,
container=self.new_backup.container,
name=self.new_backup.name,
description=self.new_backup.description,
volume_id=self.backup.volume_id,
container=self.backup.container,
name=self.backup.name,
description=self.backup.description,
force=True,
is_incremental=True,
snapshot_id=self.new_backup.snapshot_id,
snapshot_id=self.backup.snapshot_id,
)

@@ -117,7 +109,7 @@ self.assertEqual(self.columns, columns)

"wow=much-cool",
self.new_backup.volume_id,
self.backup.volume_id,
]
verifylist = [
("properties", {"foo": "bar", "wow": "much-cool"}),
("volume", self.new_backup.volume_id),
("volume", self.backup.volume_id),
]

@@ -129,3 +121,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volume_sdk_client.create_backup.assert_called_with(
volume_id=self.new_backup.volume_id,
volume_id=self.backup.volume_id,
container=None,

@@ -149,7 +141,7 @@ name=None,

"wow=much-cool",
self.new_backup.volume_id,
self.backup.volume_id,
]
verifylist = [
("properties", {"foo": "bar", "wow": "much-cool"}),
("volume", self.new_backup.volume_id),
("volume", self.backup.volume_id),
]

@@ -169,7 +161,7 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

"my-az",
self.new_backup.volume_id,
self.backup.volume_id,
]
verifylist = [
("availability_zone", "my-az"),
("volume", self.new_backup.volume_id),
("volume", self.backup.volume_id),
]

@@ -181,3 +173,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volume_sdk_client.create_backup.assert_called_with(
volume_id=self.new_backup.volume_id,
volume_id=self.backup.volume_id,
container=None,

@@ -199,7 +191,7 @@ name=None,

"my-az",
self.new_backup.volume_id,
self.backup.volume_id,
]
verifylist = [
("availability_zone", "my-az"),
("volume", self.new_backup.volume_id),
("volume", self.backup.volume_id),
]

@@ -216,11 +208,11 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

"--description",
self.new_backup.description,
self.backup.description,
"--container",
self.new_backup.container,
self.new_backup.volume_id,
self.backup.container,
self.backup.volume_id,
]
verifylist = [
("description", self.new_backup.description),
("container", self.new_backup.container),
("volume", self.new_backup.volume_id),
("description", self.backup.description),
("container", self.backup.container),
("volume", self.backup.volume_id),
]

@@ -232,6 +224,6 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist)

self.volume_sdk_client.create_backup.assert_called_with(
volume_id=self.new_backup.volume_id,
container=self.new_backup.container,
volume_id=self.backup.volume_id,
container=self.backup.container,
name=None,
description=self.new_backup.description,
description=self.backup.description,
force=False,

@@ -245,13 +237,9 @@ is_incremental=False,

class TestBackupDelete(volume_fakes.TestVolume):
backups = volume_fakes.create_backups(count=2)
def setUp(self):
super().setUp()
self.volume_sdk_client.find_backup = volume_fakes.get_backups(
self.backups
)
self.backups = list(sdk_fakes.generate_fake_resources(_backup.Backup))
self.volume_sdk_client.find_backup.side_effect = self.backups
self.volume_sdk_client.delete_backup.return_value = None
# Get the command object to mock
self.cmd = volume_backup.DeleteVolumeBackup(self.app, None)

@@ -299,3 +287,3 @@

for b in self.backups:
calls.append(call(b.id, ignore_missing=False, force=False))
calls.append(mock.call(b.id, ignore_missing=False, force=False))
self.volume_sdk_client.delete_backup.assert_has_calls(calls)

@@ -340,7 +328,2 @@ self.assertIsNone(result)

class TestBackupList(volume_fakes.TestVolume):
volume = volume_fakes.create_one_volume()
backups = volume_fakes.create_backups(
attrs={'volume_id': volume.name}, count=3
)
columns = (

@@ -361,41 +344,47 @@ 'ID',

data = []
for b in backups:
data.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
)
)
data_long = []
for b in backups:
data_long.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
b.availability_zone,
volume_backup.VolumeIdColumn(b.volume_id),
b.container,
)
)
def setUp(self):
super().setUp()
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.volumes.return_value = [self.volume]
self.backups = list(
sdk_fakes.generate_fake_resources(
_backup.Backup,
attrs={'volume_id': self.volume.id},
)
)
self.volume_sdk_client.backups.return_value = self.backups
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.find_backup.return_value = self.backups[0]
# Get the command to test
self.data = []
for b in self.backups:
self.data.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
)
)
self.data_long = []
for b in self.backups:
self.data_long.append(
(
b.id,
b.name,
b.description,
b.status,
b.size,
b.is_incremental,
b.created_at,
b.availability_zone,
volume_backup.VolumeIdColumn(b.volume_id),
b.container,
)
)
self.cmd = volume_backup.ListVolumeBackup(self.app, None)

@@ -478,7 +467,2 @@

class TestBackupRestore(volume_fakes.TestVolume):
volume = volume_fakes.create_one_volume()
backup = volume_fakes.create_one_backup(
attrs={'volume_id': volume.id},
)
columns = (

@@ -490,13 +474,12 @@ "id",

data = (
backup.id,
volume.id,
volume.name,
)
def setUp(self):
super().setUp()
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup, volume_id=self.volume.id
)
self.volume_sdk_client.find_backup.return_value = self.backup
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.create_backup.return_value = self.backup
self.volume_sdk_client.restore_backup.return_value = {

@@ -508,3 +491,8 @@ 'id': self.backup['id'],

# Get the command object to mock
self.data = (
self.backup.id,
self.volume.id,
self.volume.name,
)
self.cmd = volume_backup.RestoreVolumeBackup(self.app, None)

@@ -598,13 +586,11 @@

class TestBackupSet(TestBackupLegacy):
backup = volume_fakes.create_one_backup(
attrs={'metadata': {'wow': 'cool'}},
)
class TestBackupSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.backups_mock.get.return_value = self.backup
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup, metadata={'wow': 'cool'}
)
self.volume_sdk_client.find_backup.return_value = self.backup
# Get the command object to test
self.cmd = volume_backup.SetVolumeBackup(self.app, None)

@@ -626,10 +612,12 @@

# In base command class ShowOne in cliff, abstract method take_action()
# returns nothing
result = self.cmd.take_action(parsed_args)
self.backups_mock.update.assert_called_once_with(
self.backup.id, **{'name': 'new_name'}
)
self.assertIsNone(result)
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.volume_sdk_client.update_backup.assert_called_once_with(
self.backup, name='new_name'
)
def test_backup_set_name_pre_v39(self):

@@ -670,9 +658,10 @@ self.set_volume_api_version('3.8')

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
# Set expected values
kwargs = {'description': 'new_description'}
self.backups_mock.update.assert_called_once_with(
self.backup.id, **kwargs
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.update_backup.assert_called_once_with(
self.backup, description='new_description'
)

@@ -706,9 +695,16 @@ def test_backup_set_description_pre_v39(self):

result = self.cmd.take_action(parsed_args)
self.backups_mock.reset_state.assert_called_once_with(
self.backup.id, 'error'
)
self.assertIsNone(result)
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.volume_sdk_client.reset_backup_status.assert_called_with(
self.backup, status='error'
)
def test_backup_set_state_failed(self):
self.backups_mock.reset_state.side_effect = exceptions.CommandError()
self.volume_sdk_client.reset_backup_status.side_effect = (
sdk_exceptions.NotFoundException('foo')
)
arglist = ['--state', 'error', self.backup.id]

@@ -718,13 +714,14 @@ verifylist = [('state', 'error'), ('backup', self.backup.id)]

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(
'One or more of the set operations failed', str(e)
)
self.backups_mock.reset_state.assert_called_with(
self.backup.id, 'error'
exc = self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args
)
self.assertEqual('One or more of the set operations failed', str(exc))
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.volume_sdk_client.reset_backup_status.assert_called_with(
self.backup, status='error'
)
def test_backup_set_no_property(self):

@@ -744,11 +741,10 @@ self.set_volume_api_version('3.43')

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
# Set expected values
kwargs = {
'metadata': {},
}
self.backups_mock.update.assert_called_once_with(
self.backup.id, **kwargs
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.update_backup.assert_called_once_with(
self.backup, metadata={}
)

@@ -788,11 +784,10 @@ def test_backup_set_no_property_pre_v343(self):

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
# Set expected values
kwargs = {
'metadata': {'wow': 'cool', 'foo': 'bar'},
}
self.backups_mock.update.assert_called_once_with(
self.backup.id, **kwargs
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.update_backup.assert_called_once_with(
self.backup, metadata={'wow': 'cool', 'foo': 'bar'}
)

@@ -819,13 +814,12 @@ def test_backup_set_property_pre_v343(self):

class TestBackupUnset(TestBackupLegacy):
backup = volume_fakes.create_one_backup(
attrs={'metadata': {'foo': 'bar'}},
)
class TestBackupUnset(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.backups_mock.get.return_value = self.backup
self.backup = sdk_fakes.generate_fake_resource(
_backup.Backup, metadata={'foo': 'bar', 'wow': 'cool'}
)
self.volume_sdk_client.find_backup.return_value = self.backup
self.volume_sdk_client.delete_backup_metadata.return_value = None
# Get the command object to test
self.cmd = volume_backup.UnsetVolumeBackup(self.app, None)

@@ -848,11 +842,10 @@

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
# Set expected values
kwargs = {
'metadata': {},
}
self.backups_mock.update.assert_called_once_with(
self.backup.id, **kwargs
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_backup_metadata.assert_called_once_with(
self.backup, keys=['wow']
)

@@ -880,4 +873,2 @@ def test_backup_unset_property_pre_v343(self):

class TestBackupShow(volume_fakes.TestVolume):
backup = volume_fakes.create_one_backup()
columns = (

@@ -905,24 +896,2 @@ "availability_zone",

)
data = (
backup.availability_zone,
backup.container,
backup.created_at,
backup.data_timestamp,
backup.description,
backup.encryption_key_id,
backup.fail_reason,
backup.has_dependent_backups,
backup.id,
backup.is_incremental,
backup.metadata,
backup.name,
backup.object_count,
backup.project_id,
backup.size,
backup.snapshot_id,
backup.status,
backup.updated_at,
backup.user_id,
backup.volume_id,
)

@@ -932,4 +901,28 @@ def setUp(self):

self.backup = sdk_fakes.generate_fake_resource(_backup.Backup)
self.volume_sdk_client.find_backup.return_value = self.backup
# Get the command object to test
self.data = (
self.backup.availability_zone,
self.backup.container,
self.backup.created_at,
self.backup.data_timestamp,
self.backup.description,
self.backup.encryption_key_id,
self.backup.fail_reason,
self.backup.has_dependent_backups,
self.backup.id,
self.backup.is_incremental,
self.backup.metadata,
self.backup.name,
self.backup.object_count,
self.backup.project_id,
self.backup.size,
self.backup.snapshot_id,
self.backup.status,
self.backup.updated_at,
self.backup.user_id,
self.backup.volume_id,
)
self.cmd = volume_backup.ShowVolumeBackup(self.app, None)

@@ -943,5 +936,7 @@

columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.find_backup.assert_called_with(self.backup.id)
self.volume_sdk_client.find_backup.assert_called_with(
self.backup.id, ignore_missing=False
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)

@@ -16,30 +16,181 @@ #

from openstack.block_storage.v3 import snapshot as _snapshot
from openstack.block_storage.v3 import volume as _volume
from openstack import exceptions as sdk_exceptions
from openstack.test import fakes as sdk_fakes
from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes_v3
from openstackclient.tests.unit.identity.v3 import fakes as project_fakes
from openstackclient.tests.unit import utils as test_utils
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
from openstackclient.volume.v3 import volume_snapshot
class TestVolumeSnapshot(volume_fakes_v3.TestVolume):
class TestVolumeSnapshotCreate(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshots_mock = self.volume_client.volume_snapshots
self.snapshots_mock.reset_mock()
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.volume_sdk_client.find_volume.return_value = self.volume
self.snapshot = sdk_fakes.generate_fake_resource(
_snapshot.Snapshot, volume_id=self.volume.id
)
self.volume_sdk_client.create_snapshot.return_value = self.snapshot
self.volume_sdk_client.manage_snapshot.return_value = self.snapshot
self.volume_sdk_client.unmanage_snapshot.return_value = None
self.columns = (
'created_at',
'description',
'id',
'name',
'properties',
'size',
'status',
'volume_id',
)
self.data = (
self.snapshot.created_at,
self.snapshot.description,
self.snapshot.id,
self.snapshot.name,
format_columns.DictColumn(self.snapshot.metadata),
self.snapshot.size,
self.snapshot.status,
self.snapshot.volume_id,
)
self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
class TestVolumeSnapshotDelete(TestVolumeSnapshot):
snapshots = volume_fakes.create_snapshots(count=2)
def test_snapshot_create(self):
arglist = [
"--volume",
self.snapshot.volume_id,
"--description",
self.snapshot.description,
"--force",
'--property',
'Alpha=a',
'--property',
'Beta=b',
self.snapshot.name,
]
verifylist = [
("volume", self.snapshot.volume_id),
("description", self.snapshot.description),
("force", True),
('properties', {'Alpha': 'a', 'Beta': 'b'}),
("snapshot_name", self.snapshot.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.snapshot.volume_id, ignore_missing=False
)
self.volume_sdk_client.create_snapshot.assert_called_with(
volume_id=self.snapshot.volume_id,
force=True,
name=self.snapshot.name,
description=self.snapshot.description,
metadata={'Alpha': 'a', 'Beta': 'b'},
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
def test_snapshot_create_without_name(self):
arglist = [
"--volume",
self.snapshot.volume_id,
]
verifylist = [
("volume", self.snapshot.volume_id),
]
self.assertRaises(
test_utils.ParserException,
self.check_parser,
self.cmd,
arglist,
verifylist,
)
def test_snapshot_create_without_volume(self):
arglist = [
"--description",
self.snapshot.description,
"--force",
self.snapshot.name,
]
verifylist = [
("description", self.snapshot.description),
("force", True),
("snapshot_name", self.snapshot.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.snapshot.name, ignore_missing=False
)
self.volume_sdk_client.create_snapshot.assert_called_with(
volume_id=self.snapshot.volume_id,
force=True,
name=self.snapshot.name,
description=self.snapshot.description,
metadata=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
def test_snapshot_create_with_remote_source(self):
arglist = [
'--remote-source',
'source-name=test_source_name',
'--remote-source',
'source-id=test_source_id',
'--volume',
self.snapshot.volume_id,
self.snapshot.name,
]
ref_dict = {
'source-name': 'test_source_name',
'source-id': 'test_source_id',
}
verifylist = [
('remote_source', ref_dict),
('volume', self.snapshot.volume_id),
("snapshot_name", self.snapshot.name),
]
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)
self.volume_sdk_client.find_volume.assert_called_once_with(
self.snapshot.volume_id, ignore_missing=False
)
self.volume_sdk_client.manage_snapshot.assert_called_with(
volume_id=self.snapshot.volume_id,
ref=ref_dict,
name=self.snapshot.name,
description=None,
metadata=None,
)
self.volume_sdk_client.create_snapshot.assert_not_called()
class TestVolumeSnapshotDelete(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshots_mock.get = volume_fakes.get_snapshots(self.snapshots)
self.snapshots_mock.delete.return_value = None
self.snapshots = list(
sdk_fakes.generate_fake_resources(_snapshot.Snapshot)
)
self.volume_sdk_client.find_snapshot.side_effect = self.snapshots
self.volume_sdk_client.delete_snapshot.return_value = None
self.volume_sdk_client.unmanage_snapshot.return_value = None
# Get the command object to mock
self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)

@@ -53,7 +204,10 @@

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.snapshots_mock.delete.assert_called_with(
self.snapshots[0].id, False
self.volume_sdk_client.find_snapshot.assert_called_once_with(
self.snapshots[0].id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_snapshot.assert_called_once_with(
self.snapshots[0].id, force=False
)

@@ -66,7 +220,10 @@ def test_snapshot_delete_with_force(self):

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.snapshots_mock.delete.assert_called_with(
self.snapshots[0].id, True
self.volume_sdk_client.find_snapshot.assert_called_once_with(
self.snapshots[0].id, ignore_missing=False
)
self.assertIsNone(result)
self.volume_sdk_client.delete_snapshot.assert_called_once_with(
self.snapshots[0].id, force=True
)

@@ -80,13 +237,20 @@ def test_delete_multiple_snapshots(self):

]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
calls = []
for s in self.snapshots:
calls.append(mock.call(s.id, False))
self.snapshots_mock.delete.assert_has_calls(calls)
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_has_calls(
[mock.call(x.id, ignore_missing=False) for x in self.snapshots]
)
self.volume_sdk_client.delete_snapshot.assert_has_calls(
[mock.call(x.id, force=False) for x in self.snapshots]
)
def test_delete_multiple_snapshots_with_exception(self):
self.volume_sdk_client.find_snapshot.side_effect = [
self.snapshots[0],
sdk_exceptions.NotFoundException(),
]
arglist = [

@@ -102,22 +266,21 @@ self.snapshots[0].id,

find_mock_result = [self.snapshots[0], exceptions.CommandError]
with mock.patch.object(
utils, 'find_resource', side_effect=find_mock_result
) as find_mock:
try:
self.cmd.take_action(parsed_args)
self.fail('CommandError should be raised.')
except exceptions.CommandError as e:
self.assertEqual('1 of 2 snapshots failed to delete.', str(e))
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertEqual('1 of 2 snapshots failed to delete.', str(exc))
find_mock.assert_any_call(
self.snapshots_mock, self.snapshots[0].id
)
find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot')
self.volume_sdk_client.find_snapshot.assert_has_calls(
[
mock.call(self.snapshots[0].id, ignore_missing=False),
mock.call('unexist_snapshot', ignore_missing=False),
]
)
self.volume_sdk_client.delete_snapshot.assert_has_calls(
[
mock.call(self.snapshots[0].id, force=False),
]
)
self.assertEqual(2, find_mock.call_count)
self.snapshots_mock.delete.assert_called_once_with(
self.snapshots[0].id, False
)
def test_snapshot_delete_remote(self):

@@ -129,2 +292,3 @@ arglist = ['--remote', self.snapshots[0].id]

result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)

@@ -134,3 +298,2 @@ self.volume_sdk_client.unmanage_snapshot.assert_called_with(

)
self.assertIsNone(result)

@@ -159,10 +322,482 @@ def test_snapshot_delete_with_remote_force(self):

verifylist = [('remote', True), ('snapshots', arglist[1:])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volume_sdk_client.unmanage_snapshot.assert_has_calls(
[mock.call(s.id) for s in self.snapshots]
)
class TestVolumeSnapshotList(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.volume = sdk_fakes.generate_fake_resource(_volume.Volume)
self.snapshots = list(
sdk_fakes.generate_fake_resources(
_snapshot.Snapshot, attrs={'volume_id': self.volume.name}
)
)
self.project = project_fakes.FakeProject.create_one_project()
self.volume_sdk_client.volumes.return_value = [self.volume]
self.volume_sdk_client.find_volume.return_value = self.volume
self.volume_sdk_client.snapshots.return_value = self.snapshots
self.project_mock = self.identity_client.projects
self.project_mock.get.return_value = self.project
self.columns = ("ID", "Name", "Description", "Status", "Size")
self.columns_long = self.columns + (
"Created At",
"Volume",
"Properties",
)
self.data = []
self.data_long = []
for s in self.snapshots:
self.data.append(
(
s.id,
s.name,
s.description,
s.status,
s.size,
)
)
self.data_long.append(
(
s.id,
s.name,
s.description,
s.status,
s.size,
s.created_at,
volume_snapshot.VolumeIdColumn(
s.volume_id, volume_cache={self.volume.id: self.volume}
),
format_columns.DictColumn(s.metadata),
)
)
self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
def test_snapshot_list_without_options(self):
arglist = []
verifylist = [('all_projects', False), ('long', False)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
all_projects=False,
name=None,
status=None,
project_id=None,
volume_id=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
def test_snapshot_list_with_options(self):
arglist = [
"--long",
"--limit",
"2",
"--project",
self.project.id,
"--marker",
self.snapshots[0].id,
]
verifylist = [
("long", True),
("limit", 2),
("project", self.project.id),
("marker", self.snapshots[0].id),
('all_projects', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=2,
marker=self.snapshots[0].id,
all_projects=True,
project_id=self.project.id,
name=None,
status=None,
volume_id=None,
)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
def test_snapshot_list_all_projects(self):
arglist = [
'--all-projects',
]
verifylist = [('long', False), ('all_projects', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
all_projects=True,
name=None,
status=None,
project_id=None,
volume_id=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
def test_snapshot_list_name_option(self):
arglist = [
'--name',
self.snapshots[0].name,
]
verifylist = [
('all_projects', False),
('long', False),
('name', self.snapshots[0].name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
all_projects=False,
name=self.snapshots[0].name,
status=None,
project_id=None,
volume_id=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
def test_snapshot_list_status_option(self):
arglist = [
'--status',
'available',
]
verifylist = [
('all_projects', False),
('long', False),
('status', 'available'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
all_projects=False,
name=None,
status='available',
project_id=None,
volume_id=None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
def test_snapshot_list_volumeid_option(self):
arglist = [
'--volume',
self.volume.id,
]
verifylist = [
('all_projects', False),
('long', False),
('volume', self.volume.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.snapshots.assert_called_once_with(
limit=None,
marker=None,
all_projects=False,
name=None,
status=None,
project_id=None,
volume_id=self.volume.id,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
def test_snapshot_list_negative_limit(self):
arglist = [
"--limit",
"-2",
]
verifylist = [
("limit", -2),
]
self.assertRaises(
test_utils.ParserException,
self.check_parser,
self.cmd,
arglist,
verifylist,
)
class TestVolumeSnapshotSet(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshot = sdk_fakes.generate_fake_resource(
_snapshot.Snapshot, metadata={'foo': 'bar'}
)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.volume_sdk_client.set_snapshot_metadata.return_value = None
self.volume_sdk_client.update_snapshot.return_value = None
# Get the command object to mock
self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
def test_snapshot_set_no_option(self):
arglist = [
self.snapshot.id,
]
verifylist = [
("snapshot", self.snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
calls = []
for s in self.snapshots:
calls.append(mock.call(s.id))
self.volume_sdk_client.unmanage_snapshot.assert_has_calls(calls)
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_called_once_with(
parsed_args.snapshot, ignore_missing=False
)
self.volume_sdk_client.reset_snapshot_status.assert_not_called()
self.volume_sdk_client.update_snapshot.assert_not_called()
self.volume_sdk_client.set_snapshot_metadata.assert_not_called()
def test_snapshot_set_name_and_property(self):
arglist = [
"--name",
"new_snapshot",
"--property",
"x=y",
"--property",
"foo=foo",
self.snapshot.id,
]
verifylist = [
("name", "new_snapshot"),
("properties", {"x": "y", "foo": "foo"}),
("snapshot", self.snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volume_sdk_client.update_snapshot.assert_called_with(
self.snapshot.id, name="new_snapshot"
)
self.volume_sdk_client.set_snapshot_metadata.assert_called_with(
self.snapshot.id, x="y", foo="foo"
)
def test_snapshot_set_with_no_property(self):
arglist = [
"--no-property",
self.snapshot.id,
]
verifylist = [
("no_property", True),
("snapshot", self.snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_called_once_with(
parsed_args.snapshot, ignore_missing=False
)
self.volume_sdk_client.reset_snapshot_status.assert_not_called()
self.volume_sdk_client.update_snapshot.assert_not_called()
self.volume_sdk_client.set_snapshot_metadata.assert_not_called()
self.volume_sdk_client.delete_snapshot_metadata.assert_called_with(
self.snapshot.id, keys=["foo"]
)
def test_snapshot_set_with_no_property_and_property(self):
arglist = [
"--no-property",
"--property",
"foo_1=bar_1",
self.snapshot.id,
]
verifylist = [
("no_property", True),
("properties", {"foo_1": "bar_1"}),
("snapshot", self.snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volume_sdk_client.find_snapshot.assert_called_once_with(
parsed_args.snapshot, ignore_missing=False
)
self.volume_sdk_client.reset_snapshot_status.assert_not_called()
self.volume_sdk_client.update_snapshot.assert_not_called()
self.volume_sdk_client.delete_snapshot_metadata.assert_called_with(
self.snapshot.id, keys=["foo"]
)
self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with(
self.snapshot.id,
foo_1="bar_1",
)
def test_snapshot_set_state_to_error(self):
arglist = ["--state", "error", self.snapshot.id]
verifylist = [("state", "error"), ("snapshot", self.snapshot.id)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volume_sdk_client.reset_snapshot_status.assert_called_with(
self.snapshot.id, "error"
)
def test_volume_set_state_failed(self):
self.volume_sdk_client.reset_snapshot_status.side_effect = (
exceptions.CommandError()
)
arglist = ['--state', 'error', self.snapshot.id]
verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertEqual('One or more of the set operations failed', str(exc))
self.volume_sdk_client.reset_snapshot_status.assert_called_once_with(
self.snapshot.id, 'error'
)
def test_volume_set_name_and_state_failed(self):
self.volume_sdk_client.reset_snapshot_status.side_effect = (
exceptions.CommandError()
)
arglist = [
'--state',
'error',
"--name",
"new_snapshot",
self.snapshot.id,
]
verifylist = [
('state', 'error'),
("name", "new_snapshot"),
('snapshot', self.snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertEqual('One or more of the set operations failed', str(exc))
self.volume_sdk_client.update_snapshot.assert_called_once_with(
self.snapshot.id, name="new_snapshot"
)
self.volume_sdk_client.reset_snapshot_status.assert_called_once_with(
self.snapshot.id, 'error'
)
class TestVolumeSnapshotShow(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot)
self.columns = (
'created_at',
'description',
'id',
'name',
'properties',
'size',
'status',
'volume_id',
)
self.data = (
self.snapshot.created_at,
self.snapshot.description,
self.snapshot.id,
self.snapshot.name,
format_columns.DictColumn(self.snapshot.metadata),
self.snapshot.size,
self.snapshot.status,
self.snapshot.volume_id,
)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
def test_snapshot_show(self):
arglist = [self.snapshot.id]
verifylist = [("snapshot", self.snapshot.id)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.volume_sdk_client.find_snapshot.assert_called_with(
self.snapshot.id, ignore_missing=False
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
class TestVolumeSnapshotUnset(volume_fakes.TestVolume):
def setUp(self):
super().setUp()
self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot)
self.volume_sdk_client.find_snapshot.return_value = self.snapshot
self.volume_sdk_client.delete_snapshot_metadata.return_value = None
self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
def test_snapshot_unset(self):
arglist = [
"--property",
"foo",
self.snapshot.id,
]
verifylist = [
("properties", ["foo"]),
("snapshot", self.snapshot.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.volume_sdk_client.delete_snapshot_metadata.assert_called_with(
self.snapshot.id, keys=["foo"]
)

@@ -48,29 +48,30 @@ #

def take_action(self, parsed_args):
service_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
columns: tuple[str, ...] = (
"binary",
"host",
"availability_zone",
"status",
"state",
"updated_at",
)
column_names: tuple[str, ...] = (
"Binary",
"Host",
"Zone",
"Status",
"State",
"Updated At",
)
if parsed_args.long:
columns = [
"Binary",
"Host",
"Zone",
"Status",
"State",
"Updated At",
"Disabled Reason",
]
else:
columns = [
"Binary",
"Host",
"Zone",
"Status",
"State",
"Updated At",
]
columns += ("disabled_reason",)
column_names += ("Disabled Reason",)
data = service_client.services.list(
parsed_args.host, parsed_args.service
data = volume_client.services(
host=parsed_args.host, binary=parsed_args.service
)
return (
columns,
column_names,
(

@@ -91,4 +92,8 @@ utils.get_item_properties(

parser = super().get_parser(prog_name)
parser.add_argument("host", metavar="<host>", help=_("Name of host"))
parser.add_argument(
"host",
metavar="<host>",
help=_("Name of host"),
)
parser.add_argument(
"service",

@@ -123,17 +128,15 @@ metavar="<service>",

service_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
service = volume_client.find_service(
host=parsed_args.host, service=parsed_args.service
)
if parsed_args.enable:
service_client.services.enable(
parsed_args.host, parsed_args.service
service.enable(volume_client)
if parsed_args.disable:
service.disable(
volume_client,
reason=parsed_args.disable_reason,
)
if parsed_args.disable:
if parsed_args.disable_reason:
service_client.services.disable_log_reason(
parsed_args.host,
parsed_args.service,
parsed_args.disable_reason,
)
else:
service_client.services.disable(
parsed_args.host, parsed_args.service
)

@@ -420,9 +420,15 @@ #

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
backup = utils.find_resource(volume_client.backups, parsed_args.backup)
volume_client = self.app.client_manager.sdk_connection.volume
backup = volume_client.find_backup(
parsed_args.backup,
ignore_missing=False,
)
result = 0
if parsed_args.state:
try:
volume_client.backups.reset_state(backup.id, parsed_args.state)
volume_client.reset_backup_status(
backup, status=parsed_args.state
)
except Exception as e:

@@ -429,0 +435,0 @@ LOG.error(_("Failed to set backup state: %s"), e)

@@ -17,7 +17,8 @@ #

import copy
import functools
import logging
import typing as ty
from cliff import columns as cliff_columns
from openstack.block_storage.v2 import snapshot as _snapshot
from osc_lib.cli import format_columns

@@ -64,2 +65,38 @@ from osc_lib.cli import parseractions

def _format_snapshot(snapshot: _snapshot.Snapshot) -> dict[str, ty.Any]:
# Some columns returned by openstacksdk should not be shown because they're
# either irrelevant or duplicates
ignored_columns = {
# computed columns
'location',
# create-only columns
'consumes_quota',
'force',
'group_snapshot_id',
# ignored columns
'os-extended-snapshot-attributes:progress',
'os-extended-snapshot-attributes:project_id',
'updated_at',
'user_id',
# unnecessary columns
'links',
}
info = snapshot.to_dict(original_names=True)
data = {}
for key, value in info.items():
if key in ignored_columns:
continue
data[key] = value
data.update(
{
'properties': format_columns.DictColumn(data.pop('metadata')),
}
)
return data
class CreateVolumeSnapshot(command.ShowOne):

@@ -99,2 +136,3 @@ _description = _("Create new volume snapshot")

action=parseractions.KeyValueAction,
dest="properties",
help=_(

@@ -119,7 +157,9 @@ "Set a property to this snapshot "

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
volume = parsed_args.volume
if not parsed_args.volume:
volume = parsed_args.snapshot_name
volume_id = utils.find_resource(volume_client.volumes, volume).id
volume_id = volume_client.find_volume(volume, ignore_missing=False).id
if parsed_args.remote_source:

@@ -134,3 +174,4 @@ # Create a new snapshot from an existing remote snapshot source

LOG.warning(msg)
snapshot = volume_client.volume_snapshots.manage(
snapshot = volume_client.manage_snapshot(
volume_id=volume_id,

@@ -140,23 +181,18 @@ ref=parsed_args.remote_source,

description=parsed_args.description,
metadata=parsed_args.property,
metadata=parsed_args.properties,
)
else:
# create a new snapshot from scratch
snapshot = volume_client.volume_snapshots.create(
volume_id,
snapshot = volume_client.create_snapshot(
volume_id=volume_id,
force=parsed_args.force,
name=parsed_args.snapshot_name,
description=parsed_args.description,
metadata=parsed_args.property,
metadata=parsed_args.properties,
)
snapshot._info.update(
{
'properties': format_columns.DictColumn(
snapshot._info.pop('metadata')
)
}
)
return zip(*sorted(snapshot._info.items()))
data = _format_snapshot(snapshot)
return zip(*sorted(data.items()))
class DeleteVolumeSnapshot(command.Command):

@@ -184,12 +220,12 @@ _description = _("Delete volume snapshot(s)")

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
result = 0
for i in parsed_args.snapshots:
for snapshot in parsed_args.snapshots:
try:
snapshot_id = utils.find_resource(
volume_client.volume_snapshots, i
snapshot_id = volume_client.find_snapshot(
snapshot, ignore_missing=False
).id
volume_client.volume_snapshots.delete(
snapshot_id, parsed_args.force
volume_client.delete_snapshot(
snapshot_id, force=parsed_args.force
)

@@ -203,3 +239,3 @@ except Exception as e:

)
% {'snapshot': i, 'e': e}
% {'snapshot': snapshot, 'e': e}
)

@@ -271,22 +307,30 @@

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
identity_client = self.app.client_manager.identity
columns: tuple[str, ...] = (
'id',
'name',
'description',
'status',
'size',
)
column_headers: tuple[str, ...] = (
'ID',
'Name',
'Description',
'Status',
'Size',
)
if parsed_args.long:
columns = [
'ID',
'Name',
'Description',
'Status',
'Size',
columns += (
'created_at',
'volume_id',
'metadata',
)
column_headers += (
'Created At',
'Volume ID',
'Metadata',
]
column_headers = copy.deepcopy(columns)
column_headers[6] = 'Volume'
column_headers[7] = 'Properties'
else:
columns = ['ID', 'Name', 'Description', 'Status', 'Size']
column_headers = copy.deepcopy(columns)
'Volume',
'Properties',
)

@@ -296,3 +340,3 @@ # Cache the volume list

try:
for s in volume_client.volumes.list():
for s in volume_client.volumes():
volume_cache[s.id] = s

@@ -308,4 +352,4 @@ except Exception: # noqa: S110

if parsed_args.volume:
volume_id = utils.find_resource(
volume_client.volumes, parsed_args.volume
volume_id = volume_client.find_volume(
parsed_args.volume, ignore_missing=False
).id

@@ -326,14 +370,10 @@

search_opts = {
'all_tenants': all_projects,
'project_id': project_id,
'name': parsed_args.name,
'status': parsed_args.status,
'volume_id': volume_id,
}
data = volume_client.volume_snapshots.list(
search_opts=search_opts,
data = volume_client.snapshots(
marker=parsed_args.marker,
limit=parsed_args.limit,
all_projects=all_projects,
project_id=project_id,
name=parsed_args.name,
status=parsed_args.status,
volume_id=volume_id,
)

@@ -347,4 +387,4 @@ return (

formatters={
'Metadata': format_columns.DictColumn,
'Volume ID': _VolumeIdColumn,
'metadata': format_columns.DictColumn,
'volume_id': _VolumeIdColumn,
},

@@ -390,2 +430,3 @@ )

action=parseractions.KeyValueAction,
dest='properties',
help=_(

@@ -417,5 +458,6 @@ 'Property to add/change for this snapshot '

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot
volume_client = self.app.client_manager.sdk_connection.volume
snapshot = volume_client.find_snapshot(
parsed_args.snapshot, ignore_missing=False
)

@@ -426,6 +468,4 @@

try:
key_list = snapshot.metadata.keys()
volume_client.volume_snapshots.delete_metadata(
snapshot.id,
list(key_list),
volume_client.delete_snapshot_metadata(
snapshot.id, keys=list(snapshot.metadata)
)

@@ -436,6 +476,6 @@ except Exception as e:

if parsed_args.property:
if parsed_args.properties:
try:
volume_client.volume_snapshots.set_metadata(
snapshot.id, parsed_args.property
volume_client.set_snapshot_metadata(
snapshot.id, **parsed_args.properties
)

@@ -448,3 +488,3 @@ except Exception as e:

try:
volume_client.volume_snapshots.reset_state(
volume_client.reset_snapshot_status(
snapshot.id, parsed_args.state

@@ -463,3 +503,3 @@ )

try:
volume_client.volume_snapshots.update(snapshot.id, **kwargs)
volume_client.update_snapshot(snapshot.id, **kwargs)
except Exception as e:

@@ -491,16 +531,12 @@ LOG.error(

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot
volume_client = self.app.client_manager.sdk_connection.volume
snapshot = volume_client.find_snapshot(
parsed_args.snapshot, ignore_missing=False
)
snapshot._info.update(
{
'properties': format_columns.DictColumn(
snapshot._info.pop('metadata')
)
}
)
return zip(*sorted(snapshot._info.items()))
data = _format_snapshot(snapshot)
return zip(*sorted(data.items()))
class UnsetVolumeSnapshot(command.Command):

@@ -521,2 +557,3 @@ _description = _("Unset volume snapshot properties")

default=[],
dest='properties',
help=_(

@@ -530,11 +567,11 @@ 'Property to remove from snapshot '

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot
volume_client = self.app.client_manager.sdk_connection.volume
snapshot = volume_client.find_snapshot(
parsed_args.snapshot, ignore_missing=False
)
if parsed_args.property:
volume_client.volume_snapshots.delete_metadata(
snapshot.id,
parsed_args.property,
if parsed_args.properties:
volume_client.delete_snapshot_metadata(
snapshot.id, keys=parsed_args.properties
)

@@ -110,3 +110,3 @@ #

def _get_parser(self, prog_name):
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)

@@ -173,2 +173,3 @@ parser.add_argument(

action=parseractions.KeyValueAction,
dest="properties",
help=_(

@@ -194,2 +195,4 @@ "Set a property to this volume "

action="store_true",
dest="bootable",
default=None,
help=_("Mark volume as bootable"),

@@ -199,3 +202,5 @@ )

"--non-bootable",
action="store_true",
action="store_false",
dest="bootable",
default=None,
help=_("Mark volume as non-bootable (default)"),

@@ -207,2 +212,4 @@ )

action="store_true",
dest="read_only",
default=None,
help=_("Set volume to read-only access mode"),

@@ -212,13 +219,11 @@ )

"--read-write",
action="store_true",
action="store_false",
dest="read_only",
default=None,
help=_("Set volume to read-write access mode (default)"),
)
return parser, source_group
def get_parser(self, prog_name):
parser, _ = self._get_parser(prog_name)
return parser
def take_action(self, parsed_args):
CreateVolume._check_size_arg(parsed_args)
self._check_size_arg(parsed_args)
# size is validated in the above call to

@@ -274,3 +279,3 @@ # _check_size_arg where we check that size

availability_zone=parsed_args.availability_zone,
metadata=parsed_args.property,
metadata=parsed_args.properties,
imageRef=image,

@@ -282,3 +287,3 @@ source_volid=source_volume,

if parsed_args.bootable or parsed_args.non_bootable:
if parsed_args.bootable is not None:
try:

@@ -302,3 +307,4 @@ if utils.wait_for_status(

LOG.error(_("Failed to set volume bootable property: %s"), e)
if parsed_args.read_only or parsed_args.read_write:
if parsed_args.read_only is not None:
try:

@@ -370,14 +376,15 @@ if utils.wait_for_status(

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
result = 0
for i in parsed_args.volumes:
for volume in parsed_args.volumes:
try:
volume_obj = utils.find_resource(volume_client.volumes, i)
if parsed_args.force:
volume_client.volumes.force_delete(volume_obj.id)
else:
volume_client.volumes.delete(
volume_obj.id, cascade=parsed_args.purge
)
volume_obj = volume_client.find_volume(
volume, ignore_missing=False
)
volume_client.delete_volume(
volume_obj.id,
force=parsed_args.force,
cascade=parsed_args.purge,
)
except Exception as e:

@@ -390,3 +397,3 @@ result += 1

),
{'volume': i, 'e': e},
{'volume': volume, 'e': e},
)

@@ -636,2 +643,3 @@

action=parseractions.KeyValueAction,
dest="properties",
help=_(

@@ -646,2 +654,3 @@ 'Set a property on this volume '

action=parseractions.KeyValueAction,
dest="image_properties",
help=_(

@@ -723,2 +732,4 @@ 'Set an image property on this volume '

action="store_true",
dest="bootable",
default=None,
help=_("Mark volume as bootable"),

@@ -728,3 +739,5 @@ )

"--non-bootable",
action="store_true",
action="store_false",
dest="bootable",
default=None,
help=_("Mark volume as non-bootable"),

@@ -736,2 +749,4 @@ )

action="store_true",
dest="read_only",
default=None,
help=_("Set volume to read-only access mode"),

@@ -741,3 +756,5 @@ )

"--read-write",
action="store_true",
action="store_false",
dest="read_only",
default=None,
help=_("Set volume to read-write access mode"),

@@ -791,18 +808,20 @@ )

if parsed_args.property:
if parsed_args.properties:
try:
volume_client.volumes.set_metadata(
volume.id, parsed_args.property
volume.id, parsed_args.properties
)
except Exception as e:
LOG.error(_("Failed to set volume property: %s"), e)
LOG.error(_("Failed to set volume properties: %s"), e)
result += 1
if parsed_args.image_property:
if parsed_args.image_properties:
try:
volume_client.volumes.set_image_metadata(
volume.id, parsed_args.image_property
volume.id, parsed_args.image_properties
)
except Exception as e:
LOG.error(_("Failed to set image property: %s"), e)
LOG.error(_("Failed to set image properties: %s"), e)
result += 1
if parsed_args.state:

@@ -814,2 +833,3 @@ try:

result += 1
if parsed_args.attached:

@@ -823,2 +843,3 @@ try:

result += 1
if parsed_args.detached:

@@ -832,3 +853,4 @@ try:

result += 1
if parsed_args.bootable or parsed_args.non_bootable:
if parsed_args.bootable is not None:
try:

@@ -841,3 +863,4 @@ volume_client.volumes.set_bootable(

result += 1
if parsed_args.read_only or parsed_args.read_write:
if parsed_args.read_only is not None:
try:

@@ -853,2 +876,3 @@ volume_client.volumes.update_readonly_flag(

result += 1
policy = parsed_args.migration_policy or parsed_args.retype_policy

@@ -954,2 +978,3 @@ if parsed_args.type:

action='append',
dest='properties',
help=_(

@@ -964,2 +989,3 @@ 'Remove a property from volume '

action='append',
dest='image_properties',
help=_(

@@ -977,18 +1003,18 @@ 'Remove an image property from volume '

result = 0
if parsed_args.property:
if parsed_args.properties:
try:
volume_client.volumes.delete_metadata(
volume.id, parsed_args.property
volume.id, parsed_args.properties
)
except Exception as e:
LOG.error(_("Failed to unset volume property: %s"), e)
LOG.error(_("Failed to unset volume properties: %s"), e)
result += 1
if parsed_args.image_property:
if parsed_args.image_properties:
try:
volume_client.volumes.delete_image_metadata(
volume.id, parsed_args.image_property
volume.id, parsed_args.image_properties
)
except Exception as e:
LOG.error(_("Failed to unset image property: %s"), e)
LOG.error(_("Failed to unset image properties: %s"), e)
result += 1

@@ -995,0 +1021,0 @@

@@ -17,6 +17,5 @@ #

from cinderclient import api_versions
from openstack import utils as sdk_utils
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils

@@ -37,3 +36,3 @@ from openstackclient.i18n import _

metavar="<host>",
default="",
default=None,
help=_(

@@ -47,5 +46,5 @@ "List block storage service log level of specified host "

metavar="<service>",
default="",
default=None,
choices=(
'',
None,
'*',

@@ -65,3 +64,3 @@ 'cinder-api',

metavar="<log-prefix>",
default="",
default=None,
help="Prefix for the log, e.g. 'sqlalchemy'",

@@ -72,3 +71,3 @@ )

def take_action(self, parsed_args):
service_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
columns = [

@@ -81,3 +80,3 @@ "Binary",

if service_client.api_version < api_versions.APIVersion('3.32'):
if not sdk_utils.supports_microversion(volume_client, '3.32'):
msg = _(

@@ -89,18 +88,13 @@ "--os-volume-api-version 3.32 or greater is required to "

data = service_client.services.get_log_levels(
data = []
for entry in volume_client.get_service_log_levels(
binary=parsed_args.service,
server=parsed_args.host,
prefix=parsed_args.log_prefix,
)
):
entry_levels = sorted(entry.levels.items(), key=lambda x: x[0])
for prefix, level in entry_levels:
data.append((entry.binary, entry.host, prefix, level))
return (
columns,
(
utils.get_item_properties(
s,
columns,
)
for s in data
),
)
return (columns, data)

@@ -121,3 +115,3 @@

type=str.upper,
help=_("Desired log level."),
help=_("Desired log level"),
)

@@ -127,3 +121,3 @@ parser.add_argument(

metavar="<host>",
default="",
default=None,
help=_(

@@ -137,5 +131,5 @@ "Set block storage service log level of specified host "

metavar="<service>",
default="",
default=None,
choices=(
'',
None,
'*',

@@ -155,3 +149,3 @@ 'cinder-api',

metavar="<log-prefix>",
default="",
default=None,
help="Prefix for the log, e.g. 'sqlalchemy'",

@@ -162,5 +156,5 @@ )

def take_action(self, parsed_args):
service_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
if service_client.api_version < api_versions.APIVersion('3.32'):
if not sdk_utils.supports_microversion(volume_client, '3.32'):
msg = _(

@@ -172,3 +166,3 @@ "--os-volume-api-version 3.32 or greater is required to "

service_client.services.set_log_levels(
volume_client.set_service_log_levels(
level=parsed_args.level,

@@ -175,0 +169,0 @@ binary=parsed_args.service,

@@ -17,13 +17,45 @@ #

from cinderclient import api_versions
from openstack import utils as sdk_utils
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.volume.v2 import service as service_v2
from openstackclient.i18n import _
class ListService(service_v2.ListService):
class ListService(command.Lister):
_description = _("List service command")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"--host",
metavar="<host>",
help=_("List services on specified host (name only)"),
)
parser.add_argument(
"--service",
metavar="<service>",
help=_("List only specified service (name only)"),
)
parser.add_argument(
"--long",
action="store_true",
default=False,
help=_("List additional fields in output"),
)
return parser
def take_action(self, parsed_args):
service_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
columns = [
columns: tuple[str, ...] = (
"binary",
"host",
"availability_zone",
"status",
"state",
"updated_at",
)
column_names: tuple[str, ...] = (
"Binary",

@@ -35,16 +67,19 @@ "Host",

"Updated At",
]
)
if service_client.api_version >= api_versions.APIVersion('3.7'):
columns.append("Cluster")
if service_client.api_version >= api_versions.APIVersion('3.49'):
columns.append("Backend State")
if sdk_utils.supports_microversion(volume_client, '3.7'):
columns += ("cluster",)
column_names += ("Cluster",)
if sdk_utils.supports_microversion(volume_client, '3.49'):
columns += ("backend_state",)
column_names += ("Backend State",)
if parsed_args.long:
columns.append("Disabled Reason")
columns += ("disabled_reason",)
column_names += ("Disabled Reason",)
data = service_client.services.list(
parsed_args.host, parsed_args.service
data = volume_client.services(
host=parsed_args.host, binary=parsed_args.service
)
return (
columns,
column_names,
(

@@ -58,1 +93,57 @@ utils.get_item_properties(

)
class SetService(command.Command):
_description = _("Set volume service properties")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"host",
metavar="<host>",
help=_("Name of host"),
)
parser.add_argument(
"service",
metavar="<service>",
help=_("Name of service (Binary name)"),
)
enabled_group = parser.add_mutually_exclusive_group()
enabled_group.add_argument(
"--enable", action="store_true", help=_("Enable volume service")
)
enabled_group.add_argument(
"--disable", action="store_true", help=_("Disable volume service")
)
parser.add_argument(
"--disable-reason",
metavar="<reason>",
help=_(
"Reason for disabling the service "
"(should be used with --disable option)"
),
)
return parser
def take_action(self, parsed_args):
if parsed_args.disable_reason and not parsed_args.disable:
msg = _(
"Cannot specify option --disable-reason without "
"--disable specified."
)
raise exceptions.CommandError(msg)
volume_client = self.app.client_manager.sdk_connection.volume
service = volume_client.find_service(
host=parsed_args.host, service=parsed_args.service
)
if parsed_args.enable:
service.enable(volume_client)
if parsed_args.disable:
service.disable(
volume_client,
reason=parsed_args.disable_reason,
)

@@ -21,3 +21,2 @@ #

from cinderclient import api_versions
from cliff import columns as cliff_columns

@@ -516,9 +515,15 @@ from openstack import utils as sdk_utils

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
backup = utils.find_resource(volume_client.backups, parsed_args.backup)
volume_client = self.app.client_manager.sdk_connection.volume
backup = volume_client.find_backup(
parsed_args.backup,
ignore_missing=False,
)
result = 0
if parsed_args.state:
try:
volume_client.backups.reset_state(backup.id, parsed_args.state)
volume_client.reset_backup_status(
backup, status=parsed_args.state
)
except Exception as e:

@@ -531,3 +536,3 @@ LOG.error(_("Failed to set backup state: %s"), e)

if parsed_args.name:
if volume_client.api_version < api_versions.APIVersion('3.9'):
if not sdk_utils.supports_microversion(volume_client, '3.9'):
msg = _(

@@ -542,3 +547,3 @@ '--os-volume-api-version 3.9 or greater is required to '

if parsed_args.description:
if volume_client.api_version < api_versions.APIVersion('3.9'):
if not sdk_utils.supports_microversion(volume_client, '3.9'):
msg = _(

@@ -553,3 +558,3 @@ '--os-volume-api-version 3.9 or greater is required to '

if parsed_args.no_property:
if volume_client.api_version < api_versions.APIVersion('3.43'):
if not sdk_utils.supports_microversion(volume_client, '3.43'):
msg = _(

@@ -562,3 +567,3 @@ '--os-volume-api-version 3.43 or greater is required to '

if parsed_args.properties:
if volume_client.api_version < api_versions.APIVersion('3.43'):
if not sdk_utils.supports_microversion(volume_client, '3.43'):
msg = _(

@@ -570,3 +575,3 @@ '--os-volume-api-version 3.43 or greater is required to '

if volume_client.api_version >= api_versions.APIVersion('3.43'):
if sdk_utils.supports_microversion(volume_client, '3.43'):
metadata = copy.deepcopy(backup.metadata)

@@ -582,3 +587,3 @@

try:
volume_client.backups.update(backup.id, **kwargs)
volume_client.update_backup(backup, **kwargs)
except Exception as e:

@@ -619,5 +624,5 @@ LOG.error("Failed to update backup: %s", e)

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client = self.app.client_manager.sdk_connection.volume
if volume_client.api_version < api_versions.APIVersion('3.43'):
if not sdk_utils.supports_microversion(volume_client, '3.43'):
msg = _(

@@ -629,3 +634,5 @@ '--os-volume-api-version 3.43 or greater is required to '

backup = utils.find_resource(volume_client.backups, parsed_args.backup)
backup = volume_client.find_backup(
parsed_args.backup, ignore_missing=False
)
metadata = copy.deepcopy(backup.metadata)

@@ -645,9 +652,5 @@

kwargs = {
'metadata': metadata,
}
volume_client.delete_backup_metadata(backup, keys=list(metadata))
volume_client.backups.update(backup.id, **kwargs)
class ShowVolumeBackup(command.ShowOne):

@@ -667,3 +670,5 @@ _description = _("Display volume backup details")

volume_client = self.app.client_manager.sdk_connection.volume
backup = volume_client.find_backup(parsed_args.backup)
backup = volume_client.find_backup(
parsed_args.backup, ignore_missing=False
)
columns: tuple[str, ...] = (

@@ -670,0 +675,0 @@ "availability_zone",

@@ -17,4 +17,10 @@ #

import functools
import logging
import typing as ty
from cliff import columns as cliff_columns
from openstack.block_storage.v3 import snapshot as _snapshot
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command

@@ -24,3 +30,5 @@ from osc_lib import exceptions

from openstackclient.common import pagination
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common

@@ -30,2 +38,157 @@ LOG = logging.getLogger(__name__)

class VolumeIdColumn(cliff_columns.FormattableColumn):
"""Formattable column for volume ID column.
Unlike the parent FormattableColumn class, the initializer of the
class takes volume_cache as the second argument.
osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
object with a single parameter "column value", so you need to pass
a partially initialized class like
``functools.partial(VolumeIdColumn, volume_cache)``.
"""
def __init__(self, value, volume_cache=None):
super().__init__(value)
self._volume_cache = volume_cache or {}
def human_readable(self):
"""Return a volume name if available
:rtype: either the volume ID or name
"""
volume_id = self._value
volume = volume_id
if volume_id in self._volume_cache.keys():
volume = self._volume_cache[volume_id].name
return volume
def _format_snapshot(snapshot: _snapshot.Snapshot) -> dict[str, ty.Any]:
# Some columns returned by openstacksdk should not be shown because they're
# either irrelevant or duplicates
ignored_columns = {
# computed columns
'location',
# create-only columns
'consumes_quota',
'force',
'group_snapshot_id',
# ignored columns
'os-extended-snapshot-attributes:progress',
'os-extended-snapshot-attributes:project_id',
'updated_at',
'user_id',
# unnecessary columns
'links',
}
info = snapshot.to_dict(original_names=True)
data = {}
for key, value in info.items():
if key in ignored_columns:
continue
data[key] = value
data.update(
{
'properties': format_columns.DictColumn(data.pop('metadata')),
}
)
return data
class CreateVolumeSnapshot(command.ShowOne):
_description = _("Create new volume snapshot")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"snapshot_name",
metavar="<snapshot-name>",
help=_("Name of the new snapshot"),
)
parser.add_argument(
"--volume",
metavar="<volume>",
help=_(
"Volume to snapshot (name or ID) (default is <snapshot-name>)"
),
)
parser.add_argument(
"--description",
metavar="<description>",
help=_("Description of the snapshot"),
)
parser.add_argument(
"--force",
action="store_true",
default=False,
help=_(
"Create a snapshot attached to an instance. Default is False"
),
)
parser.add_argument(
"--property",
metavar="<key=value>",
dest='properties',
action=parseractions.KeyValueAction,
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 existing 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):
volume_client = self.app.client_manager.sdk_connection.volume
volume = parsed_args.volume
if not parsed_args.volume:
volume = parsed_args.snapshot_name
volume_id = volume_client.find_volume(volume, ignore_missing=False).id
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.manage_snapshot(
volume_id=volume_id,
ref=parsed_args.remote_source,
name=parsed_args.snapshot_name,
description=parsed_args.description,
metadata=parsed_args.properties,
)
else:
# Create a new snapshot from scratch
snapshot = volume_client.create_snapshot(
volume_id=volume_id,
force=parsed_args.force,
name=parsed_args.snapshot_name,
description=parsed_args.description,
metadata=parsed_args.properties,
)
data = _format_snapshot(snapshot)
return zip(*sorted(data.items()))
class DeleteVolumeSnapshot(command.Command):

@@ -61,5 +224,3 @@ _description = _("Delete volume snapshot(s)")

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client_sdk = self.app.client_manager.sdk_connection.volume
volume_client = self.app.client_manager.sdk_connection.volume
result = 0

@@ -75,12 +236,12 @@

for i in parsed_args.snapshots:
for snapshot in parsed_args.snapshots:
try:
snapshot_id = utils.find_resource(
volume_client.volume_snapshots, i
snapshot_id = volume_client.find_snapshot(
snapshot, ignore_missing=False
).id
if parsed_args.remote:
volume_client_sdk.unmanage_snapshot(snapshot_id)
volume_client.unmanage_snapshot(snapshot_id)
else:
volume_client.volume_snapshots.delete(
snapshot_id, parsed_args.force
volume_client.delete_snapshot(
snapshot_id, force=parsed_args.force
)

@@ -94,3 +255,3 @@ except Exception as e:

)
% {'snapshot': i, 'e': e}
% {'snapshot': snapshot, 'e': e}
)

@@ -105,1 +266,315 @@

raise exceptions.CommandError(msg)
class ListVolumeSnapshot(command.Lister):
_description = _("List volume snapshots")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
default=False,
help=_('Include all projects (admin only)'),
)
parser.add_argument(
'--project',
metavar='<project>',
help=_('Filter results by project (name or ID) (admin only)'),
)
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--long',
action='store_true',
default=False,
help=_('List additional fields in output'),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_('Filters results by a name.'),
)
parser.add_argument(
'--status',
metavar='<status>',
choices=[
'available',
'error',
'creating',
'deleting',
'error_deleting',
],
help=_(
"Filters results by a status. "
"('available', 'error', 'creating', 'deleting'"
" or 'error_deleting')"
),
)
parser.add_argument(
'--volume',
metavar='<volume>',
default=None,
help=_('Filters results by a volume (name or ID).'),
)
pagination.add_marker_pagination_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.sdk_connection.volume
identity_client = self.app.client_manager.identity
columns: tuple[str, ...] = (
'id',
'name',
'description',
'status',
'size',
)
column_headers: tuple[str, ...] = (
'ID',
'Name',
'Description',
'Status',
'Size',
)
if parsed_args.long:
columns += (
'created_at',
'volume_id',
'metadata',
)
column_headers += (
'Created At',
'Volume',
'Properties',
)
# Cache the volume list
volume_cache = {}
try:
for s in volume_client.volumes():
volume_cache[s.id] = s
except Exception: # noqa: S110
# Just forget it if there's any trouble
pass
_VolumeIdColumn = functools.partial(
VolumeIdColumn, volume_cache=volume_cache
)
volume_id = None
if parsed_args.volume:
volume_id = volume_client.find_volume(
parsed_args.volume, ignore_missing=False
).id
project_id = None
if parsed_args.project:
project_id = identity_common.find_project(
identity_client,
parsed_args.project,
parsed_args.project_domain,
).id
# set value of 'all_tenants' when using project option
all_projects = (
True if parsed_args.project else parsed_args.all_projects
)
data = volume_client.snapshots(
marker=parsed_args.marker,
limit=parsed_args.limit,
all_projects=all_projects,
project_id=project_id,
name=parsed_args.name,
status=parsed_args.status,
volume_id=volume_id,
)
return (
column_headers,
(
utils.get_item_properties(
s,
columns,
formatters={
'metadata': format_columns.DictColumn,
'volume_id': _VolumeIdColumn,
},
)
for s in data
),
)
class SetVolumeSnapshot(command.Command):
_description = _("Set volume snapshot properties")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'snapshot',
metavar='<snapshot>',
help=_('Snapshot to modify (name or ID)'),
)
parser.add_argument(
'--name', metavar='<name>', help=_('New snapshot name')
)
parser.add_argument(
'--description',
metavar='<description>',
help=_('New snapshot description'),
)
parser.add_argument(
"--no-property",
dest="no_property",
action="store_true",
help=_(
"Remove all properties from <snapshot> "
"(specify both --no-property and --property to "
"remove the current properties before setting "
"new properties.)"
),
)
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
dest='properties',
help=_(
'Property to add/change for this snapshot '
'(repeat option to set multiple properties)'
),
)
parser.add_argument(
'--state',
metavar='<state>',
choices=[
'available',
'error',
'creating',
'deleting',
'error_deleting',
],
help=_(
'New snapshot state. ("available", "error", "creating", '
'"deleting", or "error_deleting") (admin only) '
'(This option simply changes the state of the snapshot '
'in the database with no regard to actual status, '
'exercise caution when using)'
),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.sdk_connection.volume
snapshot = volume_client.find_snapshot(
parsed_args.snapshot, ignore_missing=False
)
result = 0
if parsed_args.no_property:
try:
volume_client.delete_snapshot_metadata(
snapshot.id, keys=list(snapshot.metadata)
)
except Exception as e:
LOG.error(_("Failed to clean snapshot properties: %s"), e)
result += 1
if parsed_args.properties:
try:
volume_client.set_snapshot_metadata(
snapshot.id, **parsed_args.properties
)
except Exception as e:
LOG.error(_("Failed to set snapshot property: %s"), e)
result += 1
if parsed_args.state:
try:
volume_client.reset_snapshot_status(
snapshot.id, parsed_args.state
)
except Exception as e:
LOG.error(_("Failed to set snapshot state: %s"), e)
result += 1
kwargs = {}
if parsed_args.name:
kwargs['name'] = parsed_args.name
if parsed_args.description:
kwargs['description'] = parsed_args.description
if kwargs:
try:
volume_client.update_snapshot(snapshot.id, **kwargs)
except Exception as e:
LOG.error(
_("Failed to update snapshot name or description: %s"),
e,
)
result += 1
if result > 0:
raise exceptions.CommandError(
_("One or more of the set operations failed")
)
class ShowVolumeSnapshot(command.ShowOne):
_description = _("Display volume snapshot details")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
"snapshot",
metavar="<snapshot>",
help=_("Snapshot to display (name or ID)"),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.sdk_connection.volume
snapshot = volume_client.find_snapshot(
parsed_args.snapshot, ignore_missing=False
)
data = _format_snapshot(snapshot)
return zip(*sorted(data.items()))
class UnsetVolumeSnapshot(command.Command):
_description = _("Unset volume snapshot properties")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument(
'snapshot',
metavar='<snapshot>',
help=_('Snapshot to modify (name or ID)'),
)
parser.add_argument(
'--property',
metavar='<key>',
dest='properties',
action='append',
default=[],
help=_(
'Property to remove from snapshot '
'(repeat option to remove multiple properties)'
),
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.sdk_connection.volume
snapshot = volume_client.find_snapshot(
parsed_args.snapshot, ignore_missing=False
)
if parsed_args.properties:
volume_client.delete_snapshot_metadata(
snapshot.id, keys=parsed_args.properties
)

@@ -34,3 +34,2 @@ #

from openstackclient.identity import common as identity_common
from openstackclient.volume.v2 import volume as volume_v2

@@ -95,3 +94,3 @@

class CreateVolume(volume_v2.CreateVolume):
class CreateVolume(command.ShowOne):
_description = _("Create new volume")

@@ -122,5 +121,45 @@

def get_parser(self, prog_name):
parser, source_group = self._get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"name",
metavar="<name>",
nargs="?",
help=_("Volume name"),
)
parser.add_argument(
"--size",
metavar="<size>",
type=int,
help=_(
"Volume size in GB (required unless --snapshot or "
"--source specified)"
),
)
parser.add_argument(
"--type",
metavar="<volume-type>",
help=_("Set the type of volume"),
)
source_group = parser.add_mutually_exclusive_group()
source_group.add_argument(
"--image",
metavar="<image>",
help=_("Use <image> as source of volume (name or ID)"),
)
source_group.add_argument(
"--snapshot",
metavar="<snapshot>",
help=_("Use <snapshot> as source of volume (name or ID)"),
)
source_group.add_argument(
"--source",
metavar="<volume>",
help=_("Volume to clone (name or ID)"),
)
source_group.add_argument(
"--source-replicated",
metavar="<replicated-volume>",
help=argparse.SUPPRESS,
)
source_group.add_argument(
"--backup",

@@ -145,2 +184,68 @@ metavar="<backup>",

parser.add_argument(
"--description",
metavar="<description>",
help=_("Volume description"),
)
parser.add_argument(
"--availability-zone",
metavar="<availability-zone>",
help=_("Create volume in <availability-zone>"),
)
parser.add_argument(
"--consistency-group",
metavar="consistency-group>",
help=_("Consistency group where the new volume belongs to"),
)
parser.add_argument(
"--property",
metavar="<key=value>",
action=parseractions.KeyValueAction,
dest="properties",
help=_(
"Set a property to this volume "
"(repeat option to set multiple properties)"
),
)
parser.add_argument(
"--hint",
metavar="<key=value>",
action=KeyValueHintAction,
help=_(
"Arbitrary scheduler hint key-value pairs to help creating "
"a volume. Repeat the option to set multiple hints. "
"'same_host' and 'different_host' get values appended when "
"repeated, all other keys take the last given value"
),
)
bootable_group = parser.add_mutually_exclusive_group()
bootable_group.add_argument(
"--bootable",
action="store_true",
dest="bootable",
default=None,
help=_("Mark volume as bootable"),
)
bootable_group.add_argument(
"--non-bootable",
action="store_false",
dest="bootable",
default=None,
help=_("Mark volume as non-bootable (default)"),
)
readonly_group = parser.add_mutually_exclusive_group()
readonly_group.add_argument(
"--read-only",
action="store_true",
dest="read_only",
default=None,
help=_("Set volume to read-only access mode"),
)
readonly_group.add_argument(
"--read-write",
action="store_false",
dest="read_only",
default=None,
help=_("Set volume to read-write access mode (default)"),
)
parser.add_argument(
"--host",

@@ -167,3 +272,3 @@ metavar="<host>",

def take_action(self, parsed_args):
CreateVolume._check_size_arg(parsed_args)
self._check_size_arg(parsed_args)
# size is validated in the above call to

@@ -202,4 +307,3 @@ # _check_size_arg where we check that size

or parsed_args.hint
or parsed_args.read_only
or parsed_args.read_write
or parsed_args.read_only is not None
):

@@ -241,6 +345,20 @@ msg = _(

availability_zone=parsed_args.availability_zone,
metadata=parsed_args.property,
metadata=parsed_args.properties,
bootable=parsed_args.bootable,
)
return zip(*sorted(volume.items()))
data = {}
for key, value in volume.to_dict().items():
# FIXME(stephenfin): Stop ignoring these once we bump SDK
# https://review.opendev.org/c/openstack/openstacksdk/+/945836/
if key in (
'cluster_name',
'consumes_quota',
'encryption_key_id',
'service_uuid',
'shared_targets',
'volume_type_id',
):
continue
data[key] = value
return zip(*sorted(data.items()))

@@ -297,3 +415,3 @@ source_volume = None

availability_zone=parsed_args.availability_zone,
metadata=parsed_args.property,
metadata=parsed_args.properties,
imageRef=image,

@@ -306,3 +424,3 @@ source_volid=source_volume,

if parsed_args.bootable or parsed_args.non_bootable:
if parsed_args.bootable is not None:
try:

@@ -326,3 +444,4 @@ if utils.wait_for_status(

LOG.error(_("Failed to set volume bootable property: %s"), e)
if parsed_args.read_only or parsed_args.read_write:
if parsed_args.read_only is not None:
try:

@@ -364,3 +483,3 @@ if utils.wait_for_status(

class DeleteVolume(volume_v2.DeleteVolume):
class DeleteVolume(command.Command):
_description = _("Delete volume(s)")

@@ -371,2 +490,24 @@

parser.add_argument(
"volumes",
metavar="<volume>",
nargs="+",
help=_("Volume(s) to delete (name or ID)"),
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"--force",
action="store_true",
help=_(
"Attempt forced removal of volume(s), regardless of state "
"(defaults to False)"
),
)
group.add_argument(
"--purge",
action="store_true",
help=_(
"Remove any snapshots along with volume(s) (defaults to False)"
),
)
parser.add_argument(
'--remote',

@@ -379,4 +520,3 @@ action='store_true',

def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
volume_client_sdk = self.app.client_manager.sdk_connection.volume
volume_client = self.app.client_manager.sdk_connection.volume
result = 0

@@ -391,12 +531,14 @@

for i in parsed_args.volumes:
for volume in parsed_args.volumes:
try:
volume_obj = utils.find_resource(volume_client.volumes, i)
volume_obj = volume_client.find_volume(
volume, ignore_missing=False
)
if parsed_args.remote:
volume_client_sdk.unmanage_volume(volume_obj.id)
elif parsed_args.force:
volume_client.volumes.force_delete(volume_obj.id)
volume_client.unmanage_volume(volume_obj.id)
else:
volume_client.volumes.delete(
volume_obj.id, cascade=parsed_args.purge
volume_client.delete_volume(
volume_obj.id,
force=parsed_args.force,
cascade=parsed_args.purge,
)

@@ -410,3 +552,3 @@ except Exception as e:

),
{'volume': i, 'e': e},
{'volume': volume, 'e': e},
)

@@ -656,2 +798,3 @@

action=parseractions.KeyValueAction,
dest='properties',
help=_(

@@ -666,2 +809,3 @@ 'Set a property on this volume '

action=parseractions.KeyValueAction,
dest='image_properties',
help=_(

@@ -743,2 +887,4 @@ 'Set an image property on this volume '

action="store_true",
dest="bootable",
default=None,
help=_("Mark volume as bootable"),

@@ -748,3 +894,5 @@ )

"--non-bootable",
action="store_true",
action="store_false",
dest="bootable",
default=None,
help=_("Mark volume as non-bootable"),

@@ -756,2 +904,4 @@ )

action="store_true",
dest="read_only",
default=None,
help=_("Set volume to read-only access mode"),

@@ -761,3 +911,5 @@ )

"--read-write",
action="store_true",
action="store_false",
dest="read_only",
default=None,
help=_("Set volume to read-write access mode"),

@@ -820,18 +972,20 @@ )

if parsed_args.property:
if parsed_args.properties:
try:
volume_client.volumes.set_metadata(
volume.id, parsed_args.property
volume.id, parsed_args.properties
)
except Exception as e:
LOG.error(_("Failed to set volume property: %s"), e)
LOG.error(_("Failed to set volume properties: %s"), e)
result += 1
if parsed_args.image_property:
if parsed_args.image_properties:
try:
volume_client.volumes.set_image_metadata(
volume.id, parsed_args.image_property
volume.id, parsed_args.image_properties
)
except Exception as e:
LOG.error(_("Failed to set image property: %s"), e)
LOG.error(_("Failed to set image properties: %s"), e)
result += 1
if parsed_args.state:

@@ -843,2 +997,3 @@ try:

result += 1
if parsed_args.attached:

@@ -852,2 +1007,3 @@ try:

result += 1
if parsed_args.detached:

@@ -861,3 +1017,4 @@ try:

result += 1
if parsed_args.bootable or parsed_args.non_bootable:
if parsed_args.bootable is not None:
try:

@@ -870,3 +1027,4 @@ volume_client.volumes.set_bootable(

result += 1
if parsed_args.read_only or parsed_args.read_write:
if parsed_args.read_only is not None:
try:

@@ -882,2 +1040,3 @@ volume_client.volumes.update_readonly_flag(

result += 1
policy = parsed_args.migration_policy or parsed_args.retype_policy

@@ -983,2 +1142,3 @@ if parsed_args.type:

action='append',
dest='properties',
help=_(

@@ -993,2 +1153,3 @@ 'Remove a property from volume '

action='append',
dest='image_properties',
help=_(

@@ -1006,18 +1167,18 @@ 'Remove an image property from volume '

result = 0
if parsed_args.property:
if parsed_args.properties:
try:
volume_client.volumes.delete_metadata(
volume.id, parsed_args.property
volume.id, parsed_args.properties
)
except Exception as e:
LOG.error(_("Failed to unset volume property: %s"), e)
LOG.error(_("Failed to unset volume properties: %s"), e)
result += 1
if parsed_args.image_property:
if parsed_args.image_properties:
try:
volume_client.volumes.delete_image_metadata(
volume.id, parsed_args.image_property
volume.id, parsed_args.image_properties
)
except Exception as e:
LOG.error(_("Failed to unset image property: %s"), e)
LOG.error(_("Failed to unset image properties: %s"), e)
result += 1

@@ -1024,0 +1185,0 @@

+150
-52
Metadata-Version: 2.1
Name: python-openstackclient
Version: 8.0.0
Version: 8.1.0
Summary: OpenStack Command-line Client

@@ -15,7 +15,7 @@ Home-page: https://docs.openstack.org/python-openstackclient/latest/

Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Description-Content-Type: text/x-rst

@@ -27,3 +27,3 @@ License-File: LICENSE

Requires-Dist: iso8601>=0.1.11
Requires-Dist: openstacksdk>=3.3.0
Requires-Dist: openstacksdk>=4.5.0
Requires-Dist: osc-lib>=2.3.0

@@ -36,6 +36,2 @@ Requires-Dist: oslo.i18n>=3.15.3

========================
Team and repository tags
========================
===============

@@ -49,5 +45,6 @@ OpenStackClient

OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
OpenStackClient (OSC) is a command-line client for OpenStack that brings
the command set for Compute, Identity, Image, Network, Object Store and Block
Storage APIs together in a single shell with a uniform command structure.
Support for additional service APIs is provided via plugins.

@@ -57,60 +54,88 @@ The primary goal is to provide a unified shell command structure and a common

* `PyPi`_ - package installation
* `Online Documentation`_
* `Launchpad project`_ - bugs and feature requests
* `Blueprints`_ - feature specifications (historical only)
* `Source`_
* `Developer`_ - getting started as a developer
* `Contributing`_ - contributing code
* `Testing`_ - testing code
* IRC: #openstack-sdks on OFTC (irc.oftc.net)
* License: Apache 2.0
.. _PyPi: https://pypi.org/project/python-openstackclient
.. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/
.. _Blueprints: https://blueprints.launchpad.net/python-openstackclient
.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient
.. _Source: https://opendev.org/openstack/python-openstackclient
.. _Developer: https://docs.openstack.org/project-team-guide/project-setup/python.html
.. _Contributing: https://docs.openstack.org/infra/manual/developers.html
.. _Testing: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html#testing
.. _Release Notes: https://docs.openstack.org/releasenotes/python-openstackclient
Getting Started
===============
OpenStack Client can be installed from PyPI using pip::
OpenStack Client can be installed from PyPI using pip:
.. code-block:: shell
python3 -m pip install python-openstackclient
There are a few variants on getting help. A list of global options and supported
commands is shown with ``--help``::
You can use ``--help`` or the ``help`` command to get a list of global options
and supported commands:
.. code-block:: shell
openstack --help
openstack help
There is also a ``help`` command that can be used to get help text for a specific
command::
You can also get help for a specific command:
openstack help
.. code-block:: shell
openstack server create --help
openstack help server create
If you want to make changes to the OpenStackClient for testing and contribution,
make any changes and then run::
You can add support for additional services by installing their clients. For
example, to add support for the DNS service (designate):
.. code-block:: shell
python3 -m pip install python3-designateclient
A ``Dockerfile`` is provided for your convenience in the repository. You can
use this to build your own container images:
.. code-block:: shell
git clone https://opendev.org/openstack/python-openstackclient
cd python-openstackclient
python3 -m pip install -e .
podman build . -t example.com/myuser/openstackclient
For more information the available options and commands, refer to the `Users
Guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html
Configuration
=============
The CLI is configured via environment variables and command-line
options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
OpenStack Client must be configured with authentication information in order to
communicate with a given OpenStack cloud. This configuration can be achieved
via a ``clouds.yaml`` file, a set of environment variables (often shared via an
``openrc`` file), a set of command-line options, or a combination of all three.
Your cloud provider or deployment tooling will typically provide either a
``clouds.yaml`` file or ``openrc`` file for you. If using a ``clouds.yaml``
file, OpenStack Client expects to find it in one of the following locations:
Authentication using username/password is most commonly used:
* If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment
variable
* ``.`` (the current directory)
* ``$HOME/.config/openstack``
* ``/etc/openstack``
- For a local user, your configuration will look like the one below::
The options you should set will depend on the configuration of your cloud and
the authentication mechanism(s) supported. For example, consider a cloud that
supports username/password authentication. Configuration for this cloud using a
``clouds.yaml`` file would look like so:
.. code-block:: yaml
clouds:
my-cloud:
auth:
auth_url: '<url-to-openstack-identity>'
project_name: '<project-name>'
project_domain_name: '<project-domain-name>'
username: '<username>'
user_domain_name: '<user-domain-name>'
password: '<password>' # (optional)
region_name: '<region>'
The corresponding environment variables would look very similar:
.. code-block:: shell
export OS_AUTH_URL=<url-to-openstack-identity>
export OS_IDENTITY_API_VERSION=3
export OS_REGION_NAME=<region>
export OS_PROJECT_NAME=<project-name>

@@ -122,6 +147,9 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name>

The corresponding command-line options look very similar::
Likewise, the corresponding command-line options would look very similar:
--os-auth-url <url>
--os-identity-api-version 3
::
openstack
--os-auth-url <url-to-openstack-identity>
--os-region <region>
--os-project-name <project-name>

@@ -133,4 +161,37 @@ --os-project-domain-name <project-domain-name>

- For a federated user, your configuration will look the so::
.. note::
If a password is not provided above (in plaintext), you will be
interactively prompted to provide one securely.
Some clouds use federated authentication. If this is the case, your
configuration will be slightly more involved. For example, to configure
username/password authentication for a federated user using a ``clouds.yaml``
file:
.. code-block:: yaml
clouds:
my-cloud:
auth:
auth_url: '<url-to-openstack-identity>'
project_name: '<project-name>'
project_domain_name: '<project-domain-name>'
username: '<username-in-idp>'
user_domain_name: '<user-domain-name>'
password: '<password-in-idp>'
identity_provider: '<the-desired-idp-in-keystone>'
client_id: '<the-client-id-configured-in-the-idp>'
client_secret: '<the-client-secret-configured-in-the-idp>'
openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>'
protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>'
access_token_type: '<the-access-token-type-used-by-your-idp>'
discovery_endpoint: '<the-well-known-endpoint-of-the-idp>'
auth_type: 'v3oidcpassword'
region_name: '<region>'
The corresponding environment variables would look very similar:
.. code-block:: shell
export OS_PROJECT_NAME=<project-name>

@@ -140,3 +201,2 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name>

export OS_IDENTITY_API_VERSION=3
export OS_AUTH_PLUGIN=openid
export OS_AUTH_TYPE=v3oidcpassword

@@ -153,4 +213,6 @@ export OS_USERNAME=<username-in-idp>

The corresponding command-line options look very similar::
Likewise, the corresponding command-line options would look very similar:
.. code-block:: shell
--os-project-name <project-name>

@@ -172,4 +234,40 @@ --os-project-domain-name <project-domain-name>

If a password is not provided above (in plaintext), you will be interactively
prompted to provide one securely.
For more information on configuring authentication, including an overview of
the many authentication mechanisms supported, refer to the `Authentication
guide`__. For more information on configuration in general, refer to the
`Configuration guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
.. __: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html
Contributing
============
You can clone the repository from opendev.org::
git clone https://opendev.org/openstack/python-openstackclient
cd python-openstackclient
OpenStack Client uses the same contributor process as other OpenStack projects.
For information on this process, including help on setting up you Gerrit
account and an overview of the CI process, refer to the `OpenStack Contributors
Guide`__.
For more information on contributing to OpenStack Client itself, including
guidance on how to design new commands and how to report bugs, refer to the
`Contributors Guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html
.. __: https://docs.opendev.org/opendev/infra-manual/latest/developers.html
Links
-----
* `Issue Tracker <https://bugs.launchpad.net/python-openstackclient>`_
* `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_
* `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_
* `PyPi <https://pypi.org/project/python-openstackclient>`_
* `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_
* `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_
* `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_
[tool.mypy]
python_version = "3.9"
python_version = "3.10"
show_column_numbers = true

@@ -25,2 +25,3 @@ show_error_context = true

line-length = 79
target-version = "py310"

@@ -27,0 +28,0 @@ [tool.ruff.format]

@@ -46,2 +46,3 @@ [console_scripts]

compute_service_set = openstackclient.compute.v2.service:SetService
console_connection_show = openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation
console_log_show = openstackclient.compute.v2.console:ShowConsoleLog

@@ -656,11 +657,11 @@ console_url_show = openstackclient.compute.v2.console:ShowConsoleURL

volume_service_list = openstackclient.volume.v3.service:ListService
volume_service_set = openstackclient.volume.v2.service:SetService
volume_service_set = openstackclient.volume.v3.service:SetService
volume_set = openstackclient.volume.v3.volume:SetVolume
volume_show = openstackclient.volume.v3.volume:ShowVolume
volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_create = openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_delete = openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot
volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot
volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot
volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot
volume_snapshot_unset = openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot
volume_snapshot_list = openstackclient.volume.v3.volume_snapshot:ListVolumeSnapshot
volume_snapshot_set = openstackclient.volume.v3.volume_snapshot:SetVolumeSnapshot
volume_snapshot_show = openstackclient.volume.v3.volume_snapshot:ShowVolumeSnapshot
volume_snapshot_unset = openstackclient.volume.v3.volume_snapshot:UnsetVolumeSnapshot
volume_summary = openstackclient.volume.v3.volume:VolumeSummary

@@ -667,0 +668,0 @@ volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest

@@ -1,1 +0,1 @@

{"git_version": "30aa27b7", "is_release": true}
{"git_version": "88b59d89", "is_release": true}
Metadata-Version: 2.1
Name: python-openstackclient
Version: 8.0.0
Version: 8.1.0
Summary: OpenStack Command-line Client

@@ -15,7 +15,7 @@ Home-page: https://docs.openstack.org/python-openstackclient/latest/

Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Description-Content-Type: text/x-rst

@@ -27,3 +27,3 @@ License-File: LICENSE

Requires-Dist: iso8601>=0.1.11
Requires-Dist: openstacksdk>=3.3.0
Requires-Dist: openstacksdk>=4.5.0
Requires-Dist: osc-lib>=2.3.0

@@ -36,6 +36,2 @@ Requires-Dist: oslo.i18n>=3.15.3

========================
Team and repository tags
========================
===============

@@ -49,5 +45,6 @@ OpenStackClient

OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
OpenStackClient (OSC) is a command-line client for OpenStack that brings
the command set for Compute, Identity, Image, Network, Object Store and Block
Storage APIs together in a single shell with a uniform command structure.
Support for additional service APIs is provided via plugins.

@@ -57,60 +54,88 @@ The primary goal is to provide a unified shell command structure and a common

* `PyPi`_ - package installation
* `Online Documentation`_
* `Launchpad project`_ - bugs and feature requests
* `Blueprints`_ - feature specifications (historical only)
* `Source`_
* `Developer`_ - getting started as a developer
* `Contributing`_ - contributing code
* `Testing`_ - testing code
* IRC: #openstack-sdks on OFTC (irc.oftc.net)
* License: Apache 2.0
.. _PyPi: https://pypi.org/project/python-openstackclient
.. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/
.. _Blueprints: https://blueprints.launchpad.net/python-openstackclient
.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient
.. _Source: https://opendev.org/openstack/python-openstackclient
.. _Developer: https://docs.openstack.org/project-team-guide/project-setup/python.html
.. _Contributing: https://docs.openstack.org/infra/manual/developers.html
.. _Testing: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html#testing
.. _Release Notes: https://docs.openstack.org/releasenotes/python-openstackclient
Getting Started
===============
OpenStack Client can be installed from PyPI using pip::
OpenStack Client can be installed from PyPI using pip:
.. code-block:: shell
python3 -m pip install python-openstackclient
There are a few variants on getting help. A list of global options and supported
commands is shown with ``--help``::
You can use ``--help`` or the ``help`` command to get a list of global options
and supported commands:
.. code-block:: shell
openstack --help
openstack help
There is also a ``help`` command that can be used to get help text for a specific
command::
You can also get help for a specific command:
openstack help
.. code-block:: shell
openstack server create --help
openstack help server create
If you want to make changes to the OpenStackClient for testing and contribution,
make any changes and then run::
You can add support for additional services by installing their clients. For
example, to add support for the DNS service (designate):
.. code-block:: shell
python3 -m pip install python3-designateclient
A ``Dockerfile`` is provided for your convenience in the repository. You can
use this to build your own container images:
.. code-block:: shell
git clone https://opendev.org/openstack/python-openstackclient
cd python-openstackclient
python3 -m pip install -e .
podman build . -t example.com/myuser/openstackclient
For more information the available options and commands, refer to the `Users
Guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html
Configuration
=============
The CLI is configured via environment variables and command-line
options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
OpenStack Client must be configured with authentication information in order to
communicate with a given OpenStack cloud. This configuration can be achieved
via a ``clouds.yaml`` file, a set of environment variables (often shared via an
``openrc`` file), a set of command-line options, or a combination of all three.
Your cloud provider or deployment tooling will typically provide either a
``clouds.yaml`` file or ``openrc`` file for you. If using a ``clouds.yaml``
file, OpenStack Client expects to find it in one of the following locations:
Authentication using username/password is most commonly used:
* If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment
variable
* ``.`` (the current directory)
* ``$HOME/.config/openstack``
* ``/etc/openstack``
- For a local user, your configuration will look like the one below::
The options you should set will depend on the configuration of your cloud and
the authentication mechanism(s) supported. For example, consider a cloud that
supports username/password authentication. Configuration for this cloud using a
``clouds.yaml`` file would look like so:
.. code-block:: yaml
clouds:
my-cloud:
auth:
auth_url: '<url-to-openstack-identity>'
project_name: '<project-name>'
project_domain_name: '<project-domain-name>'
username: '<username>'
user_domain_name: '<user-domain-name>'
password: '<password>' # (optional)
region_name: '<region>'
The corresponding environment variables would look very similar:
.. code-block:: shell
export OS_AUTH_URL=<url-to-openstack-identity>
export OS_IDENTITY_API_VERSION=3
export OS_REGION_NAME=<region>
export OS_PROJECT_NAME=<project-name>

@@ -122,6 +147,9 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name>

The corresponding command-line options look very similar::
Likewise, the corresponding command-line options would look very similar:
--os-auth-url <url>
--os-identity-api-version 3
::
openstack
--os-auth-url <url-to-openstack-identity>
--os-region <region>
--os-project-name <project-name>

@@ -133,4 +161,37 @@ --os-project-domain-name <project-domain-name>

- For a federated user, your configuration will look the so::
.. note::
If a password is not provided above (in plaintext), you will be
interactively prompted to provide one securely.
Some clouds use federated authentication. If this is the case, your
configuration will be slightly more involved. For example, to configure
username/password authentication for a federated user using a ``clouds.yaml``
file:
.. code-block:: yaml
clouds:
my-cloud:
auth:
auth_url: '<url-to-openstack-identity>'
project_name: '<project-name>'
project_domain_name: '<project-domain-name>'
username: '<username-in-idp>'
user_domain_name: '<user-domain-name>'
password: '<password-in-idp>'
identity_provider: '<the-desired-idp-in-keystone>'
client_id: '<the-client-id-configured-in-the-idp>'
client_secret: '<the-client-secret-configured-in-the-idp>'
openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>'
protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>'
access_token_type: '<the-access-token-type-used-by-your-idp>'
discovery_endpoint: '<the-well-known-endpoint-of-the-idp>'
auth_type: 'v3oidcpassword'
region_name: '<region>'
The corresponding environment variables would look very similar:
.. code-block:: shell
export OS_PROJECT_NAME=<project-name>

@@ -140,3 +201,2 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name>

export OS_IDENTITY_API_VERSION=3
export OS_AUTH_PLUGIN=openid
export OS_AUTH_TYPE=v3oidcpassword

@@ -153,4 +213,6 @@ export OS_USERNAME=<username-in-idp>

The corresponding command-line options look very similar::
Likewise, the corresponding command-line options would look very similar:
.. code-block:: shell
--os-project-name <project-name>

@@ -172,4 +234,40 @@ --os-project-domain-name <project-domain-name>

If a password is not provided above (in plaintext), you will be interactively
prompted to provide one securely.
For more information on configuring authentication, including an overview of
the many authentication mechanisms supported, refer to the `Authentication
guide`__. For more information on configuration in general, refer to the
`Configuration guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
.. __: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html
Contributing
============
You can clone the repository from opendev.org::
git clone https://opendev.org/openstack/python-openstackclient
cd python-openstackclient
OpenStack Client uses the same contributor process as other OpenStack projects.
For information on this process, including help on setting up you Gerrit
account and an overview of the CI process, refer to the `OpenStack Contributors
Guide`__.
For more information on contributing to OpenStack Client itself, including
guidance on how to design new commands and how to report bugs, refer to the
`Contributors Guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html
.. __: https://docs.opendev.org/opendev/infra-manual/latest/developers.html
Links
-----
* `Issue Tracker <https://bugs.launchpad.net/python-openstackclient>`_
* `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_
* `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_
* `PyPi <https://pypi.org/project/python-openstackclient>`_
* `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_
* `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_
* `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_

@@ -5,3 +5,3 @@ pbr!=2.1.0,>=2.0.0

iso8601>=0.1.11
openstacksdk>=3.3.0
openstacksdk>=4.5.0
osc-lib>=2.3.0

@@ -8,0 +8,0 @@ oslo.i18n>=3.15.3

+146
-48

@@ -1,5 +0,1 @@

========================
Team and repository tags
========================
===============

@@ -13,5 +9,6 @@ OpenStackClient

OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
OpenStackClient (OSC) is a command-line client for OpenStack that brings
the command set for Compute, Identity, Image, Network, Object Store and Block
Storage APIs together in a single shell with a uniform command structure.
Support for additional service APIs is provided via plugins.

@@ -21,60 +18,88 @@ The primary goal is to provide a unified shell command structure and a common

* `PyPi`_ - package installation
* `Online Documentation`_
* `Launchpad project`_ - bugs and feature requests
* `Blueprints`_ - feature specifications (historical only)
* `Source`_
* `Developer`_ - getting started as a developer
* `Contributing`_ - contributing code
* `Testing`_ - testing code
* IRC: #openstack-sdks on OFTC (irc.oftc.net)
* License: Apache 2.0
.. _PyPi: https://pypi.org/project/python-openstackclient
.. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/
.. _Blueprints: https://blueprints.launchpad.net/python-openstackclient
.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient
.. _Source: https://opendev.org/openstack/python-openstackclient
.. _Developer: https://docs.openstack.org/project-team-guide/project-setup/python.html
.. _Contributing: https://docs.openstack.org/infra/manual/developers.html
.. _Testing: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html#testing
.. _Release Notes: https://docs.openstack.org/releasenotes/python-openstackclient
Getting Started
===============
OpenStack Client can be installed from PyPI using pip::
OpenStack Client can be installed from PyPI using pip:
.. code-block:: shell
python3 -m pip install python-openstackclient
There are a few variants on getting help. A list of global options and supported
commands is shown with ``--help``::
You can use ``--help`` or the ``help`` command to get a list of global options
and supported commands:
.. code-block:: shell
openstack --help
openstack help
There is also a ``help`` command that can be used to get help text for a specific
command::
You can also get help for a specific command:
openstack help
.. code-block:: shell
openstack server create --help
openstack help server create
If you want to make changes to the OpenStackClient for testing and contribution,
make any changes and then run::
You can add support for additional services by installing their clients. For
example, to add support for the DNS service (designate):
.. code-block:: shell
python3 -m pip install python3-designateclient
A ``Dockerfile`` is provided for your convenience in the repository. You can
use this to build your own container images:
.. code-block:: shell
git clone https://opendev.org/openstack/python-openstackclient
cd python-openstackclient
python3 -m pip install -e .
podman build . -t example.com/myuser/openstackclient
For more information the available options and commands, refer to the `Users
Guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html
Configuration
=============
The CLI is configured via environment variables and command-line
options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
OpenStack Client must be configured with authentication information in order to
communicate with a given OpenStack cloud. This configuration can be achieved
via a ``clouds.yaml`` file, a set of environment variables (often shared via an
``openrc`` file), a set of command-line options, or a combination of all three.
Your cloud provider or deployment tooling will typically provide either a
``clouds.yaml`` file or ``openrc`` file for you. If using a ``clouds.yaml``
file, OpenStack Client expects to find it in one of the following locations:
Authentication using username/password is most commonly used:
* If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment
variable
* ``.`` (the current directory)
* ``$HOME/.config/openstack``
* ``/etc/openstack``
- For a local user, your configuration will look like the one below::
The options you should set will depend on the configuration of your cloud and
the authentication mechanism(s) supported. For example, consider a cloud that
supports username/password authentication. Configuration for this cloud using a
``clouds.yaml`` file would look like so:
.. code-block:: yaml
clouds:
my-cloud:
auth:
auth_url: '<url-to-openstack-identity>'
project_name: '<project-name>'
project_domain_name: '<project-domain-name>'
username: '<username>'
user_domain_name: '<user-domain-name>'
password: '<password>' # (optional)
region_name: '<region>'
The corresponding environment variables would look very similar:
.. code-block:: shell
export OS_AUTH_URL=<url-to-openstack-identity>
export OS_IDENTITY_API_VERSION=3
export OS_REGION_NAME=<region>
export OS_PROJECT_NAME=<project-name>

@@ -86,6 +111,9 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name>

The corresponding command-line options look very similar::
Likewise, the corresponding command-line options would look very similar:
--os-auth-url <url>
--os-identity-api-version 3
::
openstack
--os-auth-url <url-to-openstack-identity>
--os-region <region>
--os-project-name <project-name>

@@ -97,4 +125,37 @@ --os-project-domain-name <project-domain-name>

- For a federated user, your configuration will look the so::
.. note::
If a password is not provided above (in plaintext), you will be
interactively prompted to provide one securely.
Some clouds use federated authentication. If this is the case, your
configuration will be slightly more involved. For example, to configure
username/password authentication for a federated user using a ``clouds.yaml``
file:
.. code-block:: yaml
clouds:
my-cloud:
auth:
auth_url: '<url-to-openstack-identity>'
project_name: '<project-name>'
project_domain_name: '<project-domain-name>'
username: '<username-in-idp>'
user_domain_name: '<user-domain-name>'
password: '<password-in-idp>'
identity_provider: '<the-desired-idp-in-keystone>'
client_id: '<the-client-id-configured-in-the-idp>'
client_secret: '<the-client-secret-configured-in-the-idp>'
openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>'
protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>'
access_token_type: '<the-access-token-type-used-by-your-idp>'
discovery_endpoint: '<the-well-known-endpoint-of-the-idp>'
auth_type: 'v3oidcpassword'
region_name: '<region>'
The corresponding environment variables would look very similar:
.. code-block:: shell
export OS_PROJECT_NAME=<project-name>

@@ -104,3 +165,2 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name>

export OS_IDENTITY_API_VERSION=3
export OS_AUTH_PLUGIN=openid
export OS_AUTH_TYPE=v3oidcpassword

@@ -117,4 +177,6 @@ export OS_USERNAME=<username-in-idp>

The corresponding command-line options look very similar::
Likewise, the corresponding command-line options would look very similar:
.. code-block:: shell
--os-project-name <project-name>

@@ -136,3 +198,39 @@ --os-project-domain-name <project-domain-name>

If a password is not provided above (in plaintext), you will be interactively
prompted to provide one securely.
For more information on configuring authentication, including an overview of
the many authentication mechanisms supported, refer to the `Authentication
guide`__. For more information on configuration in general, refer to the
`Configuration guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
.. __: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html
Contributing
============
You can clone the repository from opendev.org::
git clone https://opendev.org/openstack/python-openstackclient
cd python-openstackclient
OpenStack Client uses the same contributor process as other OpenStack projects.
For information on this process, including help on setting up you Gerrit
account and an overview of the CI process, refer to the `OpenStack Contributors
Guide`__.
For more information on contributing to OpenStack Client itself, including
guidance on how to design new commands and how to report bugs, refer to the
`Contributors Guide`__.
.. __: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html
.. __: https://docs.opendev.org/opendev/infra-manual/latest/developers.html
Links
-----
* `Issue Tracker <https://bugs.launchpad.net/python-openstackclient>`_
* `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_
* `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_
* `PyPi <https://pypi.org/project/python-openstackclient>`_
* `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_
* `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_
* `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_

@@ -10,3 +10,3 @@ # Requirements lower bounds listed here are our best effort to keep them up to

iso8601>=0.1.11 # MIT
openstacksdk>=3.3.0 # Apache-2.0
openstacksdk>=4.5.0 # Apache-2.0
osc-lib>=2.3.0 # Apache-2.0

@@ -13,0 +13,0 @@ oslo.i18n>=3.15.3 # Apache-2.0

@@ -10,3 +10,3 @@ [metadata]

home_page = https://docs.openstack.org/python-openstackclient/latest/
python_requires = >=3.9
python_requires = >=3.10
classifier =

@@ -20,6 +20,6 @@ Environment :: OpenStack

Programming Language :: Python :: 3
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13

@@ -78,2 +78,4 @@ [files]

console_connection_show = openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation
flavor_create = openstackclient.compute.v2.flavor:CreateFlavor

@@ -772,8 +774,8 @@ flavor_delete = openstackclient.compute.v2.flavor:DeleteFlavor

volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_create = openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_delete = openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot
volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot
volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot
volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot
volume_snapshot_unset = openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot
volume_snapshot_list = openstackclient.volume.v3.volume_snapshot:ListVolumeSnapshot
volume_snapshot_set = openstackclient.volume.v3.volume_snapshot:SetVolumeSnapshot
volume_snapshot_show = openstackclient.volume.v3.volume_snapshot:ShowVolumeSnapshot
volume_snapshot_unset = openstackclient.volume.v3.volume_snapshot:UnsetVolumeSnapshot

@@ -797,3 +799,3 @@ volume_type_create = openstackclient.volume.v3.volume_type:CreateVolumeType

volume_service_list = openstackclient.volume.v3.service:ListService
volume_service_set = openstackclient.volume.v2.service:SetService
volume_service_set = openstackclient.volume.v3.service:SetService

@@ -800,0 +802,0 @@ volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display