Browse Source

OSC Network Flavor

Implements Neutron feature of Network Flavor into OpenstackClient
This patch implements the following commands:
network flavor create
network flavor delete
network flavor list
network flavor show
network flavor set

Works with openstacksdk version 0.9.8

Change-Id: I29d7a62341010a1d067a8ca93bccb7d9b8d4c425
Partially-Implements: blueprint neutron-client-flavors
Partially-Implements: blueprint network-commands-options
tags/3.9.0
Anindita Das 2 years ago
parent
commit
edaeece7f1

+ 133
- 0
doc/source/command-objects/network-flavor.rst View File

@@ -0,0 +1,133 @@
1
+==============
2
+network flavor
3
+==============
4
+
5
+A **network flavor** extension allows the user selection of operator-curated
6
+flavors during resource creations. It allows administrators to create network
7
+service flavors.
8
+
9
+Network v2
10
+
11
+.. _network_flavor_create:
12
+network flavor create
13
+---------------------
14
+
15
+Create network flavor
16
+
17
+.. program:: network flavor create
18
+.. code:: bash
19
+
20
+    openstack network flavor create
21
+        --service-type <service-type>
22
+        [--description <description>]
23
+        [--enable | --disable]
24
+        [--project <project> [--project-domain <project-domain>]]
25
+        <name>
26
+
27
+.. option:: --service-type <service-type>
28
+
29
+    Service type to which the flavor applies to: e.g. VPN.
30
+    (See openstack :ref:'network_service_provider_list` (required)
31
+
32
+.. option:: --description <description>
33
+
34
+    Description for the flavor
35
+
36
+.. option:: --enable
37
+
38
+    Enable the flavor (default)
39
+
40
+.. option:: --disable
41
+
42
+    Disable the flavor
43
+
44
+.. option:: --project <project>
45
+
46
+    Owner's project (name or ID)
47
+
48
+.. option:: --project-domain <project-domain>
49
+
50
+    Domain the project belongs to (name or ID). This can
51
+    be used in case collisions between project names
52
+    exist.
53
+
54
+.. describe:: <name>
55
+
56
+    Name for the flavor
57
+
58
+.. _network_flavor_delete:
59
+network flavor delete
60
+---------------------
61
+
62
+Delete network flavor(s)
63
+
64
+.. program:: network flavor delete
65
+.. code:: bash
66
+
67
+    openstack network flavor delete
68
+        <flavor> [<flavor> ...]
69
+
70
+.. describe:: <flavor>
71
+
72
+    Flavor(s) to delete (name or ID)
73
+
74
+network flavor list
75
+-------------------
76
+
77
+List network flavors
78
+
79
+.. program:: network flavor list
80
+.. code:: bash
81
+
82
+    openstack network flavor list
83
+
84
+.. _network_flavor_set:
85
+network flavor set
86
+------------------
87
+
88
+Set network flavor properties
89
+
90
+.. program:: network flavor set
91
+.. code:: bash
92
+
93
+    openstack network flavor set
94
+        [--name <name>]
95
+        [--description <description>]
96
+        [--enable | --disable]
97
+        <flavor>
98
+
99
+.. option:: --name <name>
100
+
101
+    Set flavor name
102
+
103
+.. option:: --description <description>
104
+
105
+    Set network flavor description
106
+
107
+.. option:: --enable
108
+
109
+    Enable network flavor
110
+
111
+.. option:: --disable
112
+
113
+    Disable network flavor
114
+
115
+.. describe:: <flavor>
116
+
117
+    Flavor to update (name or ID)
118
+
119
+.. _network_flavor_show:
120
+network flavor show
121
+-------------------
122
+
123
+Show network flavor
124
+
125
+.. program:: network flavor show
126
+.. code:: bash
127
+
128
+    openstack network flavor show
129
+        <flavor>
130
+
131
+.. describe:: <flavor>
132
+
133
+    Flavor to display (name or ID)

+ 1
- 0
doc/source/command-objects/network-service-provider.rst View File

@@ -7,6 +7,7 @@ networking service
7 7
 
8 8
 Network v2
9 9
 
10
+.. _network_service_provider_list:
10 11
 network service provider list
11 12
 -----------------------------
12 13
 

+ 1
- 0
doc/source/commands.rst View File

@@ -111,6 +111,7 @@ referring to both Compute and Volume quotas.
111 111
 * ``module``: (**Internal**) - installed Python modules in the OSC process
112 112
 * ``network``: (**Compute**, **Network**) - a virtual network for connecting servers and other resources
113 113
 * ``network agent``: (**Network**) - A network agent is an agent that handles various tasks used to implement virtual networks
114
+* ``network flavor``: (**Network**) - allows the user to choose the type of service by a set of advertised service capabilities (e.g., LOADBALANCER, FWAAS, L3, VPN, etc) rather than by a provider type or named vendor
114 115
 * ``network meter``: (**Network**) - allow traffic metering in a network
115 116
 * ``network meter rule``: (**Network**) - rules for network traffic metering
116 117
 * ``network rbac``: (**Network**) - an RBAC policy for network resources

+ 247
- 0
openstackclient/network/v2/network_flavor.py View File

@@ -0,0 +1,247 @@
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
+"""Flavor action implementations"""
15
+
16
+import logging
17
+
18
+from osc_lib.command import command
19
+from osc_lib import exceptions
20
+from osc_lib import utils
21
+
22
+from openstackclient.i18n import _
23
+from openstackclient.identity import common as identity_common
24
+from openstackclient.network import sdk_utils
25
+
26
+
27
+LOG = logging.getLogger(__name__)
28
+
29
+
30
+def _get_columns(item):
31
+    column_map = {
32
+        'is_enabled': 'enabled',
33
+        'tenant_id': 'project_id',
34
+    }
35
+
36
+    return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
37
+
38
+
39
+def _get_attrs(client_manager, parsed_args):
40
+    attrs = {}
41
+    attrs['name'] = parsed_args.name
42
+    attrs['service_type'] = parsed_args.service_type
43
+    if parsed_args.description is not None:
44
+        attrs['description'] = parsed_args.description
45
+    if parsed_args.enable:
46
+        attrs['enabled'] = True
47
+    if parsed_args.disable:
48
+        attrs['enabled'] = False
49
+    if 'project' in parsed_args and parsed_args.project is not None:
50
+        identity_client = client_manager.identity
51
+        project_id = identity_common.find_project(
52
+            identity_client,
53
+            parsed_args.project,
54
+            parsed_args.project_domain,
55
+        ).id
56
+        attrs['tenant_id'] = project_id
57
+
58
+    return attrs
59
+
60
+
61
+# TODO(dasanind): Use the SDK resource mapped attribute names once the
62
+# OSC minimum requirements include SDK 1.0.
63
+class CreateNetworkFlavor(command.ShowOne):
64
+    _description = _("Create new network flavor")
65
+
66
+    def get_parser(self, prog_name):
67
+        parser = super(CreateNetworkFlavor, self).get_parser(prog_name)
68
+        parser.add_argument(
69
+            'name',
70
+            metavar="<name>",
71
+            help=_("Name for the flavor")
72
+        )
73
+        parser.add_argument(
74
+            '--service-type',
75
+            metavar="<service-type>",
76
+            required=True,
77
+            help=_('Service type to which the flavor applies to: e.g. VPN '
78
+                   '(See openstack network service provider list for loaded '
79
+                   'examples.)')
80
+        )
81
+        parser.add_argument(
82
+            '--description',
83
+            help=_('Description for the flavor')
84
+        )
85
+        parser.add_argument(
86
+            '--project',
87
+            metavar="<project>",
88
+            help=_("Owner's project (name or ID)")
89
+        )
90
+        identity_common.add_project_domain_option_to_parser(parser)
91
+
92
+        enable_group = parser.add_mutually_exclusive_group()
93
+        enable_group.add_argument(
94
+            '--enable',
95
+            action='store_true',
96
+            help=_("Enable the flavor (default)")
97
+        )
98
+        enable_group.add_argument(
99
+            '--disable',
100
+            action='store_true',
101
+            help=_("Disable the flavor")
102
+        )
103
+
104
+        return parser
105
+
106
+    def take_action(self, parsed_args):
107
+        client = self.app.client_manager.network
108
+        attrs = _get_attrs(self.app.client_manager, parsed_args)
109
+        obj = client.create_flavor(**attrs)
110
+        display_columns, columns = _get_columns(obj)
111
+        data = utils.get_item_properties(obj, columns, formatters={})
112
+
113
+        return (display_columns, data)
114
+
115
+
116
+class DeleteNetworkFlavor(command.Command):
117
+    _description = _("Delete network flavors")
118
+
119
+    def get_parser(self, prog_name):
120
+        parser = super(DeleteNetworkFlavor, self).get_parser(prog_name)
121
+
122
+        parser.add_argument(
123
+            'flavor',
124
+            metavar='<flavor>',
125
+            nargs='+',
126
+            help=_('Flavor(s) to delete (name or ID)')
127
+        )
128
+        return parser
129
+
130
+    def take_action(self, parsed_args):
131
+        client = self.app.client_manager.network
132
+        result = 0
133
+
134
+        for flavor in parsed_args.flavor:
135
+            try:
136
+                obj = client.find_flavor(flavor, ignore_missing=False)
137
+                client.delete_flavor(obj)
138
+            except Exception as e:
139
+                result += 1
140
+                LOG.error(_("Failed to delete flavor with "
141
+                            "name or ID '%(flavor)s': %(e)s"),
142
+                          {"flavor": flavor, "e": e})
143
+        if result > 0:
144
+            total = len(parsed_args.flavor)
145
+            msg = (_("%(result)s of %(total)s flavors failed "
146
+                     "to delete.") % {"result": result, "total": total})
147
+            raise exceptions.CommandError(msg)
148
+
149
+
150
+class ListNetworkFlavor(command.Lister):
151
+    _description = _("List network flavors")
152
+
153
+    def take_action(self, parsed_args):
154
+        client = self.app.client_manager.network
155
+
156
+        columns = (
157
+            'id',
158
+            'name',
159
+            'is_enabled',
160
+            'service_type',
161
+            'description'
162
+        )
163
+        column_headers = (
164
+            'ID',
165
+            'Name',
166
+            'Enabled',
167
+            'Service Type',
168
+            'Description'
169
+        )
170
+
171
+        data = client.flavors()
172
+        return (column_headers,
173
+                (utils.get_item_properties(
174
+                    s, columns,
175
+                ) for s in data))
176
+
177
+
178
+# TODO(dasanind): Use only the SDK resource mapped attribute names once the
179
+# OSC minimum requirements include SDK 1.0.
180
+class SetNetworkFlavor(command.Command):
181
+    _description = _("Set network flavor properties")
182
+
183
+    def get_parser(self, prog_name):
184
+        parser = super(SetNetworkFlavor, self).get_parser(prog_name)
185
+        parser.add_argument(
186
+            'flavor',
187
+            metavar="<flavor>",
188
+            help=_("Flavor to update (name or ID)")
189
+        )
190
+        parser.add_argument(
191
+            '--description',
192
+            help=_('Set network flavor description')
193
+        )
194
+        enable_group = parser.add_mutually_exclusive_group()
195
+        enable_group.add_argument(
196
+            '--disable',
197
+            action='store_true',
198
+            help=_("Disable network flavor")
199
+        )
200
+        enable_group.add_argument(
201
+            '--enable',
202
+            action='store_true',
203
+            help=_("Enable network flavor")
204
+        )
205
+        parser.add_argument(
206
+            '--name',
207
+            metavar="<name>",
208
+            help=_('Set flavor name')
209
+        )
210
+
211
+        return parser
212
+
213
+    def take_action(self, parsed_args):
214
+        client = self.app.client_manager.network
215
+        obj = client.find_flavor(
216
+            parsed_args.flavor,
217
+            ignore_missing=False)
218
+        attrs = {}
219
+        if parsed_args.name is not None:
220
+            attrs['name'] = parsed_args.name
221
+        if parsed_args.description is not None:
222
+            attrs['description'] = parsed_args.description
223
+        if parsed_args.enable:
224
+            attrs['enabled'] = True
225
+        if parsed_args.disable:
226
+            attrs['enabled'] = False
227
+        client.update_flavor(obj, **attrs)
228
+
229
+
230
+class ShowNetworkFlavor(command.ShowOne):
231
+    _description = _("Display network flavor details")
232
+
233
+    def get_parser(self, prog_name):
234
+        parser = super(ShowNetworkFlavor, self).get_parser(prog_name)
235
+        parser.add_argument(
236
+            'flavor',
237
+            metavar='<flavor>',
238
+            help=_('Flavor to display (name or ID)')
239
+        )
240
+        return parser
241
+
242
+    def take_action(self, parsed_args):
243
+        client = self.app.client_manager.network
244
+        obj = client.find_flavor(parsed_args.flavor, ignore_missing=False)
245
+        display_columns, columns = _get_columns(obj)
246
+        data = utils.get_item_properties(obj, columns)
247
+        return display_columns, data

+ 153
- 0
openstackclient/tests/functional/network/v2/test_network_flavor.py View File

@@ -0,0 +1,153 @@
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
+import re
14
+import uuid
15
+
16
+from openstackclient.tests.functional import base
17
+
18
+
19
+class NetworkFlavorTests(base.TestCase):
20
+    """Functional tests for network flavor."""
21
+
22
+    @classmethod
23
+    def setUpClass(cls):
24
+        # Set up some regex for matching below
25
+        cls.re_name = re.compile("name\s+\|\s+([^|]+?)\s+\|")
26
+        cls.re_enabled = re.compile("enabled\s+\|\s+(\S+)")
27
+        cls.re_description = re.compile("description\s+\|\s+([^|]+?)\s+\|")
28
+        cls.SERVICE_TYPE = 'L3_ROUTER_NAT'
29
+
30
+    def test_network_flavor_delete(self):
31
+        """Test create, delete multiple"""
32
+        name1 = uuid.uuid4().hex
33
+        raw_output = self.openstack(
34
+            'network flavor create --description testdescription --enable'
35
+            ' --service-type ' + self.SERVICE_TYPE + ' ' + name1,
36
+        )
37
+        self.assertEqual(
38
+            name1,
39
+            re.search(self.re_name, raw_output).group(1))
40
+        self.assertEqual(
41
+            'True',
42
+            re.search(self.re_enabled, raw_output).group(1))
43
+        self.assertEqual(
44
+            'testdescription',
45
+            re.search(self.re_description, raw_output).group(1))
46
+
47
+        name2 = uuid.uuid4().hex
48
+        raw_output = self.openstack(
49
+            'network flavor create --description testdescription1 --disable'
50
+            ' --service-type ' + self.SERVICE_TYPE + ' ' + name2,
51
+        )
52
+        self.assertEqual(
53
+            name2,
54
+            re.search(self.re_name, raw_output).group(1))
55
+        self.assertEqual(
56
+            'False',
57
+            re.search(self.re_enabled, raw_output).group(1))
58
+        self.assertEqual(
59
+            'testdescription1',
60
+            re.search(self.re_description, raw_output).group(1))
61
+
62
+        raw_output = self.openstack(
63
+            'network flavor delete ' + name1 + " " + name2)
64
+        self.assertOutput('', raw_output)
65
+
66
+    def test_network_flavor_list(self):
67
+        name1 = uuid.uuid4().hex
68
+        raw_output = self.openstack(
69
+            'network flavor create --description testdescription --enable'
70
+            ' --service-type ' + self.SERVICE_TYPE + ' ' + name1,
71
+        )
72
+        self.addCleanup(self.openstack, "network flavor delete " + name1)
73
+        self.assertEqual(
74
+            name1,
75
+            re.search(self.re_name, raw_output).group(1))
76
+        self.assertEqual(
77
+            'True',
78
+            re.search(self.re_enabled, raw_output).group(1))
79
+        self.assertEqual(
80
+            'testdescription',
81
+            re.search(self.re_description, raw_output).group(1))
82
+
83
+        name2 = uuid.uuid4().hex
84
+        raw_output = self.openstack(
85
+            'network flavor create --description testdescription --disable'
86
+            ' --service-type ' + self.SERVICE_TYPE + ' ' + name2,
87
+        )
88
+        self.assertEqual(
89
+            name2,
90
+            re.search(self.re_name, raw_output).group(1))
91
+        self.assertEqual(
92
+            'False',
93
+            re.search(self.re_enabled, raw_output).group(1))
94
+        self.assertEqual(
95
+            'testdescription',
96
+            re.search(self.re_description, raw_output).group(1))
97
+        self.addCleanup(self.openstack, "network flavor delete " + name2)
98
+
99
+        # Test list
100
+        raw_output = self.openstack('network flavor list')
101
+        self.assertIsNotNone(raw_output)
102
+        self.assertIsNotNone(re.search(name1, raw_output))
103
+        self.assertIsNotNone(re.search(name2, raw_output))
104
+
105
+    def test_network_flavor_set(self):
106
+        name = uuid.uuid4().hex
107
+        newname = name + "_"
108
+        raw_output = self.openstack(
109
+            'network flavor create --description testdescription --enable'
110
+            ' --service-type ' + self.SERVICE_TYPE + ' ' + name,
111
+        )
112
+        self.addCleanup(self.openstack, "network flavor delete " + newname)
113
+        self.assertEqual(
114
+            name,
115
+            re.search(self.re_name, raw_output).group(1))
116
+        self.assertEqual(
117
+            'True',
118
+            re.search(self.re_enabled, raw_output).group(1))
119
+        self.assertEqual(
120
+            'testdescription',
121
+            re.search(self.re_description, raw_output).group(1))
122
+
123
+        self.openstack(
124
+            'network flavor set --name ' + newname + ' --disable ' + name
125
+        )
126
+        raw_output = self.openstack('network flavor show ' + newname)
127
+        self.assertEqual(
128
+            newname,
129
+            re.search(self.re_name, raw_output).group(1))
130
+        self.assertEqual(
131
+            'False',
132
+            re.search(self.re_enabled, raw_output).group(1))
133
+        self.assertEqual(
134
+            'testdescription',
135
+            re.search(self.re_description, raw_output).group(1))
136
+
137
+    def test_network_flavor_show(self):
138
+        name = uuid.uuid4().hex
139
+        self.openstack(
140
+            'network flavor create --description testdescription --enable'
141
+            ' --service-type ' + self.SERVICE_TYPE + ' ' + name,
142
+        )
143
+        self.addCleanup(self.openstack, "network flavor delete " + name)
144
+        raw_output = self.openstack('network flavor show ' + name)
145
+        self.assertEqual(
146
+            name,
147
+            re.search(self.re_name, raw_output).group(1))
148
+        self.assertEqual(
149
+            'True',
150
+            re.search(self.re_enabled, raw_output).group(1))
151
+        self.assertEqual(
152
+            'testdescription',
153
+            re.search(self.re_description, raw_output).group(1))

+ 63
- 0
openstackclient/tests/unit/network/v2/fakes.py View File

@@ -390,6 +390,69 @@ class FakeNetwork(object):
390 390
         return mock.Mock(side_effect=networks)
391 391
 
392 392
 
393
+class FakeNetworkFlavor(object):
394
+    """Fake Network Flavor."""
395
+
396
+    @staticmethod
397
+    def create_one_network_flavor(attrs=None):
398
+        """Create a fake network flavor.
399
+
400
+        :param Dictionary attrs:
401
+            A dictionary with all attributes
402
+        :return:
403
+            A FakeResource object faking the network flavor
404
+        """
405
+        attrs = attrs or {}
406
+
407
+        fake_uuid = uuid.uuid4().hex
408
+        network_flavor_attrs = {
409
+            'description': 'network-flavor-description-' + fake_uuid,
410
+            'enabled': True,
411
+            'id': 'network-flavor-id-' + fake_uuid,
412
+            'name': 'network-flavor-name-' + fake_uuid,
413
+            'service_type': 'vpn',
414
+            'tenant_id': 'project-id-' + uuid.uuid4().hex,
415
+        }
416
+
417
+        # Overwrite default attributes.
418
+        network_flavor_attrs.update(attrs)
419
+
420
+        network_flavor = fakes.FakeResource(
421
+            info=copy.deepcopy(network_flavor_attrs),
422
+            loaded=True
423
+        )
424
+
425
+        network_flavor.project_id = network_flavor_attrs['tenant_id']
426
+        network_flavor.is_enabled = network_flavor_attrs['enabled']
427
+
428
+        return network_flavor
429
+
430
+    @staticmethod
431
+    def create_flavor(attrs=None, count=2):
432
+        """Create multiple fake network flavors.
433
+
434
+        :param Dictionary attrs:
435
+            A dictionary with all attributes
436
+        :param int count:
437
+            The number of network flavors to fake
438
+        :return:
439
+            A list of FakeResource objects faking the network falvors
440
+        """
441
+        network_flavors = []
442
+        for i in range(0, count):
443
+            network_flavors.append(
444
+                FakeNetworkFlavor.create_one_network_flavor(attrs)
445
+            )
446
+        return network_flavors
447
+
448
+    @staticmethod
449
+    def get_flavor(network_flavors=None, count=2):
450
+        """Get a list of flavors."""
451
+        if network_flavors is None:
452
+            network_flavors = (FakeNetworkFlavor.create_flavor(count))
453
+        return mock.Mock(side_effect=network_flavors)
454
+
455
+
393 456
 class FakeNetworkSegment(object):
394 457
     """Fake one or more network segments."""
395 458
 

+ 407
- 0
openstackclient/tests/unit/network/v2/test_network_flavor.py View File

@@ -0,0 +1,407 @@
1
+# Copyright (c) 2016, Intel Corporation.
2
+# All Rights Reserved.
3
+#
4
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#   not use this file except in compliance with the License. You may obtain
6
+#   a copy of the License at
7
+#
8
+#        http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#   Unless required by applicable law or agreed to in writing, software
11
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#   License for the specific language governing permissions and limitations
14
+#   under the License.
15
+#
16
+
17
+import mock
18
+
19
+from osc_lib import exceptions
20
+
21
+from openstackclient.network.v2 import network_flavor
22
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
23
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
24
+from openstackclient.tests.unit import utils as tests_utils
25
+
26
+
27
+class TestNetworkFlavor(network_fakes.TestNetworkV2):
28
+
29
+    def setUp(self):
30
+        super(TestNetworkFlavor, self).setUp()
31
+
32
+        # Get a shortcut to the network client
33
+        self.network = self.app.client_manager.network
34
+        # Get a shortcut to the ProjectManager Mock
35
+        self.projects_mock = self.app.client_manager.identity.projects
36
+        # Get a shortcut to the DomainManager Mock
37
+        self.domains_mock = self.app.client_manager.identity.domains
38
+
39
+
40
+class TestCreateNetworkFlavor(TestNetworkFlavor):
41
+
42
+    project = identity_fakes_v3.FakeProject.create_one_project()
43
+    domain = identity_fakes_v3.FakeDomain.create_one_domain()
44
+    # The new network flavor created.
45
+    new_network_flavor = (
46
+        network_fakes.FakeNetworkFlavor.create_one_network_flavor())
47
+    columns = (
48
+        'description',
49
+        'enabled',
50
+        'id',
51
+        'name',
52
+        'project_id',
53
+        'service_type'
54
+    )
55
+    data = (
56
+        new_network_flavor.description,
57
+        new_network_flavor.enabled,
58
+        new_network_flavor.id,
59
+        new_network_flavor.name,
60
+        new_network_flavor.project_id,
61
+        new_network_flavor.service_type,
62
+    )
63
+
64
+    def setUp(self):
65
+        super(TestCreateNetworkFlavor, self).setUp()
66
+        self.network.create_flavor = mock.Mock(
67
+            return_value=self.new_network_flavor)
68
+
69
+        # Get the command object to test
70
+        self.cmd = network_flavor.CreateNetworkFlavor(self.app, self.namespace)
71
+
72
+        self.projects_mock.get.return_value = self.project
73
+        self.domains_mock.get.return_value = self.domain
74
+
75
+    def test_create_no_options(self):
76
+        arglist = []
77
+        verifylist = []
78
+
79
+        # Missing required args should bail here
80
+        self.assertRaises(tests_utils.ParserException, self.check_parser,
81
+                          self.cmd, arglist, verifylist)
82
+
83
+    def test_create_default_options(self):
84
+        arglist = [
85
+            '--service-type', self.new_network_flavor.service_type,
86
+            self.new_network_flavor.name,
87
+        ]
88
+        verifylist = [
89
+            ('service_type', self.new_network_flavor.service_type),
90
+            ('name', self.new_network_flavor.name),
91
+        ]
92
+
93
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
94
+        columns, data = (self.cmd.take_action(parsed_args))
95
+
96
+        self.network.create_flavor.assert_called_once_with(**{
97
+            'service_type': self.new_network_flavor.service_type,
98
+            'name': self.new_network_flavor.name,
99
+        })
100
+        self.assertEqual(self.columns, columns)
101
+        self.assertEqual(self.data, data)
102
+
103
+    def test_create_all_options(self):
104
+        arglist = [
105
+            '--description', self.new_network_flavor.description,
106
+            '--enable',
107
+            '--project', self.new_network_flavor.project_id,
108
+            '--project-domain', self.domain.name,
109
+            '--service-type', self.new_network_flavor.service_type,
110
+            self.new_network_flavor.name,
111
+        ]
112
+        verifylist = [
113
+            ('description', self.new_network_flavor.description),
114
+            ('enable', True),
115
+            ('project', self.new_network_flavor.project_id),
116
+            ('project_domain', self.domain.name),
117
+            ('service_type', self.new_network_flavor.service_type),
118
+            ('name', self.new_network_flavor.name),
119
+        ]
120
+
121
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
122
+        columns, data = (self.cmd.take_action(parsed_args))
123
+
124
+        self.network.create_flavor.assert_called_once_with(**{
125
+            'description': self.new_network_flavor.description,
126
+            'enabled': True,
127
+            'tenant_id': self.project.id,
128
+            'service_type': self.new_network_flavor.service_type,
129
+            'name': self.new_network_flavor.name,
130
+        })
131
+        self.assertEqual(self.columns, columns)
132
+        self.assertEqual(self.data, data)
133
+
134
+    def test_create_disable(self):
135
+        arglist = [
136
+            '--disable',
137
+            '--service-type', self.new_network_flavor.service_type,
138
+            self.new_network_flavor.name,
139
+        ]
140
+        verifylist = [
141
+            ('disable', True),
142
+            ('service_type', self.new_network_flavor.service_type),
143
+            ('name', self.new_network_flavor.name),
144
+        ]
145
+
146
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
147
+        columns, data = self.cmd.take_action(parsed_args)
148
+
149
+        self.network.create_flavor.assert_called_once_with(**{
150
+            'enabled': False,
151
+            'service_type': self.new_network_flavor.service_type,
152
+            'name': self.new_network_flavor.name,
153
+        })
154
+        self.assertEqual(self.columns, columns)
155
+        self.assertEqual(self.data, data)
156
+
157
+
158
+class TestDeleteNetworkFlavor(TestNetworkFlavor):
159
+
160
+    # The network flavor to delete.
161
+    _network_flavors = (
162
+        network_fakes.FakeNetworkFlavor.create_flavor(count=2))
163
+
164
+    def setUp(self):
165
+        super(TestDeleteNetworkFlavor, self).setUp()
166
+        self.network.delete_flavor = mock.Mock(return_value=None)
167
+        self.network.find_flavor = (
168
+            network_fakes.FakeNetworkFlavor.get_flavor(
169
+                network_flavors=self._network_flavors)
170
+        )
171
+
172
+        # Get the command object to test
173
+        self.cmd = network_flavor.DeleteNetworkFlavor(self.app, self.namespace)
174
+
175
+    def test_network_flavor_delete(self):
176
+        arglist = [
177
+            self._network_flavors[0].name,
178
+        ]
179
+        verifylist = [
180
+            ('flavor', [self._network_flavors[0].name]),
181
+        ]
182
+
183
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
184
+
185
+        result = self.cmd.take_action(parsed_args)
186
+        self.network.find_flavor.assert_called_once_with(
187
+            self._network_flavors[0].name, ignore_missing=False)
188
+        self.network.delete_flavor.assert_called_once_with(
189
+            self._network_flavors[0])
190
+        self.assertIsNone(result)
191
+
192
+    def test_multi_network_flavors_delete(self):
193
+        arglist = []
194
+        verifylist = []
195
+
196
+        for a in self._network_flavors:
197
+            arglist.append(a.name)
198
+        verifylist = [
199
+            ('flavor', arglist),
200
+        ]
201
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
202
+
203
+        result = self.cmd.take_action(parsed_args)
204
+
205
+        calls = []
206
+        for a in self._network_flavors:
207
+            calls.append(mock.call(a))
208
+        self.network.delete_flavor.assert_has_calls(calls)
209
+        self.assertIsNone(result)
210
+
211
+    def test_multi_network_flavors_delete_with_exception(self):
212
+        arglist = [
213
+            self._network_flavors[0].name,
214
+            'unexist_network_flavor',
215
+        ]
216
+        verifylist = [
217
+            ('flavor',
218
+             [self._network_flavors[0].name, 'unexist_network_flavor']),
219
+        ]
220
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
221
+
222
+        find_mock_result = [self._network_flavors[0], exceptions.CommandError]
223
+        self.network.find_flavor = (
224
+            mock.Mock(side_effect=find_mock_result)
225
+        )
226
+
227
+        try:
228
+            self.cmd.take_action(parsed_args)
229
+            self.fail('CommandError should be raised.')
230
+        except exceptions.CommandError as e:
231
+            self.assertEqual('1 of 2 flavors failed to delete.', str(e))
232
+
233
+        self.network.find_flavor.assert_any_call(
234
+            self._network_flavors[0].name, ignore_missing=False)
235
+        self.network.find_flavor.assert_any_call(
236
+            'unexist_network_flavor', ignore_missing=False)
237
+        self.network.delete_flavor.assert_called_once_with(
238
+            self._network_flavors[0]
239
+        )
240
+
241
+
242
+class TestListNetworkFlavor(TestNetworkFlavor):
243
+
244
+    # The network flavors to list up.
245
+    _network_flavors = (
246
+        network_fakes.FakeNetworkFlavor.create_flavor(count=2))
247
+    columns = (
248
+        'ID',
249
+        'Name',
250
+        'Enabled',
251
+        'Service Type',
252
+        'Description',
253
+    )
254
+    data = []
255
+    for flavor in _network_flavors:
256
+        data.append((
257
+            flavor.id,
258
+            flavor.name,
259
+            flavor.enabled,
260
+            flavor.service_type,
261
+            flavor.description,
262
+        ))
263
+
264
+    def setUp(self):
265
+        super(TestListNetworkFlavor, self).setUp()
266
+        self.network.flavors = mock.Mock(
267
+            return_value=self._network_flavors)
268
+
269
+        # Get the command object to test
270
+        self.cmd = network_flavor.ListNetworkFlavor(self.app, self.namespace)
271
+
272
+    def test_network_flavor_list(self):
273
+        arglist = []
274
+        verifylist = []
275
+
276
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
277
+        columns, data = self.cmd.take_action(parsed_args)
278
+
279
+        self.network.flavors.assert_called_once_with(**{})
280
+        self.assertEqual(self.columns, columns)
281
+        self.assertEqual(self.data, list(data))
282
+
283
+
284
+class TestShowNetworkFlavor(TestNetworkFlavor):
285
+
286
+    # The network flavor to show.
287
+    new_network_flavor = (
288
+        network_fakes.FakeNetworkFlavor.create_one_network_flavor())
289
+    columns = (
290
+        'description',
291
+        'enabled',
292
+        'id',
293
+        'name',
294
+        'project_id',
295
+        'service_type'
296
+    )
297
+    data = (
298
+        new_network_flavor.description,
299
+        new_network_flavor.enabled,
300
+        new_network_flavor.id,
301
+        new_network_flavor.name,
302
+        new_network_flavor.project_id,
303
+        new_network_flavor.service_type,
304
+    )
305
+
306
+    def setUp(self):
307
+        super(TestShowNetworkFlavor, self).setUp()
308
+        self.network.find_flavor = mock.Mock(
309
+            return_value=self.new_network_flavor)
310
+
311
+        # Get the command object to test
312
+        self.cmd = network_flavor.ShowNetworkFlavor(self.app, self.namespace)
313
+
314
+    def test_show_no_options(self):
315
+        arglist = []
316
+        verifylist = []
317
+
318
+        # Missing required args should bail here
319
+        self.assertRaises(tests_utils.ParserException, self.check_parser,
320
+                          self.cmd, arglist, verifylist)
321
+
322
+    def test_show_all_options(self):
323
+        arglist = [
324
+            self.new_network_flavor.name,
325
+        ]
326
+        verifylist = [
327
+            ('flavor', self.new_network_flavor.name),
328
+        ]
329
+
330
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
331
+        columns, data = self.cmd.take_action(parsed_args)
332
+
333
+        self.network.find_flavor.assert_called_once_with(
334
+            self.new_network_flavor.name, ignore_missing=False)
335
+        self.assertEqual(self.columns, columns)
336
+        self.assertEqual(self.data, data)
337
+
338
+
339
+class TestSetNetworkFlavor(TestNetworkFlavor):
340
+
341
+    # The network flavor to set.
342
+    new_network_flavor = (
343
+        network_fakes.FakeNetworkFlavor.create_one_network_flavor())
344
+
345
+    def setUp(self):
346
+        super(TestSetNetworkFlavor, self).setUp()
347
+        self.network.update_flavor = mock.Mock(return_value=None)
348
+        self.network.find_flavor = mock.Mock(
349
+            return_value=self.new_network_flavor)
350
+
351
+        # Get the command object to test
352
+        self.cmd = network_flavor.SetNetworkFlavor(self.app, self.namespace)
353
+
354
+    def test_set_nothing(self):
355
+        arglist = [self.new_network_flavor.name, ]
356
+        verifylist = [
357
+            ('flavor', self.new_network_flavor.name),
358
+        ]
359
+
360
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
361
+        result = self.cmd.take_action(parsed_args)
362
+
363
+        attrs = {}
364
+        self.network.update_flavor.assert_called_with(
365
+            self.new_network_flavor, **attrs)
366
+        self.assertIsNone(result)
367
+
368
+    def test_set_name_and_enable(self):
369
+        arglist = [
370
+            '--name', 'new_network_flavor',
371
+            '--enable',
372
+            self.new_network_flavor.name,
373
+        ]
374
+        verifylist = [
375
+            ('name', 'new_network_flavor'),
376
+            ('enable', True),
377
+            ('flavor', self.new_network_flavor.name),
378
+        ]
379
+
380
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
381
+        result = self.cmd.take_action(parsed_args)
382
+        attrs = {
383
+            'name': "new_network_flavor",
384
+            'enabled': True,
385
+        }
386
+        self.network.update_flavor.assert_called_with(
387
+            self.new_network_flavor, **attrs)
388
+        self.assertIsNone(result)
389
+
390
+    def test_set_disable(self):
391
+        arglist = [
392
+            '--disable',
393
+            self.new_network_flavor.name,
394
+        ]
395
+        verifylist = [
396
+            ('disable', True),
397
+            ('flavor', self.new_network_flavor.name),
398
+        ]
399
+
400
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
401
+        result = self.cmd.take_action(parsed_args)
402
+        attrs = {
403
+            'enabled': False,
404
+        }
405
+        self.network.update_flavor.assert_called_with(
406
+            self.new_network_flavor, **attrs)
407
+        self.assertIsNone(result)

+ 7
- 0
releasenotes/notes/network-flavor-command-support-afe3a9da962a09bf.yaml View File

@@ -0,0 +1,7 @@
1
+---
2
+features:
3
+  - |
4
+    Add ``network flavor create``,  ``network flavor delete``,
5
+    ``network flavor list``, Add ``network flavor show`` and
6
+    ``network flavor set`` command
7
+    [Implement Neutron Flavors in OSC `https://blueprints.launchpad.net/python-openstackclient/+spec/neutron-client-flavors`]

+ 6
- 0
setup.cfg View File

@@ -360,6 +360,12 @@ openstack.network.v2 =
360 360
     network_agent_set = openstackclient.network.v2.network_agent:SetNetworkAgent
361 361
     network_agent_show = openstackclient.network.v2.network_agent:ShowNetworkAgent
362 362
 
363
+    network_flavor_create = openstackclient.network.v2.network_flavor:CreateNetworkFlavor
364
+    network_flavor_delete = openstackclient.network.v2.network_flavor:DeleteNetworkFlavor
365
+    network_flavor_list = openstackclient.network.v2.network_flavor:ListNetworkFlavor
366
+    network_flavor_set = openstackclient.network.v2.network_flavor:SetNetworkFlavor
367
+    network_flavor_show = openstackclient.network.v2.network_flavor:ShowNetworkFlavor
368
+
363 369
     network_create = openstackclient.network.v2.network:CreateNetwork
364 370
     network_delete = openstackclient.network.v2.network:DeleteNetwork
365 371
     network_list = openstackclient.network.v2.network:ListNetwork

Loading…
Cancel
Save