Skip to content

Commit e68b834

Browse files
author
Chris Yeoh
committed
Adds nova client support for nova-manage network command
Adds the following commands: - nova network-disassociate - nova network-associate-host - nova network-associate-project - nova network-create This patch depends on https://review.openstack.org/#/c/15491/ Change-Id: I44c7d0bf6ba545cf1ed6f6f8390bab836fe52f9d Implements: blueprint apis-for-nova-manage
1 parent 18deaf4 commit e68b834

File tree

5 files changed

+240
-4
lines changed

5 files changed

+240
-4
lines changed

novaclient/v1_1/networks.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,47 @@ def create(self, **kwargs):
9191
body = {"network": kwargs}
9292
return self._create('/os-networks', body, 'network')
9393

94-
def disassociate(self, network):
94+
def disassociate(self, network, disassociate_host=True,
95+
disassociate_project=True):
9596
"""
96-
Disassociate a specific network from project.
97+
Disassociate a specific network from project and/or host.
9798
98-
:param network: The ID of the :class:`Network` to get.
99+
:param network: The ID of the :class:`Network`.
100+
:param disassociate_host: Whether to disassociate the host
101+
:param disassociate_project: Whether to disassociate the project
102+
"""
103+
if disassociate_host and disassociate_project:
104+
body = {"disassociate": None}
105+
elif disassociate_project:
106+
body = {"disassociate_project": None}
107+
elif disassociate_host:
108+
body = {"disassociate_host": None}
109+
else:
110+
raise CommandError(
111+
"Must disassociate either host or project or both")
112+
113+
self.api.client.post("/os-networks/%s/action" % base.getid(network),
114+
body=body)
115+
116+
def associate_host(self, network, host):
117+
"""
118+
Associate a specific network with a host.
119+
120+
:param network: The ID of the :class:`Network`.
121+
:param host: The name of the host to associate the network with
99122
"""
100123
self.api.client.post("/os-networks/%s/action" % base.getid(network),
101-
body={"disassociate": None})
124+
body={"associate_host": host})
125+
126+
def associate_project(self, network):
127+
"""
128+
Associate a specific network with a project.
129+
130+
The project is defined by the project authenticated against
131+
132+
:param network: The ID of the :class:`Network`.
133+
"""
134+
self.api.client.post("/os-networks/add", body={"id": network})
102135

103136
def add(self, network=None):
104137
"""

novaclient/v1_1/shell.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,134 @@ def do_network_show(cs, args):
512512
utils.print_dict(network._info)
513513

514514

515+
@utils.arg('--host-only',
516+
dest='host_only',
517+
metavar='<0|1>',
518+
nargs='?',
519+
type=int,
520+
const=1,
521+
default=0)
522+
@utils.arg('--project-only',
523+
dest='project_only',
524+
metavar='<0|1>',
525+
nargs='?',
526+
type=int,
527+
const=1,
528+
default=0)
529+
@utils.arg('network',
530+
metavar='<network>',
531+
help="uuid of network")
532+
def do_network_disassociate(cs, args):
533+
"""Disassociate host and/or project from the given network."""
534+
if args.host_only:
535+
cs.networks.disassociate(args.network, True, False)
536+
elif args.project_only:
537+
cs.networks.disassociate(args.network, False, True)
538+
else:
539+
cs.networks.disassociate(args.network, True, True)
540+
541+
542+
@utils.arg('network',
543+
metavar='<network>',
544+
help="uuid of network")
545+
@utils.arg('host',
546+
metavar='<host>',
547+
help="Name of host")
548+
def do_network_associate_host(cs, args):
549+
"""Associate host with network."""
550+
cs.networks.associate_host(args.network, args.host)
551+
552+
553+
@utils.arg('network',
554+
metavar='<network>',
555+
help="uuid of network")
556+
def do_network_associate_project(cs, args):
557+
"""Associate project with network."""
558+
cs.networks.associate_project(args.network)
559+
560+
561+
def _filter_network_create_options(args):
562+
valid_args = ['label', 'cidr', 'vlan', 'vpn_start', 'cidr_v6', 'gateway',
563+
'gateway_v6', 'bridge', 'multi_host', 'dns1', 'dns2', 'uuid',
564+
'fixed_cidr', 'project_id', 'priority']
565+
kwargs = {}
566+
for k, v in args.__dict__.items():
567+
if k in valid_args and v is not None:
568+
kwargs[k] = v
569+
570+
return kwargs
571+
572+
573+
@utils.arg('label',
574+
metavar='<network_label>',
575+
help="Label for network")
576+
@utils.arg('--fixed-range-v4',
577+
dest='cidr',
578+
metavar='<x.x.x.x/yy>',
579+
help="IPv4 subnet (ex: 10.0.0.0/8)")
580+
@utils.arg('--fixed-range-v6',
581+
dest="cidr_v6",
582+
help='IPv6 subnet (ex: fe80::/64')
583+
@utils.arg('--vlan',
584+
dest='vlan',
585+
metavar='<vlan id>',
586+
help="vlan id")
587+
@utils.arg('--vpn',
588+
dest='vpn_start',
589+
metavar='<vpn start>',
590+
help="vpn start")
591+
@utils.arg('--gateway',
592+
dest="gateway",
593+
help='gateway')
594+
@utils.arg('--gateway-v6',
595+
dest="gateway_v6",
596+
help='ipv6 gateway')
597+
@utils.arg('--bridge',
598+
dest="bridge",
599+
metavar='<bridge>',
600+
help='VIFs on this network are connected to this bridge')
601+
@utils.arg('--bridge-interface',
602+
dest="bridge_interface",
603+
metavar='<bridge interface>',
604+
help='the bridge is connected to this interface')
605+
@utils.arg('--multi-host',
606+
dest="multi_host",
607+
metavar="<'T'|'F'>",
608+
help='Multi host')
609+
@utils.arg('--dns1',
610+
dest="dns1",
611+
metavar="<DNS Address>", help='First DNS')
612+
@utils.arg('--dns2',
613+
dest="dns2",
614+
metavar="<DNS Address>",
615+
help='Second DNS')
616+
@utils.arg('--uuid',
617+
dest="uuid",
618+
metavar="<network uuid>",
619+
help='Network UUID')
620+
@utils.arg('--fixed-cidr',
621+
dest="fixed_cidr",
622+
metavar='<x.x.x.x/yy>',
623+
help='IPv4 subnet for fixed IPS (ex: 10.20.0.0/16)')
624+
@utils.arg('--project-id',
625+
dest="project_id",
626+
metavar="<project id>",
627+
help='Project id')
628+
@utils.arg('--priority',
629+
dest="priority",
630+
metavar="<number>",
631+
help='Network interface priority')
632+
def do_network_create(cs, args):
633+
"""Create a network."""
634+
635+
if not (args.cidr or args.cidr_v6):
636+
raise exceptions.CommandError(
637+
"Must specify eith fixed_range_v4 or fixed_range_v6")
638+
kwargs = _filter_network_create_options(args)
639+
640+
cs.networks.create(**kwargs)
641+
642+
515643
def do_image_list(cs, _args):
516644
"""Print a list of available images to boot from."""
517645
image_list = cs.images.list()

tests/v1_1/fakes.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,3 +1164,15 @@ def get_os_fping_1(self, **kw):
11641164
}
11651165
}
11661166
)
1167+
1168+
def post_os_networks(self, **kw):
1169+
return (202, {'network': kw})
1170+
1171+
def post_os_networks_1_action(self, **kw):
1172+
return (202, None)
1173+
1174+
def post_os_networks_networktest_action(self, **kw):
1175+
return (202, None)
1176+
1177+
def post_os_networks_2_action(self, **kw):
1178+
return (202, None)

tests/v1_1/test_networks.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,30 @@ def test_create(self):
2929
{'network': {'label': 'foo'}})
3030
self.assertTrue(isinstance(f, networks.Network))
3131

32+
def test_associate_project(self):
33+
cs.networks.associate_project('networktest')
34+
cs.assert_called('POST', '/os-networks/add', {'id': 'networktest'})
35+
36+
def test_associate_host(self):
37+
cs.networks.associate_host('networktest', 'testHost')
38+
cs.assert_called('POST', '/os-networks/networktest/action',
39+
{'associate_host': 'testHost'})
40+
3241
def test_disassociate(self):
3342
cs.networks.disassociate('networkdisassociate')
3443
cs.assert_called('POST', '/os-networks/networkdisassociate/action',
3544
{'disassociate': None})
3645

46+
def test_disassociate_host_only(self):
47+
cs.networks.disassociate('networkdisassociate', True, False)
48+
cs.assert_called('POST', '/os-networks/networkdisassociate/action',
49+
{'disassociate_host': None})
50+
51+
def test_disassociate_project(self):
52+
cs.networks.disassociate('networkdisassociate', False, True)
53+
cs.assert_called('POST', '/os-networks/networkdisassociate/action',
54+
{'disassociate_project': None})
55+
3756
def test_add(self):
3857
cs.networks.add('networkadd')
3958
cs.assert_called('POST', '/os-networks/add',

tests/v1_1/test_shell.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,50 @@ def test_cloudpipe_configure(self):
629629
'vpn_port': '1234'}}
630630
self.assert_called('PUT', '/os-cloudpipe/configure-project', body)
631631

632+
def test_network_associate_host(self):
633+
self.run_command('network-associate-host 1 testHost')
634+
body = {'associate_host': 'testHost'}
635+
self.assert_called('POST', '/os-networks/1/action', body)
636+
637+
def test_network_associate_project(self):
638+
self.run_command('network-associate-project 1')
639+
body = {'id': "1"}
640+
self.assert_called('POST', '/os-networks/add', body)
641+
642+
def test_network_disassociate(self):
643+
self.run_command('network-disassociate 1')
644+
body = {'disassociate': None}
645+
self.assert_called('POST', '/os-networks/1/action', body)
646+
647+
def test_network_disassociate_host(self):
648+
self.run_command('network-disassociate --host-only 1 2')
649+
body = {'disassociate_host': None}
650+
self.assert_called('POST', '/os-networks/2/action', body)
651+
652+
def test_network_disassociate(self):
653+
self.run_command('network-disassociate --project-only 1 2')
654+
body = {'disassociate_project': None}
655+
self.assert_called('POST', '/os-networks/2/action', body)
656+
657+
def test_network_create_v4(self):
658+
self.run_command('network-create --fixed-range-v4 10.0.1.0/24 \
659+
new_network')
660+
body = {'cidr': '10.0.1.0/24', 'label': 'new_network'}
661+
self.assert_called('POST', '/os-networks', body)
662+
663+
def test_network_create_v4(self):
664+
self.run_command('network-create --fixed-range-v4 10.0.1.0/24 \
665+
--dns1 10.0.1.254 new_network')
666+
body = {'network': {'cidr': '10.0.1.0/24', 'label': 'new_network',
667+
'dns1': '10.0.1.254'}}
668+
self.assert_called('POST', '/os-networks', body)
669+
670+
def test_network_create_v6(self):
671+
self.run_command('network-create --fixed-range-v6 2001::/64 \
672+
new_network')
673+
body = {'network': {'cidr_v6': '2001::/64', 'label': 'new_network'}}
674+
self.assert_called('POST', '/os-networks', body)
675+
632676
def test_backup(self):
633677
self.run_command('backup sample-server back1 daily 1')
634678
self.assert_called('POST', '/servers/1234/action',

0 commit comments

Comments
 (0)