Skip to content

Commit b36cd0f

Browse files
committed
Add router ndp proxy commands
Depends-on: https://review.opendev.org/749036 Change-Id: I77e12cc2dfe4000bd5ae6511878c6591f52d9791 Related-Bug: #1877301
1 parent 27b2496 commit b36cd0f

File tree

9 files changed

+1042
-0
lines changed

9 files changed

+1042
-0
lines changed

.zuul.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
q-qos: true
108108
neutron-tag-ports-during-bulk-creation: true
109109
neutron-conntrack-helper: true
110+
neutron-ndp-proxy: true
110111
devstack_localrc:
111112
Q_AGENT: openvswitch
112113
Q_ML2_TENANT_NETWORK_TYPE: vxlan
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=================
2+
router ndp proxy
3+
=================
4+
5+
An **NDP proxy** publishes a internal IPv6 address to public network. With the
6+
**NDP proxy**, the IPv6 address can be accessed from external. It is similar
7+
to **Floating IP** of IPv4 in functionality.
8+
9+
Network v2
10+
11+
.. autoprogram-cliff:: openstack.network.v2
12+
:command: router ndp proxy *
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Copyright (c) 2020 Troila.
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+
"""Router NDP proxy action implementations"""
17+
import logging
18+
19+
from osc_lib.command import command
20+
from osc_lib import exceptions
21+
from osc_lib import utils
22+
23+
from openstackclient.i18n import _
24+
from openstackclient.identity import common as identity_common
25+
26+
27+
LOG = logging.getLogger(__name__)
28+
29+
30+
def _get_columns(item):
31+
column_map = {}
32+
hidden_columns = ['location']
33+
return utils.get_osc_show_columns_for_sdk_resource(
34+
item, column_map, hidden_columns)
35+
36+
37+
class CreateNDPProxy(command.ShowOne):
38+
_description = _("Create NDP proxy")
39+
40+
def get_parser(self, prog_name):
41+
parser = super().get_parser(prog_name)
42+
parser.add_argument(
43+
'router',
44+
metavar='<router>',
45+
help=_("The name or ID of a router"))
46+
parser.add_argument(
47+
'--name',
48+
metavar='<name>',
49+
help=_("New NDP proxy name")
50+
)
51+
parser.add_argument(
52+
'--port',
53+
metavar='<port>',
54+
required=True,
55+
help=_("The name or ID of the network port associated "
56+
"to the NDP proxy"))
57+
parser.add_argument(
58+
'--ip-address',
59+
metavar='<ip-address>',
60+
help=_("The IPv6 address that is to be proxied. In case the port "
61+
"has multiple addresses assigned, use this option to "
62+
"select which address is to be used."))
63+
parser.add_argument(
64+
'--description',
65+
metavar='<description>',
66+
help=_("A text to describe/contextualize the use of the "
67+
"NDP proxy configuration")
68+
)
69+
70+
return parser
71+
72+
def take_action(self, parsed_args):
73+
attrs = {'name': parsed_args.name}
74+
client = self.app.client_manager.network
75+
router = client.find_router(
76+
parsed_args.router,
77+
ignore_missing=False,
78+
)
79+
attrs['router_id'] = router.id
80+
81+
if parsed_args.ip_address:
82+
attrs['ip_address'] = parsed_args.ip_address
83+
84+
port = client.find_port(parsed_args.port,
85+
ignore_missing=False)
86+
attrs['port_id'] = port.id
87+
88+
if parsed_args.description is not None:
89+
attrs['description'] = parsed_args.description
90+
91+
obj = client.create_ndp_proxy(**attrs)
92+
display_columns, columns = _get_columns(obj)
93+
data = utils.get_item_properties(obj, columns)
94+
return (display_columns, data)
95+
96+
97+
class DeleteNDPProxy(command.Command):
98+
_description = _("Delete NDP proxy")
99+
100+
def get_parser(self, prog_name):
101+
parser = super().get_parser(prog_name)
102+
parser.add_argument(
103+
'ndp_proxy',
104+
nargs="+",
105+
metavar="<ndp-proxy>",
106+
help=_("NDP proxy(s) to delete (name or ID)")
107+
)
108+
return parser
109+
110+
def take_action(self, parsed_args):
111+
client = self.app.client_manager.network
112+
result = 0
113+
114+
for ndp_proxy in parsed_args.ndp_proxy:
115+
try:
116+
obj = client.find_ndp_proxy(ndp_proxy, ignore_missing=False)
117+
client.delete_ndp_proxy(obj)
118+
except Exception as e:
119+
result += 1
120+
LOG.error(_("Failed to delete NDP proxy "
121+
"'%(ndp_proxy)s': %(e)s"),
122+
{'ndp_proxy': ndp_proxy, 'e': e})
123+
if result > 0:
124+
total = len(parsed_args.ndp_proxy)
125+
msg = (_("%(result)s of %(total)s NDP Proxy failed "
126+
"to delete.") % {'result': result, 'total': total})
127+
raise exceptions.CommandError(msg)
128+
129+
130+
class ListNDPProxy(command.Lister):
131+
_description = _("List NDP proxies")
132+
133+
def get_parser(self, prog_name):
134+
parser = super().get_parser(prog_name)
135+
parser.add_argument(
136+
'--router',
137+
metavar='<router>',
138+
help=_("List only NDP proxies belong to this router (name or ID)")
139+
)
140+
parser.add_argument(
141+
'--port',
142+
metavar='<port>',
143+
help=_("List only NDP proxies assocate to this port (name or ID)")
144+
)
145+
parser.add_argument(
146+
'--ip-address',
147+
metavar='ip-address',
148+
help=_("List only NDP proxies according to their IPv6 address")
149+
)
150+
parser.add_argument(
151+
'--project',
152+
metavar='<project>',
153+
help=_("List NDP proxies according to their project (name or ID)")
154+
)
155+
parser.add_argument(
156+
'--name',
157+
metavar='<name>',
158+
help=_("List NDP proxies according to their name")
159+
)
160+
161+
identity_common.add_project_domain_option_to_parser(parser)
162+
163+
return parser
164+
165+
def take_action(self, parsed_args):
166+
client = self.app.client_manager.network
167+
identity_client = self.app.client_manager.identity
168+
169+
columns = (
170+
'id',
171+
'name',
172+
'router_id',
173+
'ip_address',
174+
'project_id',
175+
)
176+
headers = (
177+
'ID',
178+
'Name',
179+
'Router ID',
180+
'IP Address',
181+
'Project',
182+
)
183+
184+
query = {}
185+
186+
if parsed_args.router:
187+
router = client.find_router(parsed_args.router,
188+
ignore_missing=False)
189+
query['router_id'] = router.id
190+
if parsed_args.port:
191+
port = client.find_port(parsed_args.port,
192+
ignore_missing=False)
193+
query['port_id'] = port.id
194+
if parsed_args.ip_address is not None:
195+
query['ip_address'] = parsed_args.ip_address
196+
if parsed_args.project:
197+
project_id = identity_common.find_project(
198+
identity_client,
199+
parsed_args.project,
200+
parsed_args.project_domain,
201+
).id
202+
query['project_id'] = project_id
203+
if parsed_args.name:
204+
query['name'] = parsed_args.name
205+
206+
data = client.ndp_proxies(**query)
207+
208+
return (headers,
209+
(utils.get_item_properties(
210+
s, columns,
211+
formatters={},
212+
) for s in data))
213+
214+
215+
class SetNDPProxy(command.Command):
216+
_description = _("Set NDP proxy properties")
217+
218+
def get_parser(self, prog_name):
219+
parser = super().get_parser(prog_name)
220+
parser.add_argument(
221+
'ndp_proxy',
222+
metavar='<ndp-proxy>',
223+
help=_("The ID or name of the NDP proxy to update")
224+
)
225+
parser.add_argument(
226+
'--name',
227+
metavar='<name>',
228+
help=_("Set NDP proxy name")
229+
)
230+
parser.add_argument(
231+
'--description',
232+
metavar='<description>',
233+
help=_("A text to describe/contextualize the use of "
234+
"the NDP proxy configuration")
235+
)
236+
return parser
237+
238+
def take_action(self, parsed_args):
239+
client = self.app.client_manager.network
240+
attrs = {}
241+
if parsed_args.description is not None:
242+
attrs['description'] = parsed_args.description
243+
if parsed_args.name is not None:
244+
attrs['name'] = parsed_args.name
245+
246+
obj = client.find_ndp_proxy(
247+
parsed_args.ndp_proxy, ignore_missing=False)
248+
client.update_ndp_proxy(obj, **attrs)
249+
250+
251+
class ShowNDPProxy(command.ShowOne):
252+
_description = _("Display NDP proxy details")
253+
254+
def get_parser(self, prog_name):
255+
parser = super().get_parser(prog_name)
256+
parser.add_argument(
257+
'ndp_proxy',
258+
metavar="<ndp-proxy>",
259+
help=_("The ID or name of the NDP proxy")
260+
)
261+
return parser
262+
263+
def take_action(self, parsed_args):
264+
client = self.app.client_manager.network
265+
obj = client.find_ndp_proxy(parsed_args.ndp_proxy,
266+
ignore_missing=False)
267+
display_columns, columns = _get_columns(obj)
268+
data = utils.get_item_properties(obj, columns)
269+
return (display_columns, data)

openstackclient/network/v2/router.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,21 @@ def get_parser(self, prog_name):
369369
action='store_true',
370370
help=_("Disable Source NAT on external gateway")
371371
)
372+
ndp_proxy_group = parser.add_mutually_exclusive_group()
373+
ndp_proxy_group.add_argument(
374+
'--enable-ndp-proxy',
375+
dest='enable_ndp_proxy',
376+
default=None,
377+
action='store_true',
378+
help=_("Enable IPv6 NDP proxy on external gateway")
379+
)
380+
ndp_proxy_group.add_argument(
381+
'--disable-ndp-proxy',
382+
dest='enable_ndp_proxy',
383+
default=None,
384+
action='store_false',
385+
help=_("Disable IPv6 NDP proxy on external gateway")
386+
)
372387

373388
return parser
374389

@@ -383,6 +398,14 @@ def take_action(self, parsed_args):
383398
attrs.update(
384399
self._parse_extra_properties(parsed_args.extra_properties))
385400

401+
if parsed_args.enable_ndp_proxy and not parsed_args.external_gateway:
402+
msg = (_("You must specify '--external-gateway' in order "
403+
"to enable router's NDP proxy"))
404+
raise exceptions.CommandError(msg)
405+
406+
if parsed_args.enable_ndp_proxy is not None:
407+
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
408+
386409
obj = client.create_router(**attrs)
387410
# tags cannot be set when created, so tags need to be set later.
388411
_tag.update_tags_for_set(client, obj, parsed_args)
@@ -737,6 +760,21 @@ def get_parser(self, prog_name):
737760
action='store_true',
738761
help=_("Disable Source NAT on external gateway")
739762
)
763+
ndp_proxy_group = parser.add_mutually_exclusive_group()
764+
ndp_proxy_group.add_argument(
765+
'--enable-ndp-proxy',
766+
dest='enable_ndp_proxy',
767+
default=None,
768+
action='store_true',
769+
help=_("Enable IPv6 NDP proxy on external gateway")
770+
)
771+
ndp_proxy_group.add_argument(
772+
'--disable-ndp-proxy',
773+
dest='enable_ndp_proxy',
774+
default=None,
775+
action='store_false',
776+
help=_("Disable IPv6 NDP proxy on external gateway")
777+
)
740778
qos_policy_group = parser.add_mutually_exclusive_group()
741779
qos_policy_group.add_argument(
742780
'--qos-policy',
@@ -804,6 +842,9 @@ def take_action(self, parsed_args):
804842
attrs.update(
805843
self._parse_extra_properties(parsed_args.extra_properties))
806844

845+
if parsed_args.enable_ndp_proxy is not None:
846+
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
847+
807848
if attrs:
808849
client.update_router(obj, **attrs)
809850
# tags is a subresource and it needs to be updated separately.

0 commit comments

Comments
 (0)