summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-03-04 23:39:22 +0000
committerGerrit Code Review <review@openstack.org>2016-03-04 23:39:22 +0000
commita61c5cc8a4884fd97d0938b3559f6a630cd4ad50 (patch)
treeb40156e012f2eee95f76303c277ff5d7955ba157 /openstackclient
parent5c1633f5050a21ad1866eb3056df74595780e5ae (diff)
parentd1d4a40808741c416ecc51294fab78138f9184fa (diff)
downloadpython-openstackclient-a61c5cc8a4884fd97d0938b3559f6a630cd4ad50.tar.gz
Merge "Add 'port create' command"
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/network/v2/port.py171
-rw-r--r--openstackclient/tests/network/v2/test_port.py198
2 files changed, 316 insertions, 53 deletions
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 19b3701d..f9d0fc95 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -14,13 +14,14 @@
"""Port action implementations"""
from openstackclient.common import command
+from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
def _format_admin_state(state):
return 'UP' if state else 'DOWN'
-
_formatters = {
'admin_state_up': _format_admin_state,
'allowed_address_pairs': utils.format_list_of_dicts,
@@ -49,7 +50,171 @@ def _get_columns(item):
if binding_column in columns:
columns.remove(binding_column)
columns.append(binding_column.replace('binding:', 'binding_', 1))
- return sorted(columns)
+ return tuple(sorted(columns))
+
+
+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:
+ attrs['device_id'] = parsed_args.device_id
+ if parsed_args.device_owner is not None:
+ attrs['device_owner'] = parsed_args.device_owner
+ if parsed_args.admin_state is not None:
+ attrs['admin_state_up'] = parsed_args.admin_state
+ if parsed_args.binding_profile is not None:
+ attrs['binding:profile'] = parsed_args.binding_profile
+ if parsed_args.vnic_type is not None:
+ attrs['binding:vnic_type'] = parsed_args.vnic_type
+ if parsed_args.host_id is not None:
+ attrs['binding:host_id'] = parsed_args.host_id
+
+ # The remaining options do not support 'port set' command, so they require
+ # additional check
+ 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:
+ attrs['network_id'] = parsed_args.network
+ if 'project' in parsed_args and parsed_args.project is not None:
+ # TODO(singhj): since 'project' logic is common among
+ # router, network, port etc., maybe move it to a common file.
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+
+ return attrs
+
+
+def _prepare_fixed_ips(client_manager, parsed_args):
+ """Fix and properly format fixed_ip option.
+
+ Appropriately convert any subnet names to their respective ids.
+ Convert fixed_ips in parsed args to be in valid dictionary format:
+ {'subnet': 'foo'}.
+ """
+ client = client_manager.network
+ ips = []
+
+ if parsed_args.fixed_ip:
+ for ip_spec in parsed_args.fixed_ip:
+ if 'subnet' in ip_spec:
+ subnet_name_id = ip_spec['subnet']
+ if subnet_name_id:
+ _subnet = client.find_subnet(subnet_name_id,
+ ignore_missing=False)
+ ip_spec['subnet_id'] = _subnet.id
+ del ip_spec['subnet']
+
+ if 'ip-address' in ip_spec:
+ ip_spec['ip_address'] = ip_spec['ip-address']
+ del ip_spec['ip-address']
+
+ ips.append(ip_spec)
+
+ if ips:
+ parsed_args.fixed_ip = ips
+
+
+def _add_updatable_args(parser):
+ parser.add_argument(
+ '--fixed-ip',
+ metavar='subnet=<subnet>,ip-address=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ optional_keys=['subnet', 'ip-address'],
+ help='Desired IP and/or subnet (name or ID) for this port: '
+ 'subnet=<subnet>,ip-address=<ip-address> '
+ '(this option can be repeated)')
+ parser.add_argument(
+ '--device-id',
+ metavar='<device-id>',
+ help='Device ID of this port')
+ parser.add_argument(
+ '--device-owner',
+ metavar='<device-owner>',
+ help='Device owner of this port')
+ parser.add_argument(
+ '--vnic-type',
+ metavar='<vnic-type>',
+ choices=['direct', 'direct-physical', 'macvtap',
+ 'normal', 'baremetal'],
+ help='VNIC type for this port (direct | direct-physical |'
+ ' macvtap | normal(default) | baremetal)')
+ parser.add_argument(
+ '--binding-profile',
+ metavar='<binding-profile>',
+ action=parseractions.KeyValueAction,
+ help='Custom data to be passed as binding:profile: <key>=<value> '
+ '(this option can be repeated)')
+ parser.add_argument(
+ '--host-id',
+ metavar='<host-id>',
+ help='The ID of the host where the port is allocated'
+ )
+
+
+class CreatePort(command.ShowOne):
+ """Create a new port"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreatePort, self).get_parser(prog_name)
+
+ parser.add_argument(
+ '--network',
+ metavar='<network>',
+ required=True,
+ help='Network this port belongs to (name or ID)')
+ _add_updatable_args(parser)
+ admin_group = parser.add_mutually_exclusive_group()
+ admin_group.add_argument(
+ '--enable',
+ dest='admin_state',
+ action='store_true',
+ default=True,
+ help='Enable port (default)',
+ )
+ admin_group.add_argument(
+ '--disable',
+ dest='admin_state',
+ action='store_false',
+ help='Disable port',
+ )
+ parser.add_argument(
+ '--mac-address',
+ metavar='<mac-address>',
+ help='MAC address of this port')
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help="Owner's project (name or ID)")
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help='Name of this port')
+ identity_common.add_project_domain_option_to_parser(parser)
+ # TODO(singhj): Add support for extended options:
+ # qos,security groups,dhcp, address pairs
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ _network = client.find_network(parsed_args.network,
+ ignore_missing=False)
+ parsed_args.network = _network.id
+ _prepare_fixed_ips(self.app.client_manager, parsed_args)
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ obj = client.create_port(**attrs)
+ columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
+
+ return columns, data
class DeletePort(command.Command):
@@ -90,4 +255,4 @@ class ShowPort(command.ShowOne):
obj = client.find_port(parsed_args.port, ignore_missing=False)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (tuple(columns), data)
+ return columns, data
diff --git a/openstackclient/tests/network/v2/test_port.py b/openstackclient/tests/network/v2/test_port.py
index bc246bd8..907d8a7d 100644
--- a/openstackclient/tests/network/v2/test_port.py
+++ b/openstackclient/tests/network/v2/test_port.py
@@ -27,6 +27,150 @@ class TestPort(network_fakes.TestNetworkV2):
# Get a shortcut to the network client
self.network = self.app.client_manager.network
+ def _get_common_cols_data(self, fake_port):
+ columns = (
+ 'admin_state_up',
+ 'allowed_address_pairs',
+ 'binding_host_id',
+ 'binding_profile',
+ 'binding_vif_details',
+ 'binding_vif_type',
+ 'binding_vnic_type',
+ 'device_id',
+ 'device_owner',
+ 'dns_assignment',
+ 'dns_name',
+ 'extra_dhcp_opts',
+ 'fixed_ips',
+ 'id',
+ 'mac_address',
+ 'name',
+ 'network_id',
+ 'port_security_enabled',
+ 'project_id',
+ 'security_groups',
+ 'status',
+ )
+
+ data = (
+ port._format_admin_state(fake_port.admin_state_up),
+ utils.format_list_of_dicts(fake_port.allowed_address_pairs),
+ fake_port.binding_host_id,
+ utils.format_dict(fake_port.binding_profile),
+ utils.format_dict(fake_port.binding_vif_details),
+ fake_port.binding_vif_type,
+ fake_port.binding_vnic_type,
+ fake_port.device_id,
+ fake_port.device_owner,
+ utils.format_list_of_dicts(fake_port.dns_assignment),
+ fake_port.dns_name,
+ utils.format_list_of_dicts(fake_port.extra_dhcp_opts),
+ utils.format_list_of_dicts(fake_port.fixed_ips),
+ fake_port.id,
+ fake_port.mac_address,
+ fake_port.name,
+ fake_port.network_id,
+ fake_port.port_security_enabled,
+ fake_port.project_id,
+ utils.format_list(fake_port.security_groups),
+ fake_port.status,
+ )
+
+ return columns, data
+
+
+class TestCreatePort(TestPort):
+
+ _port = network_fakes.FakePort.create_one_port()
+
+ def setUp(self):
+ super(TestCreatePort, self).setUp()
+
+ self.network.create_port = mock.Mock(return_value=self._port)
+ fake_net = network_fakes.FakeNetwork.create_one_network({
+ 'id': self._port.network_id,
+ })
+ self.network.find_network = mock.Mock(return_value=fake_net)
+ self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet()
+ self.network.find_subnet = mock.Mock(return_value=self.fake_subnet)
+ # Get the command object to test
+ self.cmd = port.CreatePort(self.app, self.namespace)
+
+ def test_create_default_options(self):
+ arglist = [
+ '--network', self._port.network_id,
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('admin_state', True),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
+ def test_create_full_options(self):
+ arglist = [
+ '--mac-address', 'aa:aa:aa:aa:aa:aa',
+ '--fixed-ip', 'subnet=%s,ip-address=10.0.0.2'
+ % self.fake_subnet.id,
+ '--device-id', 'deviceid',
+ '--device-owner', 'fakeowner',
+ '--disable',
+ '--vnic-type', 'macvtap',
+ '--binding-profile', 'foo=bar',
+ '--binding-profile', 'foo2=bar2',
+ '--network', self._port.network_id,
+ 'test-port',
+
+ ]
+ verifylist = [
+ ('mac_address', 'aa:aa:aa:aa:aa:aa'),
+ (
+ 'fixed_ip',
+ [{'subnet': self.fake_subnet.id, 'ip-address': '10.0.0.2'}]
+ ),
+ ('device_id', 'deviceid'),
+ ('device_owner', 'fakeowner'),
+ ('admin_state', False),
+ ('vnic_type', 'macvtap'),
+ ('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
+ ('network', self._port.network_id),
+ ('name', 'test-port'),
+
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_with(**{
+ 'mac_address': 'aa:aa:aa:aa:aa:aa',
+ 'fixed_ips': [{'subnet_id': self.fake_subnet.id,
+ 'ip_address': '10.0.0.2'}],
+ 'device_id': 'deviceid',
+ 'device_owner': 'fakeowner',
+ 'admin_state_up': False,
+ 'binding:vnic_type': 'macvtap',
+ 'binding:profile': {'foo': 'bar', 'foo2': 'bar2'},
+ 'network_id': self._port.network_id,
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
class TestDeletePort(TestPort):
@@ -60,54 +204,6 @@ class TestShowPort(TestPort):
# The port to show.
_port = network_fakes.FakePort.create_one_port()
- columns = (
- 'admin_state_up',
- 'allowed_address_pairs',
- 'binding_host_id',
- 'binding_profile',
- 'binding_vif_details',
- 'binding_vif_type',
- 'binding_vnic_type',
- 'device_id',
- 'device_owner',
- 'dns_assignment',
- 'dns_name',
- 'extra_dhcp_opts',
- 'fixed_ips',
- 'id',
- 'mac_address',
- 'name',
- 'network_id',
- 'port_security_enabled',
- 'project_id',
- 'security_groups',
- 'status',
- )
-
- data = (
- port._format_admin_state(_port.admin_state_up),
- utils.format_list_of_dicts(_port.allowed_address_pairs),
- _port.binding_host_id,
- utils.format_dict(_port.binding_profile),
- utils.format_dict(_port.binding_vif_details),
- _port.binding_vif_type,
- _port.binding_vnic_type,
- _port.device_id,
- _port.device_owner,
- utils.format_list_of_dicts(_port.dns_assignment),
- _port.dns_name,
- utils.format_list_of_dicts(_port.extra_dhcp_opts),
- utils.format_list_of_dicts(_port.fixed_ips),
- _port.id,
- _port.mac_address,
- _port.name,
- _port.network_id,
- _port.port_security_enabled,
- _port.project_id,
- utils.format_list(_port.security_groups),
- _port.status,
- )
-
def setUp(self):
super(TestShowPort, self).setUp()
@@ -136,5 +232,7 @@ class TestShowPort(TestPort):
self.network.find_port.assert_called_with(self._port.name,
ignore_missing=False)
- self.assertEqual(tuple(self.columns), columns)
- self.assertEqual(self.data, data)
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)