Skip to content

Commit d1d4a40

Browse files
author
Jas
committed
Add 'port create' command
This patch adds usage of 'port create' in CLI Change-Id: I888af50784c3b6c7ec30552ade79f05a5e974711 Partial-bug: #1519909 Partially-implements: blueprint neutron-client
1 parent fc8b4cf commit d1d4a40

5 files changed

Lines changed: 400 additions & 53 deletions

File tree

doc/source/command-objects/port.rst

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,84 @@ port
44

55
Network v2
66

7+
port create
8+
-----------
9+
10+
Create new port
11+
12+
.. program:: port create
13+
.. code:: bash
14+
15+
os port create
16+
--network <network>
17+
[--fixed-ip subnet=<subnet>,ip-address=<ip-address>]
18+
[--device-id <device-id>]
19+
[--device-owner <device-owner>]
20+
[--vnic-type <vnic-type>]
21+
[--binding-profile <binding-profile>]
22+
[--host-id <host-id>]
23+
[--enable | --disable]
24+
[--mac-address <mac-address>]
25+
[--project <project> [--project-domain <project-domain>]]
26+
<name>
27+
28+
.. option:: --network <network>
29+
30+
Network this port belongs to (name or ID)
31+
32+
.. option:: --fixed-ip subnet=<subnet>,ip-address=<ip-address>
33+
34+
Desired IP and/or subnet (name or ID) for this port:
35+
subnet=<subnet>,ip-address=<ip-address>
36+
(this option can be repeated)
37+
38+
.. option:: --device-id <device-id>
39+
40+
Device ID of this port
41+
42+
.. option:: --device-owner <device-owner>
43+
44+
Device owner of this port
45+
46+
.. option:: --vnic-type <vnic-type>
47+
48+
VNIC type for this port (direct | direct-physical | macvtap | normal(default) | baremetal)
49+
50+
.. option:: --binding-profile <binding-profile>
51+
52+
Custom data to be passed as binding:profile: <key>=<value>
53+
(this option can be repeated)
54+
55+
.. option:: --host-id <host-id>
56+
57+
The ID of the host where the port is allocated
58+
59+
.. option:: --enable
60+
61+
Enable port (default)
62+
63+
.. option:: --disable
64+
65+
Disable port
66+
67+
.. option:: --mac-address <mac-address>
68+
69+
MAC address of this port
70+
71+
.. option:: --project <project>
72+
73+
Owner's project (name or ID)
74+
75+
.. option:: --project-domain <project-domain>
76+
77+
Domain the project belongs to (name or ID).
78+
This can be used in case collisions between project names exist.
79+
80+
.. _port_create-name:
81+
.. describe:: <name>
82+
83+
Name of this port
84+
785
port delete
886
-----------
987

openstackclient/network/v2/port.py

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
"""Port action implementations"""
1515

1616
from openstackclient.common import command
17+
from openstackclient.common import parseractions
1718
from openstackclient.common import utils
19+
from openstackclient.identity import common as identity_common
1820

1921

2022
def _format_admin_state(state):
2123
return 'UP' if state else 'DOWN'
2224

23-
2425
_formatters = {
2526
'admin_state_up': _format_admin_state,
2627
'allowed_address_pairs': utils.format_list_of_dicts,
@@ -49,7 +50,171 @@ def _get_columns(item):
4950
if binding_column in columns:
5051
columns.remove(binding_column)
5152
columns.append(binding_column.replace('binding:', 'binding_', 1))
52-
return sorted(columns)
53+
return tuple(sorted(columns))
54+
55+
56+
def _get_attrs(client_manager, parsed_args):
57+
attrs = {}
58+
59+
if parsed_args.name is not None:
60+
attrs['name'] = str(parsed_args.name)
61+
if parsed_args.fixed_ip is not None:
62+
attrs['fixed_ips'] = parsed_args.fixed_ip
63+
if parsed_args.device_id is not None:
64+
attrs['device_id'] = parsed_args.device_id
65+
if parsed_args.device_owner is not None:
66+
attrs['device_owner'] = parsed_args.device_owner
67+
if parsed_args.admin_state is not None:
68+
attrs['admin_state_up'] = parsed_args.admin_state
69+
if parsed_args.binding_profile is not None:
70+
attrs['binding:profile'] = parsed_args.binding_profile
71+
if parsed_args.vnic_type is not None:
72+
attrs['binding:vnic_type'] = parsed_args.vnic_type
73+
if parsed_args.host_id is not None:
74+
attrs['binding:host_id'] = parsed_args.host_id
75+
76+
# The remaining options do not support 'port set' command, so they require
77+
# additional check
78+
if 'mac_address' in parsed_args and parsed_args.mac_address is not None:
79+
attrs['mac_address'] = parsed_args.mac_address
80+
if 'network' in parsed_args and parsed_args.network is not None:
81+
attrs['network_id'] = parsed_args.network
82+
if 'project' in parsed_args and parsed_args.project is not None:
83+
# TODO(singhj): since 'project' logic is common among
84+
# router, network, port etc., maybe move it to a common file.
85+
identity_client = client_manager.identity
86+
project_id = identity_common.find_project(
87+
identity_client,
88+
parsed_args.project,
89+
parsed_args.project_domain,
90+
).id
91+
attrs['tenant_id'] = project_id
92+
93+
return attrs
94+
95+
96+
def _prepare_fixed_ips(client_manager, parsed_args):
97+
"""Fix and properly format fixed_ip option.
98+
99+
Appropriately convert any subnet names to their respective ids.
100+
Convert fixed_ips in parsed args to be in valid dictionary format:
101+
{'subnet': 'foo'}.
102+
"""
103+
client = client_manager.network
104+
ips = []
105+
106+
if parsed_args.fixed_ip:
107+
for ip_spec in parsed_args.fixed_ip:
108+
if 'subnet' in ip_spec:
109+
subnet_name_id = ip_spec['subnet']
110+
if subnet_name_id:
111+
_subnet = client.find_subnet(subnet_name_id,
112+
ignore_missing=False)
113+
ip_spec['subnet_id'] = _subnet.id
114+
del ip_spec['subnet']
115+
116+
if 'ip-address' in ip_spec:
117+
ip_spec['ip_address'] = ip_spec['ip-address']
118+
del ip_spec['ip-address']
119+
120+
ips.append(ip_spec)
121+
122+
if ips:
123+
parsed_args.fixed_ip = ips
124+
125+
126+
def _add_updatable_args(parser):
127+
parser.add_argument(
128+
'--fixed-ip',
129+
metavar='subnet=<subnet>,ip-address=<ip-address>',
130+
action=parseractions.MultiKeyValueAction,
131+
optional_keys=['subnet', 'ip-address'],
132+
help='Desired IP and/or subnet (name or ID) for this port: '
133+
'subnet=<subnet>,ip-address=<ip-address> '
134+
'(this option can be repeated)')
135+
parser.add_argument(
136+
'--device-id',
137+
metavar='<device-id>',
138+
help='Device ID of this port')
139+
parser.add_argument(
140+
'--device-owner',
141+
metavar='<device-owner>',
142+
help='Device owner of this port')
143+
parser.add_argument(
144+
'--vnic-type',
145+
metavar='<vnic-type>',
146+
choices=['direct', 'direct-physical', 'macvtap',
147+
'normal', 'baremetal'],
148+
help='VNIC type for this port (direct | direct-physical |'
149+
' macvtap | normal(default) | baremetal)')
150+
parser.add_argument(
151+
'--binding-profile',
152+
metavar='<binding-profile>',
153+
action=parseractions.KeyValueAction,
154+
help='Custom data to be passed as binding:profile: <key>=<value> '
155+
'(this option can be repeated)')
156+
parser.add_argument(
157+
'--host-id',
158+
metavar='<host-id>',
159+
help='The ID of the host where the port is allocated'
160+
)
161+
162+
163+
class CreatePort(command.ShowOne):
164+
"""Create a new port"""
165+
166+
def get_parser(self, prog_name):
167+
parser = super(CreatePort, self).get_parser(prog_name)
168+
169+
parser.add_argument(
170+
'--network',
171+
metavar='<network>',
172+
required=True,
173+
help='Network this port belongs to (name or ID)')
174+
_add_updatable_args(parser)
175+
admin_group = parser.add_mutually_exclusive_group()
176+
admin_group.add_argument(
177+
'--enable',
178+
dest='admin_state',
179+
action='store_true',
180+
default=True,
181+
help='Enable port (default)',
182+
)
183+
admin_group.add_argument(
184+
'--disable',
185+
dest='admin_state',
186+
action='store_false',
187+
help='Disable port',
188+
)
189+
parser.add_argument(
190+
'--mac-address',
191+
metavar='<mac-address>',
192+
help='MAC address of this port')
193+
parser.add_argument(
194+
'--project',
195+
metavar='<project>',
196+
help="Owner's project (name or ID)")
197+
parser.add_argument(
198+
'name',
199+
metavar='<name>',
200+
help='Name of this port')
201+
identity_common.add_project_domain_option_to_parser(parser)
202+
# TODO(singhj): Add support for extended options:
203+
# qos,security groups,dhcp, address pairs
204+
return parser
205+
206+
def take_action(self, parsed_args):
207+
client = self.app.client_manager.network
208+
_network = client.find_network(parsed_args.network,
209+
ignore_missing=False)
210+
parsed_args.network = _network.id
211+
_prepare_fixed_ips(self.app.client_manager, parsed_args)
212+
attrs = _get_attrs(self.app.client_manager, parsed_args)
213+
obj = client.create_port(**attrs)
214+
columns = _get_columns(obj)
215+
data = utils.get_item_properties(obj, columns, formatters=_formatters)
216+
217+
return columns, data
53218

54219

55220
class DeletePort(command.Command):
@@ -90,4 +255,4 @@ def take_action(self, parsed_args):
90255
obj = client.find_port(parsed_args.port, ignore_missing=False)
91256
columns = _get_columns(obj)
92257
data = utils.get_item_properties(obj, columns, formatters=_formatters)
93-
return (tuple(columns), data)
258+
return columns, data

0 commit comments

Comments
 (0)