Skip to content

Commit 2aa99b4

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Implement "address scope create" command"
2 parents e0573d3 + 98bee08 commit 2aa99b4

5 files changed

Lines changed: 311 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=============
2+
address scope
3+
=============
4+
5+
An **address scope** is a scope of IPv4 or IPv6 addresses that belongs
6+
to a given project and may be shared between projects.
7+
8+
Network v2
9+
10+
address scope create
11+
--------------------
12+
13+
Create new address scope
14+
15+
.. program:: address scope create
16+
.. code:: bash
17+
18+
os address scope create
19+
[--project <project> [--project-domain <project-domain>]]
20+
[--ip-version <ip-version>]
21+
[--share | --no-share]
22+
<name>
23+
24+
.. option:: --project <project>
25+
26+
Owner's project (name or ID)
27+
28+
.. option:: --project-domain <project-domain>
29+
30+
Domain the project belongs to (name or ID).
31+
This can be used in case collisions between project names exist.
32+
33+
.. option:: --ip-version <ip-version>
34+
35+
IP version (4 or 6, default is 4)
36+
37+
.. option:: --share
38+
39+
Share the address scope between projects
40+
41+
.. option:: --no-share
42+
43+
Do not share the address scope between projects (default)
44+
45+
.. _address_scope_create-name:
46+
.. describe:: <name>
47+
48+
New address scope name

doc/source/commands.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ the API resources will be merged, as in the ``quota`` object that has options
7070
referring to both Compute and Volume quotas.
7171

7272
* ``access token``: (**Identity**) long-lived OAuth-based token
73+
* ``address scope``: (**Network**) a scope of IPv4 or IPv6 addresses
7374
* ``aggregate``: (**Compute**) a grouping of compute hosts
7475
* ``availability zone``: (**Compute**, **Network**, **Volume**) a logical partition of hosts or block storage or network services
7576
* ``backup``: (**Volume**) a volume copy
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""Address scope action implementations"""
15+
16+
from openstackclient.common import command
17+
from openstackclient.common import utils
18+
from openstackclient.i18n import _
19+
from openstackclient.identity import common as identity_common
20+
21+
22+
def _get_columns(item):
23+
columns = list(item.keys())
24+
if 'tenant_id' in columns:
25+
columns.remove('tenant_id')
26+
columns.append('project_id')
27+
28+
return tuple(sorted(columns))
29+
30+
31+
def _get_attrs(client_manager, parsed_args):
32+
attrs = {}
33+
attrs['name'] = parsed_args.name
34+
attrs['ip_version'] = parsed_args.ip_version
35+
if parsed_args.share:
36+
attrs['shared'] = True
37+
if parsed_args.no_share:
38+
attrs['shared'] = False
39+
if 'project' in parsed_args and parsed_args.project is not None:
40+
identity_client = client_manager.identity
41+
project_id = identity_common.find_project(
42+
identity_client,
43+
parsed_args.project,
44+
parsed_args.project_domain,
45+
).id
46+
attrs['tenant_id'] = project_id
47+
48+
return attrs
49+
50+
51+
class CreateAddressScope(command.ShowOne):
52+
"""Create a new Address Scope"""
53+
54+
def get_parser(self, prog_name):
55+
parser = super(CreateAddressScope, self).get_parser(prog_name)
56+
parser.add_argument(
57+
'name',
58+
metavar="<name>",
59+
help=_("New address scope name")
60+
)
61+
parser.add_argument(
62+
'--ip-version',
63+
type=int,
64+
default=4,
65+
choices=[4, 6],
66+
help=_("IP version (default is 4)")
67+
)
68+
parser.add_argument(
69+
'--project',
70+
metavar="<project>",
71+
help=_("Owner's project (name or ID)")
72+
)
73+
identity_common.add_project_domain_option_to_parser(parser)
74+
75+
share_group = parser.add_mutually_exclusive_group()
76+
share_group.add_argument(
77+
'--share',
78+
action='store_true',
79+
help=_('Share the address scope between projects')
80+
)
81+
share_group.add_argument(
82+
'--no-share',
83+
action='store_true',
84+
help=_('Do not share the address scope between projects (default)')
85+
)
86+
87+
return parser
88+
89+
def take_action(self, parsed_args):
90+
client = self.app.client_manager.network
91+
attrs = _get_attrs(self.app.client_manager, parsed_args)
92+
obj = client.create_address_scope(**attrs)
93+
columns = _get_columns(obj)
94+
data = utils.get_item_properties(obj, columns, formatters={})
95+
96+
return columns, data
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
import copy
15+
import mock
16+
17+
from openstackclient.network.v2 import address_scope
18+
from openstackclient.tests import fakes
19+
from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3
20+
from openstackclient.tests.network.v2 import fakes as network_fakes
21+
from openstackclient.tests import utils as tests_utils
22+
23+
24+
class TestAddressScope(network_fakes.TestNetworkV2):
25+
26+
def setUp(self):
27+
super(TestAddressScope, self).setUp()
28+
29+
# Get a shortcut to the network client
30+
self.network = self.app.client_manager.network
31+
32+
33+
class TestCreateAddressScope(TestAddressScope):
34+
35+
# The new address scope created.
36+
new_address_scope = (
37+
network_fakes.FakeAddressScope.create_one_address_scope(
38+
attrs={
39+
'tenant_id': identity_fakes_v3.project_id,
40+
}
41+
))
42+
columns = (
43+
'id',
44+
'ip_version',
45+
'name',
46+
'project_id',
47+
'shared'
48+
)
49+
data = (
50+
new_address_scope.id,
51+
new_address_scope.ip_version,
52+
new_address_scope.name,
53+
new_address_scope.project_id,
54+
new_address_scope.shared,
55+
)
56+
57+
def setUp(self):
58+
super(TestCreateAddressScope, self).setUp()
59+
self.network.create_address_scope = mock.Mock(
60+
return_value=self.new_address_scope)
61+
62+
# Get the command object to test
63+
self.cmd = address_scope.CreateAddressScope(self.app, self.namespace)
64+
65+
# Set identity client v3. And get a shortcut to Identity client.
66+
identity_client = identity_fakes_v3.FakeIdentityv3Client(
67+
endpoint=fakes.AUTH_URL,
68+
token=fakes.AUTH_TOKEN,
69+
)
70+
self.app.client_manager.identity = identity_client
71+
self.identity = self.app.client_manager.identity
72+
73+
# Get a shortcut to the ProjectManager Mock
74+
self.projects_mock = self.identity.projects
75+
self.projects_mock.get.return_value = fakes.FakeResource(
76+
None,
77+
copy.deepcopy(identity_fakes_v3.PROJECT),
78+
loaded=True,
79+
)
80+
81+
# Get a shortcut to the DomainManager Mock
82+
self.domains_mock = self.identity.domains
83+
self.domains_mock.get.return_value = fakes.FakeResource(
84+
None,
85+
copy.deepcopy(identity_fakes_v3.DOMAIN),
86+
loaded=True,
87+
)
88+
89+
def test_create_no_options(self):
90+
arglist = []
91+
verifylist = []
92+
93+
# Missing required args should bail here
94+
self.assertRaises(tests_utils.ParserException, self.check_parser,
95+
self.cmd, arglist, verifylist)
96+
97+
def test_create_default_options(self):
98+
arglist = [
99+
self.new_address_scope.name,
100+
]
101+
verifylist = [
102+
('project', None),
103+
('ip_version', self.new_address_scope.ip_version),
104+
('name', self.new_address_scope.name),
105+
]
106+
107+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
108+
columns, data = (self.cmd.take_action(parsed_args))
109+
110+
self.network.create_address_scope.assert_called_once_with(**{
111+
'ip_version': self.new_address_scope.ip_version,
112+
'name': self.new_address_scope.name,
113+
})
114+
self.assertEqual(self.columns, columns)
115+
self.assertEqual(self.data, data)
116+
117+
def test_create_all_options(self):
118+
arglist = [
119+
'--ip-version', str(self.new_address_scope.ip_version),
120+
'--share',
121+
'--project', identity_fakes_v3.project_name,
122+
'--project-domain', identity_fakes_v3.domain_name,
123+
self.new_address_scope.name,
124+
]
125+
verifylist = [
126+
('ip_version', self.new_address_scope.ip_version),
127+
('share', True),
128+
('project', identity_fakes_v3.project_name),
129+
('project_domain', identity_fakes_v3.domain_name),
130+
('name', self.new_address_scope.name),
131+
]
132+
133+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
134+
columns, data = (self.cmd.take_action(parsed_args))
135+
136+
self.network.create_address_scope.assert_called_once_with(**{
137+
'ip_version': self.new_address_scope.ip_version,
138+
'shared': True,
139+
'tenant_id': identity_fakes_v3.project_id,
140+
'name': self.new_address_scope.name,
141+
})
142+
self.assertEqual(self.columns, columns)
143+
self.assertEqual(self.data, data)
144+
145+
def test_create_no_share(self):
146+
arglist = [
147+
'--no-share',
148+
self.new_address_scope.name,
149+
]
150+
verifylist = [
151+
('no_share', True),
152+
('name', self.new_address_scope.name),
153+
]
154+
155+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
156+
columns, data = self.cmd.take_action(parsed_args)
157+
158+
self.network.create_address_scope.assert_called_once_with(**{
159+
'ip_version': self.new_address_scope.ip_version,
160+
'shared': False,
161+
'name': self.new_address_scope.name,
162+
})
163+
self.assertEqual(self.columns, columns)
164+
self.assertEqual(self.data, data)

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ openstack.image.v2 =
324324
image_set = openstackclient.image.v2.image:SetImage
325325

326326
openstack.network.v2 =
327+
address_scope_create = openstackclient.network.v2.address_scope:CreateAddressScope
328+
327329
ip_floating_create = openstackclient.network.v2.floating_ip:CreateFloatingIP
328330
ip_floating_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP
329331
ip_floating_list = openstackclient.network.v2.floating_ip:ListFloatingIP

0 commit comments

Comments
 (0)