Skip to content

Commit

Permalink
* Rework osapi to use network API not FK backref
Browse files Browse the repository at this point in the history
* Fixes lp854585

Change-Id: I270794a08a1bfafe7af427cd31f1f60df1faa4ba
  • Loading branch information
jkoelker committed Sep 25, 2011
1 parent bca7dd3 commit 8be1c68
Show file tree
Hide file tree
Showing 15 changed files with 441 additions and 329 deletions.
93 changes: 70 additions & 23 deletions nova/api/openstack/ips.py
Expand Up @@ -16,29 +16,34 @@
# under the License.

from lxml import etree
import time

from webob import exc

import nova
import nova.api.openstack.views.addresses
from nova import log as logging
from nova import flags
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import db


LOG = logging.getLogger('nova.api.openstack.ips')
FLAGS = flags.FLAGS


class Controller(object):
"""The servers addresses API controller for the Openstack API."""

def __init__(self):
self.compute_api = nova.compute.API()
self.network_api = nova.network.API()

def _get_instance(self, req, server_id):
def _get_instance(self, context, server_id):
try:
instance = self.compute_api.get(
req.environ['nova.context'], server_id)
instance = self.compute_api.get(context, server_id)
except nova.exception.NotFound:
raise exc.HTTPNotFound()
msg = _("Instance does not exist")
raise exc.HTTPNotFound(explanation=msg)
return instance

def create(self, req, server_id, body):
Expand All @@ -51,17 +56,23 @@ def delete(self, req, server_id, id):
class ControllerV10(Controller):

def index(self, req, server_id):
instance = self._get_instance(req, server_id)
context = req.environ['nova.context']
instance = self._get_instance(context, server_id)
networks = _get_networks_for_instance(context, self.network_api,
instance)
builder = nova.api.openstack.views.addresses.ViewBuilderV10()
return {'addresses': builder.build(instance)}
return {'addresses': builder.build(networks)}

def show(self, req, server_id, id):
instance = self._get_instance(req, server_id)
context = req.environ['nova.context']
instance = self._get_instance(context, server_id)
networks = _get_networks_for_instance(context, self.network_api,
instance)
builder = self._get_view_builder(req)
if id == 'private':
view = builder.build_private_parts(instance)
view = builder.build_private_parts(networks)
elif id == 'public':
view = builder.build_public_parts(instance)
view = builder.build_public_parts(networks)
else:
msg = _("Only private and public networks available")
raise exc.HTTPNotFound(explanation=msg)
Expand All @@ -76,28 +87,25 @@ class ControllerV11(Controller):

def index(self, req, server_id):
context = req.environ['nova.context']
interfaces = self._get_virtual_interfaces(context, server_id)
networks = self._get_view_builder(req).build(interfaces)
return {'addresses': networks}
instance = self._get_instance(context, server_id)
networks = _get_networks_for_instance(context, self.network_api,
instance)
return {'addresses': self._get_view_builder(req).build(networks)}

def show(self, req, server_id, id):
context = req.environ['nova.context']
interfaces = self._get_virtual_interfaces(context, server_id)
network = self._get_view_builder(req).build_network(interfaces, id)
instance = self._get_instance(context, server_id)
networks = _get_networks_for_instance(context, self.network_api,
instance)

network = self._get_view_builder(req).build_network(networks, id)

if network is None:
msg = _("Instance is not a member of specified network")
raise exc.HTTPNotFound(explanation=msg)

return network

def _get_virtual_interfaces(self, context, server_id):
try:
return db.api.virtual_interface_get_by_instance(context, server_id)
except nova.exception.InstanceNotFound:
msg = _("Instance does not exist")
raise exc.HTTPNotFound(explanation=msg)

def _get_view_builder(self, req):
return nova.api.openstack.views.addresses.ViewBuilderV11()

Expand Down Expand Up @@ -135,6 +143,45 @@ def index(self, addresses_dict):
return self._to_xml(addresses)


def _get_networks_for_instance(context, network_api, instance):
"""Returns a prepared nw_info list for passing into the view
builders
We end up with a datastructure like:
{'public': {'ips': [{'addr': '10.0.0.1', 'version': 4},
{'addr': '2001::1', 'version': 6}],
'floating_ips': [{'addr': '172.16.0.1', 'version': 4},
{'addr': '172.16.2.1', 'version': 4}]},
...}
"""
def _get_floats(ip):
return network_api.get_floating_ips_by_fixed_address(context, ip)

def _emit_addr(ip, version):
return {'addr': ip, 'version': version}

if FLAGS.stub_network:
return {}

nw_info = network_api.get_instance_nw_info(context, instance)

networks = {}
for net, info in nw_info:
network = {'ips': []}
network['floating_ips'] = []
if 'ip6s' in info:
network['ips'].extend([_emit_addr(ip['ip'],
6) for ip in info['ip6s']])

for ip in info['ips']:
network['ips'].append(_emit_addr(ip['ip'], 4))
floats = [_emit_addr(addr,
4) for addr in _get_floats(ip['ip'])]
network['floating_ips'].extend(floats)
networks[info['label']] = network
return networks


def create_resource(version):
controller = {
'1.0': ControllerV10,
Expand Down
23 changes: 17 additions & 6 deletions nova/api/openstack/servers.py
Expand Up @@ -23,6 +23,7 @@
import webob

from nova import compute
from nova import network
from nova import db
from nova import exception
from nova import flags
Expand All @@ -32,7 +33,6 @@
from nova.api.openstack import create_instance_helper as helper
from nova.api.openstack import ips
from nova.api.openstack import wsgi
from nova.compute import instance_types
from nova.scheduler import api as scheduler_api
import nova.api.openstack
import nova.api.openstack.views.addresses
Expand Down Expand Up @@ -72,6 +72,7 @@ class Controller(object):

def __init__(self):
self.compute_api = compute.API()
self.network_api = network.API()
self.helper = helper.CreateInstanceHelper(self)

def index(self, req):
Expand Down Expand Up @@ -106,6 +107,11 @@ def _limit_items(self, items, req):
def _action_rebuild(self, info, request, instance_id):
raise NotImplementedError()

def _get_networks_for_instance(self, req, instance):
return ips._get_networks_for_instance(req.environ['nova.context'],
self.network_api,
instance)

def _get_servers(self, req, is_detail):
"""Returns a list of servers, taking into account any search
options specified.
Expand Down Expand Up @@ -639,12 +645,15 @@ def _flavor_id_from_req_data(self, data):
def _build_view(self, req, instance, is_detail=False):
addresses = nova.api.openstack.views.addresses.ViewBuilderV10()
builder = nova.api.openstack.views.servers.ViewBuilderV10(addresses)
return builder.build(instance, is_detail=is_detail)
networks = self._get_networks_for_instance(req, instance)
return builder.build(instance, networks, is_detail=is_detail)

def _build_list(self, req, instances, is_detail=False):
addresses = nova.api.openstack.views.addresses.ViewBuilderV10()
builder = nova.api.openstack.views.servers.ViewBuilderV10(addresses)
return builder.build_list(instances, is_detail=is_detail)
get_nw = self._get_networks_for_instance
inst_data = [(inst, get_nw(req, inst)) for inst in instances]
return builder.build_list(inst_data, is_detail=is_detail)

def _limit_items(self, items, req):
return common.limited(items, req)
Expand Down Expand Up @@ -741,8 +750,8 @@ def _build_view(self, req, instance, is_detail=False):
builder = nova.api.openstack.views.servers.ViewBuilderV11(
addresses_builder, flavor_builder, image_builder,
base_url, project_id)

return builder.build(instance, is_detail=is_detail)
networks = self._get_networks_for_instance(req, instance)
return builder.build(instance, networks, is_detail=is_detail)

def _build_list(self, req, instances, is_detail=False):
params = req.GET.copy()
Expand All @@ -761,7 +770,9 @@ def _build_list(self, req, instances, is_detail=False):
builder = nova.api.openstack.views.servers.ViewBuilderV11(
addresses_builder, flavor_builder, image_builder,
base_url, project_id)
return builder.build_list(instances, is_detail=is_detail, **params)
get_nw = self._get_networks_for_instance
inst_data = [(inst, get_nw(req, inst)) for inst in instances]
return builder.build_list(inst_data, is_detail=is_detail, **params)

def _action_change_password(self, input_dict, req, id):
context = req.environ['nova.context']
Expand Down
98 changes: 39 additions & 59 deletions nova/api/openstack/views/addresses.py
Expand Up @@ -15,10 +15,11 @@
# License for the specific language governing permissions and limitations
# under the License.

import itertools

from nova import flags
from nova import utils
from nova import log as logging
from nova.api.openstack import common


FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.views.addresses')
Expand All @@ -30,76 +31,55 @@ class ViewBuilder(object):
def build(self, inst):
raise NotImplementedError()

def _extract_ips(self, network, key=None):
if key:
chain = network[key]
else:
chain = itertools.chain(network['ips'], network['floating_ips'])
for ip in chain:
if not FLAGS.use_ipv6 and ip['version'] == 6:
continue
yield ip

class ViewBuilderV10(ViewBuilder):

def build(self, inst):
private_ips = self.build_private_parts(inst)
public_ips = self.build_public_parts(inst)
return dict(public=public_ips, private=private_ips)

def build_public_parts(self, inst):
return utils.get_from_path(inst, 'fixed_ips/floating_ips/address')

def build_private_parts(self, inst):
return utils.get_from_path(inst, 'fixed_ips/address')

class ViewBuilderV10(ViewBuilder):

class ViewBuilderV11(ViewBuilder):
def build(self, networks):
if not networks:
return dict(public=[], private=[])

def build(self, interfaces):
networks = {}
for interface in interfaces:
try:
network_label = self._extract_network_label(interface)
except TypeError:
continue
return dict(public=self.build_public_parts(networks),
private=self.build_private_parts(networks))

if network_label not in networks:
networks[network_label] = []
def build_public_parts(self, nets):
ips = [self._extract_ips(nets[label],
key='floating_ips') for label in nets]
return [ip['addr'] for ip in itertools.chain(*ips)]

ip_addresses = list(self._extract_ipv4_addresses(interface))
def build_private_parts(self, nets):
ips = [self._extract_ips(nets[label], key='ips') for label in nets]
return [ip['addr'] for ip in itertools.chain(*ips)]

if FLAGS.use_ipv6:
ipv6_address = self._extract_ipv6_address(interface)
if ipv6_address is not None:
ip_addresses.append(ipv6_address)

networks[network_label].extend(ip_addresses)
class ViewBuilderV11(ViewBuilder):

return networks
def build(self, networks):
result = {}
for network in networks:
if network not in result:
result[network] = []

def build_network(self, interfaces, requested_network):
for interface in interfaces:
try:
network_label = self._extract_network_label(interface)
except TypeError:
continue
result[network].extend(self._extract_ips(networks[network]))
return result

if network_label == requested_network:
ips = list(self._extract_ipv4_addresses(interface))
ipv6 = self._extract_ipv6_address(interface)
if ipv6 is not None:
ips.append(ipv6)
return {network_label: ips}
def build_network(self, networks, requested_network):
for network in networks:
if network == requested_network:
return {network: list(self._extract_ips(networks[network]))}
return None

def _extract_network_label(self, interface):
def _extract_network_label(self, network):
try:
return interface['network']['label']
return network['label']
except (TypeError, KeyError) as exc:
raise TypeError

def _extract_ipv4_addresses(self, interface):
for fixed_ip in interface['fixed_ips']:
yield self._build_ip_entity(fixed_ip['address'], 4)
for floating_ip in fixed_ip.get('floating_ips', []):
yield self._build_ip_entity(floating_ip['address'], 4)

def _extract_ipv6_address(self, interface):
fixed_ipv6 = interface.get('fixed_ipv6')
if fixed_ipv6 is not None:
return self._build_ip_entity(fixed_ipv6, 6)

def _build_ip_entity(self, address, version):
return {'addr': address, 'version': version}

0 comments on commit 8be1c68

Please sign in to comment.