diff --git a/neutronclient/neutron/v2_0/flavor/__init__.py b/neutronclient/neutron/v2_0/flavor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py new file mode 100644 index 000000000..a8a1055fa --- /dev/null +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -0,0 +1,167 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from __future__ import print_function + +import argparse + +from neutronclient.common import utils +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListFlavor(neutronV20.ListCommand): + """List Neutron service flavors.""" + + resource = 'flavor' + list_columns = ['id', 'name', 'service_type', 'enabled'] + pagination_support = True + sorting_support = True + + +class ShowFlavor(neutronV20.ShowCommand): + """Show information about a given Neutron service flavor.""" + + resource = 'flavor' + + +class CreateFlavor(neutronV20.CreateCommand): + """Create a Neutron service flavor.""" + + resource = 'flavor' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + metavar='NAME', + help=_('Name for the flavor.')) + parser.add_argument( + 'service_type', + metavar='SERVICE_TYPE', + help=_('Service type to which the flavor applies to: e.g. VPN. ' + '(See service-provider-list for loaded examples.)')) + parser.add_argument( + '--description', + help=_('Description for the flavor.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['name', 'description', 'service_type', + 'enabled']) + return {self.resource: body} + + +class DeleteFlavor(neutronV20.DeleteCommand): + """Delete a given Neutron service flavor.""" + + resource = 'flavor' + + +class UpdateFlavor(neutronV20.UpdateCommand): + """Update a Neutron service flavor.""" + + resource = 'flavor' + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name for the flavor.')) + parser.add_argument( + '--description', + help=_('Description for the flavor.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['name', 'description', 'enabled']) + return {self.resource: body} + + +class AssociateFlavor(neutronV20.NeutronCommand): + """Associate a Neutron service flavor with a flavor profile.""" + + resource = 'flavor' + + def get_parser(self, prog_name): + parser = super(AssociateFlavor, self).get_parser(prog_name) + parser.add_argument( + 'flavor', + metavar='FLAVOR', + help=_('Name or ID of the flavor to associate.')) + parser.add_argument( + 'flavor_profile', + metavar='FLAVOR_PROFILE', + help=_('ID of the flavor profile to be associated with the ' + 'flavor.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + flavor_id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'flavor', parsed_args.flavor) + service_profile_id = neutronV20.find_resourceid_by_id( + neutron_client, 'service_profile', parsed_args.flavor_profile) + body = {'service_profile': {'id': service_profile_id}} + neutron_client.associate_flavor(flavor_id, body) + print((_('Associated flavor %(flavor)s with ' + 'flavor_profile %(profile)s') % + {'flavor': parsed_args.flavor, + 'profile': parsed_args.flavor_profile}), + file=self.app.stdout) + + +class DisassociateFlavor(neutronV20.NeutronCommand): + """Disassociate a Neutron service flavor from a flavor profile.""" + + resource = 'flavor' + + def get_parser(self, prog_name): + parser = super(DisassociateFlavor, self).get_parser(prog_name) + parser.add_argument( + 'flavor', + metavar='FLAVOR', + help=_('Name or ID of the flavor.')) + parser.add_argument( + 'flavor_profile', + metavar='FLAVOR_PROFILE', + help=_('ID of the flavor profile to be disassociated from the ' + 'flavor.')) + return parser + + def run(self, parsed_args): + neutron_client = self.get_client() + neutron_client.format = parsed_args.request_format + flavor_id = neutronV20.find_resourceid_by_name_or_id( + neutron_client, 'flavor', parsed_args.flavor) + service_profile_id = neutronV20.find_resourceid_by_id( + neutron_client, 'service_profile', parsed_args.flavor_profile) + neutron_client.disassociate_flavor(flavor_id, service_profile_id) + print((_('Disassociated flavor %(flavor)s from ' + 'flavor_profile %(profile)s') % + {'flavor': parsed_args.flavor, + 'profile': parsed_args.flavor_profile}), + file=self.app.stdout) diff --git a/neutronclient/neutron/v2_0/flavor/flavor_profile.py b/neutronclient/neutron/v2_0/flavor/flavor_profile.py new file mode 100644 index 000000000..792978782 --- /dev/null +++ b/neutronclient/neutron/v2_0/flavor/flavor_profile.py @@ -0,0 +1,99 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse + +from neutronclient.common import utils +from neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronV20 + + +class ListFlavorProfile(neutronV20.ListCommand): + """List Neutron service flavor profiles.""" + + resource = 'service_profile' + list_columns = ['id', 'description', 'enabled', 'metainfo'] + pagination_support = True + sorting_support = True + + +class ShowFlavorProfile(neutronV20.ShowCommand): + """Show information about a given Neutron service flavor profile.""" + + resource = 'service_profile' + + +class CreateFlavorProfile(neutronV20.CreateCommand): + """Create a Neutron service flavor profile.""" + + resource = 'service_profile' + + def add_known_arguments(self, parser): + parser.add_argument( + '--description', + help=_('Description for the flavor profile.')) + parser.add_argument( + '--driver', + help=_('Python module path to driver.')) + parser.add_argument( + '--metainfo', + help=_('Metainfo for the flavor profile.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['description', 'driver', 'enabled', + 'metainfo']) + return {self.resource: body} + + +class DeleteFlavorProfile(neutronV20.DeleteCommand): + """Delete a given Neutron service flavor profile.""" + + resource = 'service_profile' + + +class UpdateFlavorProfile(neutronV20.UpdateCommand): + """Update a given Neutron service flavor profile.""" + + resource = 'service_profile' + + def add_known_arguments(self, parser): + parser.add_argument( + '--description', + help=_('Description for the flavor profile.')) + parser.add_argument( + '--driver', + help=_('Python module path to driver.')) + parser.add_argument( + '--metainfo', + help=_('Metainfo for the flavor profile.')) + utils.add_boolean_argument( + parser, + '--enabled', + default=argparse.SUPPRESS, + help=_('Sets enabled flag.')) + + def args2body(self, parsed_args): + body = {} + neutronV20.update_dict(parsed_args, body, + ['description', 'driver', 'enabled', + 'metainfo']) + return {self.resource: body} diff --git a/neutronclient/shell.py b/neutronclient/shell.py index dc15c71b7..f8874088c 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -45,6 +45,8 @@ from neutronclient.neutron.v2_0 import agent from neutronclient.neutron.v2_0 import agentscheduler from neutronclient.neutron.v2_0 import credential from neutronclient.neutron.v2_0 import extension +from neutronclient.neutron.v2_0.flavor import flavor +from neutronclient.neutron.v2_0.flavor import flavor_profile from neutronclient.neutron.v2_0 import floatingip from neutronclient.neutron.v2_0.fw import firewall from neutronclient.neutron.v2_0.fw import firewallpolicy @@ -391,6 +393,18 @@ COMMAND_V2 = { bandwidth_limit_rule.DeleteQoSBandwidthLimitRule ), 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, + 'flavor-list': flavor.ListFlavor, + 'flavor-show': flavor.ShowFlavor, + 'flavor-create': flavor.CreateFlavor, + 'flavor-delete': flavor.DeleteFlavor, + 'flavor-update': flavor.UpdateFlavor, + 'flavor-associate': flavor.AssociateFlavor, + 'flavor-disassociate': flavor.DisassociateFlavor, + 'flavor-profile-list': flavor_profile.ListFlavorProfile, + 'flavor-profile-show': flavor_profile.ShowFlavorProfile, + 'flavor-profile-create': flavor_profile.CreateFlavorProfile, + 'flavor-profile-delete': flavor_profile.DeleteFlavorProfile, + 'flavor-profile-update': flavor_profile.UpdateFlavorProfile, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/flavor/__init__.py b/neutronclient/tests/unit/flavor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor.py b/neutronclient/tests/unit/flavor/test_cli20_flavor.py new file mode 100644 index 000000000..22bd8bb9b --- /dev/null +++ b/neutronclient/tests/unit/flavor/test_cli20_flavor.py @@ -0,0 +1,154 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import sys + +from neutronclient.neutron.v2_0.flavor import flavor +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20FlavorJSON(test_cli20.CLITestV20Base): + + def setUp(self): + """Prepare test environment.""" + super(CLITestV20FlavorJSON, self).setUp(plurals={'flavors': 'flavor'}) + self.register_non_admin_status_resource('flavor') + self.register_non_admin_status_resource('service_profile') + + def test_create_flavor_with_missing_params(self): + """Create test flavor with missing parameters.""" + resource = 'flavor' + cmd = flavor.CreateFlavor( + test_cli20.MyApp(sys.stdout), None) + name = 'Test flavor' + myid = 'myid' + position_names = [] + position_values = [] + args = [] + self.assertRaises( + SystemExit, self._test_create_resource, + resource, cmd, name, myid, args, position_names, position_values) + + def test_create_flavor_with_mandatory_params(self): + """Create test flavor with minimal parameters.""" + resource = 'flavor' + cmd = flavor.CreateFlavor( + test_cli20.MyApp(sys.stdout), None) + name = 'Test flavor' + myid = 'myid' + service_type = 'DUMMY' + # Defaults are returned in body + position_names = ['name', 'service_type'] + position_values = [name, service_type] + args = [name, service_type] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_flavor_with_optional_params(self): + """Create test flavor including optional parameters.""" + resource = 'flavor' + cmd = flavor.CreateFlavor( + test_cli20.MyApp(sys.stdout), None) + name = 'Test flavor' + myid = 'myid' + service_type = 'DUMMY' + description = 'Test description' + position_names = ['name', 'service_type', 'description', 'enabled'] + position_values = [name, service_type, description, 'False'] + args = [name, service_type, + '--description', description, + '--enabled=False'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_delete_flavor(self): + """Delete flavor.""" + resource = 'flavor' + cmd = flavor.DeleteFlavor(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) + + def test_list_flavors(self): + """List flavors test.""" + resources = 'flavors' + cmd = flavor.ListFlavor( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_list_flavors_with_pagination(self): + """List flavors test with pagination.""" + resources = 'flavors' + cmd = flavor.ListFlavor( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_flavors_with_sort(self): + """List flavors test with sorting by name and id.""" + resources = 'flavors' + cmd = flavor.ListFlavor( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_show_flavor(self): + """Show flavor test.""" + resource = 'flavor' + cmd = flavor.ShowFlavor( + test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_update_flavor_with_name(self): + """Update flavor test.""" + resource = 'flavor' + cmd = flavor.UpdateFlavor( + test_cli20.MyApp(sys.stdout), None) + newname = 'Test New Name' + newdescription = 'New Description' + args = ['--name', newname, + '--description', newdescription, + '--enabled', 'False', self.test_id] + self._test_update_resource(resource, cmd, self.test_id, args, + {'name': newname, + 'description': newdescription, + 'enabled': 'False'}) + + def test_associate_flavor(self): + """Associate flavor test.""" + resource = 'service_profile' + cmd = flavor.AssociateFlavor(test_cli20.MyApp(sys.stdout), None) + flavor_id = 'flavor-id' + profile_id = 'profile-id' + name = '' + args = [flavor_id, profile_id] + position_names = ['id'] + position_values = [profile_id] + self._test_create_resource(resource, cmd, name, profile_id, args, + position_names, position_values, + cmd_resource='flavor_profile_binding', + parent_id=flavor_id) + + def test_disassociate_flavor(self): + """Disassociate flavor test.""" + resource = 'flavor_profile_binding' + cmd = flavor.DisassociateFlavor(test_cli20.MyApp(sys.stdout), None) + flavor_id = 'flavor-id' + profile_id = 'profile-id' + args = [flavor_id, profile_id] + self._test_delete_resource(resource, cmd, profile_id, args, + parent_id=flavor_id) diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py b/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py new file mode 100644 index 000000000..d13090297 --- /dev/null +++ b/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py @@ -0,0 +1,122 @@ +# Copyright 2015 Hewlett-Packard Development Company, L.P. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import sys + +from neutronclient.neutron.v2_0.flavor import flavor_profile +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20FlavorProfileJSON(test_cli20.CLITestV20Base): + + def setUp(self): + """Prepare test environment.""" + super(CLITestV20FlavorProfileJSON, self).setUp( + plurals={'service_profiles': 'service_profile'}) + self.register_non_admin_status_resource('service_profile') + + def test_create_flavor_profile_with_mandatory_params(self): + """Create test flavor profile test.""" + resource = 'service_profile' + cmd = flavor_profile.CreateFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + name = '' + description = 'Test flavor profile' + myid = 'myid' + metainfo = "{'a':'b'}" + # Defaults are returned in body + position_names = ['description', 'metainfo'] + position_values = [description, metainfo] + args = ['--description', description, '--metainfo', metainfo] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_create_flavor_profile_with_optional_params(self): + """Create test flavor profile disabled test.""" + resource = 'service_profile' + cmd = flavor_profile.CreateFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + name = '' + description = 'Test flavor profile - disabled' + myid = 'myid' + driver = 'mydriver' + metainfo = "{'a':'b'}" + position_names = ['description', 'driver', 'metainfo', 'enabled'] + position_values = [description, driver, metainfo, 'False'] + args = ['--description', description, '--driver', driver, + '--metainfo', metainfo, '--enabled=False'] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + + def test_list_flavor_profiles(self): + """List flavor profiles test.""" + resources = 'service_profiles' + cmd = flavor_profile.ListFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_list_flavor_profiles_with_pagination(self): + """List flavor profiles test with pagination.""" + resources = 'service_profiles' + cmd = flavor_profile.ListFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_flavor_profiles_with_sort(self): + """List flavor profiles test with sort by description.""" + resources = 'service_profiles' + cmd = flavor_profile.ListFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["description"], + sort_dir=["asc"]) + + def test_show_flavor_profile(self): + """Show flavor profile test.""" + resource = 'service_profile' + cmd = flavor_profile.ShowFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_update_flavor_profile(self): + """Update flavor profile test.""" + resource = 'service_profile' + cmd = flavor_profile.UpdateFlavorProfile( + test_cli20.MyApp(sys.stdout), None) + newdescription = 'Test new description' + newdriver = 'NewDriver' + newmetainfo = "{'c':'d'}" + newenabled = "False" + args = ['--description', newdescription, + '--driver', newdriver, + '--metainfo', newmetainfo, + '--enabled', newenabled, + self.test_id] + self._test_update_resource(resource, cmd, self.test_id, args, + {'description': newdescription, + 'driver': newdriver, + 'metainfo': newmetainfo, + 'enabled': newenabled}) + + def test_delete_flavor_profile(self): + """Delete flavor profile.""" + resource = 'service_profile' + cmd = flavor_profile.DeleteFlavorProfile(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 3cb8ffcf4..9ff91e5b9 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -437,6 +437,12 @@ class Client(ClientBase): qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" qos_rule_types_path = "/qos/rule-types" qos_rule_type_path = "/qos/rule-types/%s" + flavors_path = "/flavors" + flavor_path = "/flavors/%s" + service_profiles_path = "/service_profiles" + service_profile_path = "/service_profiles/%s" + flavor_profile_bindings_path = flavor_path + service_profiles_path + flavor_profile_binding_path = flavor_path + service_profile_path # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -474,6 +480,7 @@ class Client(ClientBase): 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', 'rule_types': 'rule_type', + 'flavors': 'flavor', } @APIParamsCall @@ -1737,6 +1744,72 @@ class Client(ClientBase): return self.delete(self.qos_bandwidth_limit_rule_path % (policy, rule)) + @APIParamsCall + def create_flavor(self, body=None): + """Creates a new Neutron service flavor.""" + return self.post(self.flavors_path, body=body) + + @APIParamsCall + def delete_flavor(self, flavor): + """Deletes the specified Neutron service flavor.""" + return self.delete(self.flavor_path % (flavor)) + + @APIParamsCall + def list_flavors(self, retrieve_all=True, **_params): + """Fetches a list of all Neutron service flavors for a tenant.""" + return self.list('flavors', self.flavors_path, retrieve_all, + **_params) + + @APIParamsCall + def show_flavor(self, flavor, **_params): + """Fetches information for a certain Neutron service flavor.""" + return self.get(self.flavor_path % (flavor), params=_params) + + @APIParamsCall + def update_flavor(self, flavor, body): + """Update a Neutron service flavor.""" + return self.put(self.flavor_path % (flavor), body=body) + + @APIParamsCall + def associate_flavor(self, flavor, body): + """Associate a Neutron service flavor with a profile.""" + return self.post(self.flavor_profile_bindings_path % + (flavor), body=body) + + @APIParamsCall + def disassociate_flavor(self, flavor, flavor_profile): + """Disassociate a Neutron service flavor with a profile.""" + return self.delete(self.flavor_profile_binding_path % + (flavor, flavor_profile)) + + @APIParamsCall + def create_service_profile(self, body=None): + """Creates a new Neutron service flavor profile.""" + return self.post(self.service_profiles_path, body=body) + + @APIParamsCall + def delete_service_profile(self, flavor_profile): + """Deletes the specified Neutron service flavor profile.""" + return self.delete(self.service_profile_path % (flavor_profile)) + + @APIParamsCall + def list_service_profiles(self, retrieve_all=True, **_params): + """Fetches a list of all Neutron service flavor profiles.""" + return self.list('service_profiles', self.service_profiles_path, + retrieve_all, **_params) + + @APIParamsCall + def show_service_profile(self, flavor_profile, **_params): + """Fetches information for a certain Neutron service flavor profile.""" + return self.get(self.service_profile_path % (flavor_profile), + params=_params) + + @APIParamsCall + def update_service_profile(self, service_profile, body): + """Update a Neutron service profile.""" + return self.put(self.service_profile_path % (service_profile), + body=body) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs)