diff options
| -rw-r--r-- | doc/source/command-objects/compute-service.rst | 11 | ||||
| -rw-r--r-- | doc/source/command-objects/port.rst | 62 | ||||
| -rw-r--r-- | openstackclient/compute/v2/service.py | 22 | ||||
| -rw-r--r-- | openstackclient/network/v2/port.py | 52 | ||||
| -rw-r--r-- | openstackclient/tests/compute/v2/test_service.py | 26 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/test_endpoint.py | 3 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/test_project.py | 4 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/test_role.py | 6 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/test_service.py | 3 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/test_token.py | 3 | ||||
| -rw-r--r-- | openstackclient/tests/identity/v2_0/test_user.py | 24 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/test_port.py | 79 | ||||
| -rw-r--r-- | releasenotes/notes/add-disable-reason-6e0f28459a09a60d.yaml | 4 | ||||
| -rw-r--r-- | releasenotes/notes/add-port-set-command-2b4fe38ec71ad48f.yaml | 5 | ||||
| -rw-r--r-- | setup.cfg | 1 |
15 files changed, 276 insertions, 29 deletions
diff --git a/doc/source/command-objects/compute-service.rst b/doc/source/command-objects/compute-service.rst index 7bcab4d6..25a133d9 100644 --- a/doc/source/command-objects/compute-service.rst +++ b/doc/source/command-objects/compute-service.rst @@ -50,19 +50,24 @@ Set service command .. program:: compute service set .. code:: bash - os compute service list + os compute service set [--enable | --disable] + [--disable-reason <reason>] <host> <service> .. _compute-service-set: -.. describe:: --enable +.. option:: --enable Enable service (default) -.. describe:: --disable +.. option:: --disable Disable service +.. option:: --disable-reason <reason> + + Reason for disabling the service (in quotes) + .. describe:: <host> Name of host diff --git a/doc/source/command-objects/port.rst b/doc/source/command-objects/port.rst index 3083b075..e9c09173 100644 --- a/doc/source/command-objects/port.rst +++ b/doc/source/command-objects/port.rst @@ -45,7 +45,8 @@ Create new port .. option:: --vnic-type <vnic-type> - VNIC type for this port (direct | direct-physical | macvtap | normal(default) | baremetal) + VNIC type for this port (direct | direct-physical | macvtap | normal | baremetal). + If unspecified during port creation, default value will be 'normal'. .. option:: --binding-profile <binding-profile> @@ -108,6 +109,65 @@ List ports os port list +port set +-------- + +Set port properties + +.. program:: port set +.. code:: bash + + os port set + [--fixed-ip subnet=<subnet>,ip-address=<ip-address>] + [--device-id <device-id>] + [--device-owner <device-owner>] + [--vnic-type <vnic-type>] + [--binding-profile <binding-profile>] + [--host-id <host-id>] + [--enable | --disable] + <port> + +.. option:: --fixed-ip subnet=<subnet>,ip-address=<ip-address> + + Desired IP and/or subnet for this port: + subnet=<subnet>,ip-address=<ip-address> + (you can repeat this option) + +.. option:: --device-id <device-id> + + Device ID of this port + +.. option:: --device-owner <device-owner> + + Device owner of this port + +.. option:: --vnic-type <vnic-type> + + VNIC type for this port (direct | direct-physical | macvtap | normal | baremetal). + If unspecified during port creation, default value will be 'normal'. + +.. option:: --binding-profile <binding-profile> + + Custom data to be passed as binding:profile: <key>=<value> + (this option can be repeated) + +.. option:: --host-id <host-id> + + The ID of the host where the port is allocated + +.. option:: --enable + + Enable port + +.. option:: --disable + + Disable port + +.. _port_set-port: +.. describe:: <port> + + Port to modify (name or ID) + port show --------- diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 3c062724..1cc91711 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -95,14 +95,24 @@ class SetService(command.Command): dest="enabled", help="Disable a service", action="store_false") + parser.add_argument( + "--disable-reason", + default=None, + metavar="<reason>", + help="Reason for disabling the service (in quotas)" + ) return parser def take_action(self, parsed_args): compute_client = self.app.client_manager.compute - - if parsed_args.enabled: - action = compute_client.services.enable + cs = compute_client.services + + if not parsed_args.enabled: + if parsed_args.disable_reason: + cs.disable_log_reason(parsed_args.host, + parsed_args.service, + parsed_args.disable_reason) + else: + cs.disable(parsed_args.host, parsed_args.service) else: - action = compute_client.services.disable - - action(parsed_args.host, parsed_args.service) + cs.enable(parsed_args.host, parsed_args.service) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 449dcfd4..b618a4b0 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -14,6 +14,7 @@ """Port action implementations""" from openstackclient.common import command +from openstackclient.common import exceptions from openstackclient.common import parseractions from openstackclient.common import utils from openstackclient.identity import common as identity_common @@ -56,8 +57,6 @@ def _get_columns(item): def _get_attrs(client_manager, parsed_args): attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) if parsed_args.fixed_ip is not None: attrs['fixed_ips'] = parsed_args.fixed_ip if parsed_args.device_id is not None: @@ -75,6 +74,8 @@ def _get_attrs(client_manager, parsed_args): # The remaining options do not support 'port set' command, so they require # additional check + if 'name' in parsed_args and parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) if 'mac_address' in parsed_args and parsed_args.mac_address is not None: attrs['mac_address'] = parsed_args.mac_address if 'network' in parsed_args and parsed_args.network is not None: @@ -145,8 +146,9 @@ def _add_updatable_args(parser): metavar='<vnic-type>', choices=['direct', 'direct-physical', 'macvtap', 'normal', 'baremetal'], - help='VNIC type for this port (direct | direct-physical |' - ' macvtap | normal(default) | baremetal)') + help="VNIC type for this port (direct | direct-physical |" + " macvtap | normal | baremetal). If unspecified during" + " port creation, default value will be 'normal'.") parser.add_argument( '--binding-profile', metavar='<binding-profile>', @@ -265,6 +267,48 @@ class ListPort(command.Lister): ) for s in data)) +class SetPort(command.Command): + """Set port properties""" + + def get_parser(self, prog_name): + parser = super(SetPort, self).get_parser(prog_name) + _add_updatable_args(parser) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + dest='admin_state', + action='store_true', + default=None, + help='Enable port', + ) + admin_group.add_argument( + '--disable', + dest='admin_state', + action='store_false', + help='Disable port', + ) + parser.add_argument( + 'port', + metavar="<port>", + help=("Port to modify (name or ID)") + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + + _prepare_fixed_ips(self.app.client_manager, parsed_args) + attrs = _get_attrs(self.app.client_manager, parsed_args) + + if attrs == {}: + msg = "Nothing specified to be set" + raise exceptions.CommandError(msg) + + obj = client.find_port(parsed_args.port, ignore_missing=False) + client.update_port(obj, **attrs) + + class ShowPort(command.ShowOne): """Display port details""" diff --git a/openstackclient/tests/compute/v2/test_service.py b/openstackclient/tests/compute/v2/test_service.py index 2f8b2e7d..0246fbc8 100644 --- a/openstackclient/tests/compute/v2/test_service.py +++ b/openstackclient/tests/compute/v2/test_service.py @@ -153,3 +153,29 @@ class TestServiceSet(TestService): compute_fakes.service_binary, ) self.assertIsNone(result) + + def test_service_set_disable_with_reason(self): + reason = 'earthquake' + arglist = [ + compute_fakes.service_host, + compute_fakes.service_binary, + '--disable', + '--disable-reason', + reason + ] + verifylist = [ + ('host', compute_fakes.service_host), + ('service', compute_fakes.service_binary), + ('enabled', False), + ('disable_reason', reason) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable_log_reason.assert_called_with( + compute_fakes.service_host, + compute_fakes.service_binary, + reason + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/identity/v2_0/test_endpoint.py b/openstackclient/tests/identity/v2_0/test_endpoint.py index 088fdcd1..45ece45a 100644 --- a/openstackclient/tests/identity/v2_0/test_endpoint.py +++ b/openstackclient/tests/identity/v2_0/test_endpoint.py @@ -132,11 +132,12 @@ class TestEndpointDelete(TestEndpoint): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) self.endpoints_mock.delete.assert_called_with( identity_fakes.endpoint_id, ) + self.assertIsNone(result) class TestEndpointList(TestEndpoint): diff --git a/openstackclient/tests/identity/v2_0/test_project.py b/openstackclient/tests/identity/v2_0/test_project.py index 98570297..38684aaf 100644 --- a/openstackclient/tests/identity/v2_0/test_project.py +++ b/openstackclient/tests/identity/v2_0/test_project.py @@ -613,10 +613,9 @@ class TestProjectUnset(TestProject): verifylist = [ ('property', ['fee', 'fo']), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { 'description': identity_fakes.project_description, @@ -631,3 +630,4 @@ class TestProjectUnset(TestProject): identity_fakes.project_id, **kwargs ) + self.assertIsNone(result) diff --git a/openstackclient/tests/identity/v2_0/test_role.py b/openstackclient/tests/identity/v2_0/test_role.py index 03b7f924..3c4b79a4 100644 --- a/openstackclient/tests/identity/v2_0/test_role.py +++ b/openstackclient/tests/identity/v2_0/test_role.py @@ -240,11 +240,12 @@ class TestRoleDelete(TestRole): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) self.roles_mock.delete.assert_called_with( identity_fakes.role_id, ) + self.assertIsNone(result) class TestRoleList(TestRole): @@ -459,7 +460,7 @@ class TestRoleRemove(TestRole): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # RoleManager.remove_user_role(user, role, tenant=None) self.roles_mock.remove_user_role.assert_called_with( @@ -467,6 +468,7 @@ class TestRoleRemove(TestRole): identity_fakes.role_id, identity_fakes.project_id, ) + self.assertIsNone(result) class TestRoleShow(TestRole): diff --git a/openstackclient/tests/identity/v2_0/test_service.py b/openstackclient/tests/identity/v2_0/test_service.py index 606b1433..dc0fbcd1 100644 --- a/openstackclient/tests/identity/v2_0/test_service.py +++ b/openstackclient/tests/identity/v2_0/test_service.py @@ -194,11 +194,12 @@ class TestServiceDelete(TestService): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) self.services_mock.delete.assert_called_with( identity_fakes.service_id, ) + self.assertIsNone(result) class TestServiceList(TestService): diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py index c90477f9..613139dd 100644 --- a/openstackclient/tests/identity/v2_0/test_token.py +++ b/openstackclient/tests/identity/v2_0/test_token.py @@ -99,6 +99,7 @@ class TestTokenRevoke(TestToken): verifylist = [('token', self.TOKEN)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) self.tokens_mock.delete.assert_called_with(self.TOKEN) + self.assertIsNone(result) diff --git a/openstackclient/tests/identity/v2_0/test_user.py b/openstackclient/tests/identity/v2_0/test_user.py index 9afe4ad1..921e215d 100644 --- a/openstackclient/tests/identity/v2_0/test_user.py +++ b/openstackclient/tests/identity/v2_0/test_user.py @@ -414,11 +414,12 @@ class TestUserDelete(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) self.users_mock.delete.assert_called_with( identity_fakes.user_id, ) + self.assertIsNone(result) class TestUserList(TestUser): @@ -578,7 +579,7 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { @@ -590,6 +591,7 @@ class TestUserSet(TestUser): identity_fakes.user_id, **kwargs ) + self.assertIsNone(result) def test_user_set_password(self): arglist = [ @@ -608,13 +610,14 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # UserManager.update_password(user, password) self.users_mock.update_password.assert_called_with( identity_fakes.user_id, 'secret', ) + self.assertIsNone(result) def test_user_set_password_prompt(self): arglist = [ @@ -636,13 +639,14 @@ class TestUserSet(TestUser): mocker = mock.Mock() mocker.return_value = 'abc123' with mock.patch("openstackclient.common.utils.get_password", mocker): - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # UserManager.update_password(user, password) self.users_mock.update_password.assert_called_with( identity_fakes.user_id, 'abc123', ) + self.assertIsNone(result) def test_user_set_email(self): arglist = [ @@ -660,7 +664,7 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { @@ -672,6 +676,7 @@ class TestUserSet(TestUser): identity_fakes.user_id, **kwargs ) + self.assertIsNone(result) def test_user_set_project(self): arglist = [ @@ -689,13 +694,14 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # UserManager.update_tenant(user, tenant) self.users_mock.update_tenant.assert_called_with( identity_fakes.user_id, identity_fakes.project_id, ) + self.assertIsNone(result) def test_user_set_enable(self): arglist = [ @@ -713,7 +719,7 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { @@ -724,6 +730,7 @@ class TestUserSet(TestUser): identity_fakes.user_id, **kwargs ) + self.assertIsNone(result) def test_user_set_disable(self): arglist = [ @@ -741,7 +748,7 @@ class TestUserSet(TestUser): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { @@ -752,6 +759,7 @@ class TestUserSet(TestUser): identity_fakes.user_id, **kwargs ) + self.assertIsNone(result) class TestUserShow(TestUser): diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py index 30e290c6..7b1c655f 100644 --- a/openstackclient/tests/network/v2/test_port.py +++ b/openstackclient/tests/network/v2/test_port.py @@ -240,6 +240,85 @@ class TestListPort(TestPort): self.assertEqual(self.data, list(data)) +class TestSetPort(TestPort): + + _port = network_fakes.FakePort.create_one_port() + + def setUp(self): + super(TestSetPort, self).setUp() + + self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() + self.network.find_subnet = mock.Mock(return_value=self.fake_subnet) + self.network.find_port = mock.Mock(return_value=self._port) + self.network.update_port = mock.Mock(return_value=None) + + # Get the command object to test + self.cmd = port.SetPort(self.app, self.namespace) + + def test_set_fixed_ip(self): + arglist = [ + '--fixed-ip', 'ip-address=10.0.0.11', + self._port.name, + ] + verifylist = [ + ('fixed_ip', [{'ip-address': '10.0.0.11'}]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'fixed_ips': [{'ip_address': '10.0.0.11'}], + } + self.network.update_port.assert_called_with(self._port, **attrs) + self.assertIsNone(result) + + def test_set_this(self): + arglist = [ + '--disable', + self._port.name, + ] + verifylist = [ + ('admin_state', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': False, + } + self.network.update_port.assert_called_with(self._port, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + '--enable', + '--vnic-type', 'macvtap', + '--binding-profile', 'foo=bar', + '--host-id', 'binding-host-id-xxxx', + self._port.name, + ] + verifylist = [ + ('admin_state', True), + ('vnic_type', 'macvtap'), + ('binding_profile', {'foo': 'bar'}), + ('host_id', 'binding-host-id-xxxx'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'admin_state_up': True, + 'binding:vnic_type': 'macvtap', + 'binding:profile': {'foo': 'bar'}, + 'binding:host_id': 'binding-host-id-xxxx', + } + self.network.update_port.assert_called_with(self._port, **attrs) + self.assertIsNone(result) + + class TestShowPort(TestPort): # The port to show. diff --git a/releasenotes/notes/add-disable-reason-6e0f28459a09a60d.yaml b/releasenotes/notes/add-disable-reason-6e0f28459a09a60d.yaml new file mode 100644 index 00000000..298ccad5 --- /dev/null +++ b/releasenotes/notes/add-disable-reason-6e0f28459a09a60d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add support for the ``--disable-reason`` of ``service set`` command diff --git a/releasenotes/notes/add-port-set-command-2b4fe38ec71ad48f.yaml b/releasenotes/notes/add-port-set-command-2b4fe38ec71ad48f.yaml new file mode 100644 index 00000000..89784d2b --- /dev/null +++ b/releasenotes/notes/add-port-set-command-2b4fe38ec71ad48f.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add support for the ``port set`` command. + [Bug `1519909 <https://bugs.launchpad.net/python-openstackclient/+bug/1519909>`_] @@ -336,6 +336,7 @@ openstack.network.v2 = port_create = openstackclient.network.v2.port:CreatePort port_delete = openstackclient.network.v2.port:DeletePort port_list = openstackclient.network.v2.port:ListPort + port_set = openstackclient.network.v2.port:SetPort port_show = openstackclient.network.v2.port:ShowPort router_create = openstackclient.network.v2.router:CreateRouter |
