add image member commands for image API
This commit adds the following commands: image project add image project remove Closes-Bug: 1402420 Change-Id: I07954e9fa43a3ad6078dd939ecedf9f038299e7b
This commit is contained in:
parent
1af89f757c
commit
7bb459837b
@ -322,3 +322,57 @@ Display image details
|
||||
.. describe:: <image>
|
||||
|
||||
Image to display (name or ID)
|
||||
|
||||
image add project
|
||||
-----------------
|
||||
|
||||
*Only supported for Image v2*
|
||||
|
||||
Associate project with image
|
||||
|
||||
.. progran:: image add project
|
||||
.. code:: bash
|
||||
|
||||
os image add project
|
||||
[--project-domain <project-domain>]
|
||||
<image> <project>
|
||||
|
||||
.. option:: --project-domain <project-domain>
|
||||
|
||||
Domain the project belongs to (name or ID).
|
||||
This can be used in case collisions between project names exist.
|
||||
|
||||
.. describe:: <image>
|
||||
|
||||
Image to share (name or ID).
|
||||
|
||||
.. describe:: <project>
|
||||
|
||||
Project to associate with image (name or ID)
|
||||
|
||||
image remove project
|
||||
--------------------
|
||||
|
||||
*Only supported for Image v2*
|
||||
|
||||
Disassociate project with image
|
||||
|
||||
.. progran:: image remove project
|
||||
.. code:: bash
|
||||
|
||||
os image remove remove
|
||||
[--project-domain <project-domain>]
|
||||
<image> <project>
|
||||
|
||||
.. option:: --project-domain <project-domain>
|
||||
|
||||
Domain the project belongs to (name or ID).
|
||||
This can be used in case collisions between project names exist.
|
||||
|
||||
.. describe:: <image>
|
||||
|
||||
Image to unshare (name or ID).
|
||||
|
||||
.. describe:: <project>
|
||||
|
||||
Project to disassociate with image (name or ID)
|
||||
|
@ -27,6 +27,49 @@ from glanceclient.common import utils as gc_utils
|
||||
from openstackclient.api import utils as api_utils
|
||||
from openstackclient.common import parseractions
|
||||
from openstackclient.common import utils
|
||||
from openstackclient.identity import common
|
||||
|
||||
|
||||
class AddProjectToImage(show.ShowOne):
|
||||
"""Associate project with image"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".AddProjectToImage")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddProjectToImage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"image",
|
||||
metavar="<image>",
|
||||
help="Image to share (name or ID)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"project",
|
||||
metavar="<project>",
|
||||
help="Project to associate with image (name or ID)",
|
||||
)
|
||||
common.add_project_domain_option_to_parser(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
image_client = self.app.client_manager.image
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
project_id = common.find_project(identity_client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain).id
|
||||
|
||||
image_id = utils.find_resource(
|
||||
image_client.images,
|
||||
parsed_args.image).id
|
||||
|
||||
image_member = image_client.image_members.create(
|
||||
image_id,
|
||||
project_id,
|
||||
)
|
||||
|
||||
return zip(*sorted(six.iteritems(image_member._info)))
|
||||
|
||||
|
||||
class DeleteImage(command.Command):
|
||||
@ -192,6 +235,43 @@ class ListImage(lister.Lister):
|
||||
)
|
||||
|
||||
|
||||
class RemoveProjectImage(command.Command):
|
||||
"""Disassociate project with image"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".RemoveProjectImage")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveProjectImage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"image",
|
||||
metavar="<image>",
|
||||
help="Image to unshare (name or ID)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"project",
|
||||
metavar="<project>",
|
||||
help="Project to disassociate with image (name or ID)",
|
||||
)
|
||||
common.add_project_domain_option_to_parser(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
image_client = self.app.client_manager.image
|
||||
identity_client = self.app.client_manager.identity
|
||||
|
||||
project_id = common.find_project(identity_client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain).id
|
||||
|
||||
image_id = utils.find_resource(
|
||||
image_client.images,
|
||||
parsed_args.image).id
|
||||
|
||||
image_client.image_members.delete(image_id, project_id)
|
||||
|
||||
|
||||
class SaveImage(command.Command):
|
||||
"""Save an image locally"""
|
||||
|
||||
|
@ -18,6 +18,7 @@ import mock
|
||||
from openstackclient.tests import fakes
|
||||
from openstackclient.tests import utils
|
||||
|
||||
from openstackclient.tests.identity.v3 import fakes as identity_fakes
|
||||
|
||||
image_id = '0f41529e-7c12-4de8-be2d-181abb825b3c'
|
||||
image_name = 'graven'
|
||||
@ -36,6 +37,13 @@ IMAGE = {
|
||||
IMAGE_columns = tuple(sorted(IMAGE))
|
||||
IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE)))
|
||||
|
||||
member_status = 'pending'
|
||||
MEMBER = {
|
||||
'member_id': identity_fakes.project_id,
|
||||
'image_id': image_id,
|
||||
'status': member_status,
|
||||
}
|
||||
|
||||
# Just enough v2 schema to do some testing
|
||||
IMAGE_schema = {
|
||||
"additionalProperties": {
|
||||
@ -125,6 +133,8 @@ class FakeImagev2Client(object):
|
||||
def __init__(self, **kwargs):
|
||||
self.images = mock.Mock()
|
||||
self.images.resource_class = fakes.FakeResource(None, {})
|
||||
self.image_members = mock.Mock()
|
||||
self.image_members.resource_class = fakes.FakeResource(None, {})
|
||||
self.auth_token = kwargs['token']
|
||||
self.management_url = kwargs['endpoint']
|
||||
|
||||
@ -137,3 +147,8 @@ class TestImagev2(utils.TestCommand):
|
||||
endpoint=fakes.AUTH_URL,
|
||||
token=fakes.AUTH_TOKEN,
|
||||
)
|
||||
|
||||
self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client(
|
||||
endpoint=fakes.AUTH_URL,
|
||||
token=fakes.AUTH_TOKEN,
|
||||
)
|
||||
|
@ -21,6 +21,7 @@ import warlock
|
||||
from glanceclient.v2 import schemas
|
||||
from openstackclient.image.v2 import image
|
||||
from openstackclient.tests import fakes
|
||||
from openstackclient.tests.identity.v3 import fakes as identity_fakes
|
||||
from openstackclient.tests.image.v2 import fakes as image_fakes
|
||||
|
||||
|
||||
@ -32,6 +33,96 @@ class TestImage(image_fakes.TestImagev2):
|
||||
# Get a shortcut to the ServerManager Mock
|
||||
self.images_mock = self.app.client_manager.image.images
|
||||
self.images_mock.reset_mock()
|
||||
self.image_members_mock = self.app.client_manager.image.image_members
|
||||
self.image_members_mock.reset_mock()
|
||||
self.project_mock = self.app.client_manager.identity.projects
|
||||
self.project_mock.reset_mock()
|
||||
self.domain_mock = self.app.client_manager.identity.domains
|
||||
self.domain_mock.reset_mock()
|
||||
|
||||
|
||||
class TestAddProjectToImage(TestImage):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAddProjectToImage, self).setUp()
|
||||
|
||||
# This is the return value for utils.find_resource()
|
||||
self.images_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(image_fakes.IMAGE),
|
||||
loaded=True,
|
||||
)
|
||||
self.image_members_mock.create.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(image_fakes.MEMBER),
|
||||
loaded=True,
|
||||
)
|
||||
self.project_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.PROJECT),
|
||||
loaded=True,
|
||||
)
|
||||
self.domain_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.DOMAIN),
|
||||
loaded=True,
|
||||
)
|
||||
# Get the command object to test
|
||||
self.cmd = image.AddProjectToImage(self.app, None)
|
||||
|
||||
def test_add_project_to_image_no_option(self):
|
||||
arglist = [
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
]
|
||||
verifylist = [
|
||||
('image', image_fakes.image_id),
|
||||
('project', identity_fakes.project_id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# DisplayCommandBase.take_action() returns two tuples
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.image_members_mock.create.assert_called_with(
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id
|
||||
)
|
||||
collist = ('image_id', 'member_id', 'status')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
image_fakes.member_status
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
||||
def test_add_project_to_image_with_option(self):
|
||||
arglist = [
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
'--project-domain', identity_fakes.domain_id,
|
||||
]
|
||||
verifylist = [
|
||||
('image', image_fakes.image_id),
|
||||
('project', identity_fakes.project_id),
|
||||
('project_domain', identity_fakes.domain_id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# DisplayCommandBase.take_action() returns two tuples
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
self.image_members_mock.create.assert_called_with(
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id
|
||||
)
|
||||
collist = ('image_id', 'member_id', 'status')
|
||||
self.assertEqual(collist, columns)
|
||||
datalist = (
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
image_fakes.member_status
|
||||
)
|
||||
self.assertEqual(datalist, data)
|
||||
|
||||
|
||||
class TestImageDelete(TestImage):
|
||||
@ -298,6 +389,70 @@ class TestImageList(TestImage):
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
|
||||
class TestRemoveProjectImage(TestImage):
|
||||
|
||||
def setUp(self):
|
||||
super(TestRemoveProjectImage, self).setUp()
|
||||
|
||||
# This is the return value for utils.find_resource()
|
||||
self.images_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(image_fakes.IMAGE),
|
||||
loaded=True,
|
||||
)
|
||||
self.project_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.PROJECT),
|
||||
loaded=True,
|
||||
)
|
||||
self.domain_mock.get.return_value = fakes.FakeResource(
|
||||
None,
|
||||
copy.deepcopy(identity_fakes.DOMAIN),
|
||||
loaded=True,
|
||||
)
|
||||
self.image_members_mock.delete.return_value = None
|
||||
# Get the command object to test
|
||||
self.cmd = image.RemoveProjectImage(self.app, None)
|
||||
|
||||
def test_remove_project_image_no_options(self):
|
||||
arglist = [
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
]
|
||||
verifylist = [
|
||||
('image', image_fakes.image_id),
|
||||
('project', identity_fakes.project_id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# DisplayCommandBase.take_action() returns two tuples
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.image_members_mock.delete.assert_called_with(
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
)
|
||||
|
||||
def test_remove_project_image_with_options(self):
|
||||
arglist = [
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
'--project-domain', identity_fakes.domain_id,
|
||||
]
|
||||
verifylist = [
|
||||
('image', image_fakes.image_id),
|
||||
('project', identity_fakes.project_id),
|
||||
('project_domain', identity_fakes.domain_id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
# DisplayCommandBase.take_action() returns two tuples
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.image_members_mock.delete.assert_called_with(
|
||||
image_fakes.image_id,
|
||||
identity_fakes.project_id,
|
||||
)
|
||||
|
||||
|
||||
class TestImageShow(TestImage):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -314,8 +314,10 @@ openstack.image.v1 =
|
||||
image_show = openstackclient.image.v1.image:ShowImage
|
||||
|
||||
openstack.image.v2 =
|
||||
image_add_project = openstackclient.image.v2.image:AddProjectToImage
|
||||
image_delete = openstackclient.image.v2.image:DeleteImage
|
||||
image_list = openstackclient.image.v2.image:ListImage
|
||||
image_remove_project = openstackclient.image.v2.image:RemoveProjectImage
|
||||
image_save = openstackclient.image.v2.image:SaveImage
|
||||
image_show = openstackclient.image.v2.image:ShowImage
|
||||
image_set = openstackclient.image.v2.image:SetImage
|
||||
|
Loading…
Reference in New Issue
Block a user