Skip to content

Commit

Permalink
Add public network support when launching an instance.
Browse files Browse the repository at this point in the history
Fixes bug 1039419.

A concept of public network has been implemented in Quantum.
To launch an instance connected to public network, we need to check public
network (whose 'shared' attribute is True) in addition to network owned by
the current tenant.

Change-Id: I128e68a8b9404056f74153bf8f576cfa8b438e19
  • Loading branch information
amotoki committed Aug 23, 2012
1 parent 5aa8847 commit 8e09b93
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 35 deletions.
57 changes: 34 additions & 23 deletions nova/network/quantumv2/api.py
@@ -1,5 +1,6 @@
# Copyright 2012 OpenStack LLC.
# All Rights Reserved
# Copyright (c) 2012 NEC Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
Expand Down Expand Up @@ -60,27 +61,43 @@ def setup_networks_on_host(self, context, instance, host=None,
teardown=False):
"""Setup or teardown the network structures."""

def _get_available_networks(self, context, project_id,
net_ids=None):
"""Return a network list available for the tenant.
The list contains networks owned by the tenant and public networks.
If net_ids specified, it searches networks with requested IDs only.
"""
quantum = quantumv2.get_client(context)

# If user has specified to attach instance only to specific
# networks, add them to **search_opts
# (1) Retrieve non-public network list owned by the tenant.
search_opts = {"tenant_id": project_id, 'shared': False}
if net_ids:
search_opts['id'] = net_ids
nets = quantum.list_networks(**search_opts).get('networks', [])
# (2) Retrieve public network list.
search_opts = {'shared': True}
if net_ids:
search_opts['id'] = net_ids
nets += quantum.list_networks(**search_opts).get('networks', [])

return nets

def allocate_for_instance(self, context, instance, **kwargs):
"""Allocate all network resources for the instance."""
quantum = quantumv2.get_client(context)
LOG.debug(_('allocate_for_instance() for %s'),
instance['display_name'])
search_opts = {}
if instance['project_id']:
search_opts.update({"tenant_id": instance['project_id']})
else:
if not instance['project_id']:
msg = _('empty project id for instance %s')
raise exception.InvalidInput(
reason=msg % instance['display_name'])

# If user has specified to attach instance only to specific
# networks, add them to **search_opts
# Tenant-only network only allowed so far
requested_networks = kwargs.get('requested_networks')
ports = {}
fixed_ips = {}
net_ids = []
if requested_networks:
net_ids = []
for network_id, fixed_ip, port_id in requested_networks:
if port_id:
port = quantum.show_port(port_id).get('port')
Expand All @@ -89,10 +106,9 @@ def allocate_for_instance(self, context, instance, **kwargs):
elif fixed_ip:
fixed_ips[network_id] = fixed_ip
net_ids.append(network_id)
search_opts['id'] = net_ids

data = quantum.list_networks(**search_opts)
nets = data.get('networks', [])
nets = self._get_available_networks(context, instance['project_id'],
net_ids)

touched_port_ids = []
created_port_ids = []
Expand Down Expand Up @@ -169,12 +185,11 @@ def remove_fixed_ip_from_instance(self, context, instance, address):
raise NotImplementedError()

def validate_networks(self, context, requested_networks):
"""Validate that the tenant has the requested networks."""
"""Validate that the tenant can use the requested networks."""
LOG.debug(_('validate_networks() for %s'),
requested_networks)
if not requested_networks:
return
search_opts = {"tenant_id": context.project_id}
net_ids = []

for (net_id, _i, port_id) in requested_networks:
Expand All @@ -191,9 +206,8 @@ def validate_networks(self, context, requested_networks):
raise exception.NetworkDuplicated(network_id=net_id)
net_ids.append(net_id)

search_opts['id'] = net_ids
data = quantumv2.get_client(context).list_networks(**search_opts)
nets = data.get('networks', [])
nets = self._get_available_networks(context, context.project_id,
net_ids)
if len(nets) != len(net_ids):
requsted_netid_set = set(net_ids)
returned_netid_set = set([net['id'] for net in nets])
Expand Down Expand Up @@ -297,11 +311,8 @@ def _build_network_info_model(self, context, instance, networks=None):
data = quantumv2.get_client(context).list_ports(**search_opts)
ports = data.get('ports', [])
if not networks:
search_opts = {}
if instance['project_id']:
search_opts.update({"tenant_id": instance['project_id']})
data = quantumv2.get_client(context).list_networks(**search_opts)
networks = data.get('networks', [])
networks = self._get_available_networks(context,
instance['project_id'])
nw_info = network_model.NetworkInfo()
for port in ports:
network_name = None
Expand All @@ -310,12 +321,12 @@ def _build_network_info_model(self, context, instance, networks=None):
network_name = net['name']
break

subnets = self._get_subnets_from_port(context, port)
network_IPs = [network_model.FixedIP(address=ip_address)
for ip_address in [ip['ip_address']
for ip in port['fixed_ips']]]
# TODO(gongysh) get floating_ips for each fixed_ip

subnets = self._get_subnets_from_port(context, port)
for subnet in subnets:
subnet['ips'] = [fixed_ip for fixed_ip in network_IPs
if fixed_ip.is_in_subnet(subnet)]
Expand Down
95 changes: 83 additions & 12 deletions nova/tests/network/test_quantumv2.py
Expand Up @@ -155,7 +155,10 @@ def setUp(self):
self.nets3 = self.nets2 + [{'id': 'my_netid3',
'name': 'my_netname3',
'tenant_id': 'my_tenantid'}]
self.nets = [self.nets1, self.nets2, self.nets3]
self.nets4 = [{'id': 'his_netid4',
'name': 'his_netname4',
'tenant_id': 'his_tenantid'}]
self.nets = [self.nets1, self.nets2, self.nets3, self.nets4]

self.port_data1 = [{'network_id': 'my_netid1',
'device_id': 'device_id1',
Expand Down Expand Up @@ -214,8 +217,10 @@ def _get_instance_nw_info(self, number):
{'ports': port_data})
nets = number == 1 and self.nets1 or self.nets2
self.moxed_client.list_networks(
tenant_id=self.instance['project_id']).AndReturn(
{'networks': nets})
tenant_id=self.instance['project_id'],
shared=False).AndReturn({'networks': nets})
self.moxed_client.list_networks(
shared=True).AndReturn({'networks': []})
for i in xrange(1, number + 1):
subnet_data = i == 1 and self.subnet_data1 or self.subnet_data2
self.moxed_client.list_subnets(
Expand Down Expand Up @@ -263,11 +268,10 @@ def _allocate_for_instance(self, net_idx=1, **kwargs):
self.instance,
networks=nets).AndReturn(None)

mox_list_network_params = dict(tenant_id=self.instance['project_id'])
ports = {}
fixed_ips = {}
req_net_ids = []
if 'requested_networks' in kwargs:
req_net_ids = []
for id, fixed_ip, port_id in kwargs['requested_networks']:
if port_id:
self.moxed_client.show_port(port_id).AndReturn(
Expand All @@ -279,12 +283,21 @@ def _allocate_for_instance(self, net_idx=1, **kwargs):
else:
fixed_ips[id] = fixed_ip
req_net_ids.append(id)
search_ids = [net['id'] for net in nets if net['id'] in req_net_ids]

mox_list_network_params['id'] = [net['id'] for net in nets
if net['id'] in req_net_ids]
mox_list_network_params = dict(tenant_id=self.instance['project_id'],
shared=False)
if search_ids:
mox_list_network_params['id'] = search_ids
self.moxed_client.list_networks(
**mox_list_network_params).AndReturn({'networks': nets})

mox_list_network_params = dict(shared=True)
if search_ids:
mox_list_network_params['id'] = search_ids
self.moxed_client.list_networks(
**mox_list_network_params).AndReturn({'networks': []})

for network in nets:
port_req_body = {
'port': {
Expand Down Expand Up @@ -349,8 +362,11 @@ def test_allocate_for_instance_ex1(self):
"""
api = quantumapi.API()
self.moxed_client.list_networks(
tenant_id=self.instance['project_id']).AndReturn(
tenant_id=self.instance['project_id'],
shared=False).AndReturn(
{'networks': self.nets2})
self.moxed_client.list_networks(shared=True).AndReturn(
{'networks': []})
index = 0
for network in self.nets2:
port_req_body = {
Expand Down Expand Up @@ -385,8 +401,11 @@ def test_allocate_for_instance_ex2(self):
"""
api = quantumapi.API()
self.moxed_client.list_networks(
tenant_id=self.instance['project_id']).AndReturn(
tenant_id=self.instance['project_id'],
shared=False).AndReturn(
{'networks': self.nets2})
self.moxed_client.list_networks(shared=True).AndReturn(
{'networks': []})
port_req_body = {
'port': {
'network_id': self.nets2[0]['id'],
Expand Down Expand Up @@ -426,8 +445,13 @@ def test_validate_networks(self):
('my_netid2', 'test2', None)]
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1', 'my_netid2']),
tenant_id=self.context.project_id).AndReturn(
tenant_id=self.context.project_id,
shared=False).AndReturn(
{'networks': self.nets2})
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1', 'my_netid2']),
shared=True).AndReturn(
{'networks': []})
self.mox.ReplayAll()
api = quantumapi.API()
api.validate_networks(self.context, requested_networks)
Expand All @@ -437,8 +461,13 @@ def test_validate_networks_ex_1(self):
('my_netid2', 'test2', None)]
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1', 'my_netid2']),
tenant_id=self.context.project_id).AndReturn(
tenant_id=self.context.project_id,
shared=False).AndReturn(
{'networks': self.nets1})
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1', 'my_netid2']),
shared=True).AndReturn(
{'networks': []})
self.mox.ReplayAll()
api = quantumapi.API()
try:
Expand All @@ -452,8 +481,13 @@ def test_validate_networks_ex_2(self):
('my_netid3', 'test3', None)]
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1', 'my_netid2', 'my_netid3']),
tenant_id=self.context.project_id).AndReturn(
tenant_id=self.context.project_id,
shared=False).AndReturn(
{'networks': self.nets1})
self.moxed_client.list_networks(
id=mox.SameElementsAs(['my_netid1', 'my_netid2', 'my_netid3']),
shared=True).AndReturn(
{'networks': []})
self.mox.ReplayAll()
api = quantumapi.API()
try:
Expand All @@ -471,3 +505,40 @@ def test_get_instance_uuids_by_ip_filter(self):
result = api.get_instance_uuids_by_ip_filter(self.context, filters)
self.assertEquals('device_id1', result[0]['instance_uuid'])
self.assertEquals('device_id2', result[1]['instance_uuid'])

def _get_available_networks(self, prv_nets, pub_nets, req_ids=None):
api = quantumapi.API()
nets = prv_nets + pub_nets
mox_list_network_params = dict(tenant_id=self.instance['project_id'],
shared=False)
if req_ids:
mox_list_network_params['id'] = req_ids
self.moxed_client.list_networks(
**mox_list_network_params).AndReturn({'networks': prv_nets})
mox_list_network_params = dict(shared=True)
if req_ids:
mox_list_network_params['id'] = req_ids
self.moxed_client.list_networks(
**mox_list_network_params).AndReturn({'networks': pub_nets})

self.mox.ReplayAll()
rets = api._get_available_networks(self.context,
self.instance['project_id'],
req_ids)
self.assertEqual(rets, nets)

def test_get_available_networks_all_private(self):
self._get_available_networks(prv_nets=self.nets2, pub_nets=[])

def test_get_available_networks_all_public(self):
self._get_available_networks(prv_nets=[], pub_nets=self.nets2)

def test_get_available_networks_private_and_public(self):
self._get_available_networks(prv_nets=self.nets1, pub_nets=self.nets4)

def test_get_available_networks_with_network_ids(self):
prv_nets = [self.nets3[0]]
pub_nets = [self.nets3[-1]]
# specify only first and last network
req_ids = [net['id'] for net in (self.nets3[0], self.nets3[-1])]
self._get_available_networks(prv_nets, pub_nets, req_ids)

0 comments on commit 8e09b93

Please sign in to comment.