Skip to content

Commit

Permalink
Network links.
Browse files Browse the repository at this point in the history
Adds a new controller for network links, supporting
listing, show, create and delete. Includes some tests also.
  • Loading branch information
enolfc committed Apr 30, 2015
1 parent ec543fd commit c098e32
Show file tree
Hide file tree
Showing 11 changed files with 472 additions and 8 deletions.
4 changes: 2 additions & 2 deletions ooi/api/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ def show(self, req, id):
req = self._get_req(req, path="/%s/os-floating-ips" % tenant_id)
response = req.get_response(self.app)
floating_ips = self.get_from_response(response, "floating_ips", [])
for addr_type in addresses.values():
for addr in addr_type:
for addr_set in addresses.values():
for addr in addr_set:
comp.add_link(_create_network_link(addr, comp,
floating_ips))

Expand Down
155 changes: 155 additions & 0 deletions ooi/api/network_link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-

# Copyright 2015 Spanish National Research Council
#
# 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import json

import webob.exc

from ooi.api import base
from ooi.api import network as network_api
from ooi import exception
from ooi.occi.core import collection
from ooi.occi.infrastructure import compute
from ooi.occi.infrastructure import network
from ooi.occi.infrastructure import network_link
from ooi.occi import validator as occi_validator
from ooi.openstack import network as os_network


class Controller(base.Controller):
def index(self, req):
tenant_id = req.environ["keystone.token_auth"].user.project_id
req = self._get_req(req, path="/%s/os-floating-ips" % tenant_id)
response = req.get_response(self.app)
floating_ips = self.get_from_response(response, "floating_ips", [])
occi_link_resources = []
for ip in floating_ips:
if ip["instance_id"]:
net_id = "%s/%s" % (network_api.FLOATING_PREFIX, ip["pool"])
n = network.NetworkResource(title="network", id=net_id)
c = compute.ComputeResource(title="Compute",
id=ip["instance_id"])
# TODO(enolfc): get the MAC?
iface = os_network.OSNetworkInterface(c, n, "mac", ip["ip"])
occi_link_resources.append(iface)

return collection.Collection(resources=occi_link_resources)

def _get_os_network_ip(self, req, addr):
if addr["OS-EXT-IPS:type"] == "fixed":
return network.NetworkResource(title="network", id="fixed"), None
else:
tenant_id = req.environ["keystone.token_auth"].user.project_id
req = self._get_req(req, path="/%s/os-floating-ips" % tenant_id)
response = req.get_response(self.app)
floating_ips = self.get_from_response(response, "floating_ips", [])
for ip in floating_ips:
if addr["addr"] == ip["ip"]:
net = network.NetworkResource(
title="network",
id="%s/%s" % (network_api.FLOATING_PREFIX, ip["pool"]))
return net, ip["id"]
raise webob.exc.HTTPNotFound()

def _get_interface_from_id(self, req, id):
tenant_id = req.environ["keystone.token_auth"].user.project_id
try:
server_id, server_addr = id.split('_', 1)
except ValueError:
raise webob.exc.HTTPNotFound()
path = "/%s/servers/%s" % (tenant_id, server_id)
req = self._get_req(req, path=path, method="GET")
response = req.get_response(self.app)
s = self.get_from_response(response, "server", {})
addresses = s.get("addresses", {})
for addr_set in addresses.values():
for addr in addr_set:
if addr["addr"] == server_addr:
n, ip_id = self._get_os_network_ip(req, addr)
c = compute.ComputeResource(title="Compute",
id=server_id)
# TODO(enolfc): get the MAC?
return os_network.OSNetworkInterface(c, n, "mac",
addr["addr"], ip_id)
raise webob.exc.HTTPNotFound()

def show(self, req, id):
return [self._get_interface_from_id(req, id)]

def create(self, req, body):
tenant_id = req.environ["keystone.token_auth"].user.project_id
parser = req.get_parser()(req.headers, req.body)
scheme = {"category": network_link.NetworkInterface.kind}
obj = parser.parse()
validator = occi_validator.Validator(obj)
validator.validate(scheme)

attrs = obj.get("attributes", {})
server_id = attrs.get("occi.core.source")
net_id = attrs.get("occi.core.target")

# net_id is something like "fixed" or "floating/<pool_name>"
if net_id == "fixed":
raise exception.Invalid()
try:
_, pool_name = net_id.split("/", 1)
except ValueError:
raise webob.exc.HTTPNotFound()

# Allocate IP
path = "/%s/os-floating-ips" % tenant_id
req = self._get_req(req, path="/%s/os-floating-ips" % tenant_id,
body=json.dumps({"pool": pool_name}),
method="POST")
response = req.get_response(self.app)
ip = self.get_from_response(response, "floating_ip", {})

# Add it to server
req_body = {"addFloatingIp": {"address": ip["ip"]}}
path = "/%s/servers/%s/action" % (tenant_id, server_id)
req = self._get_req(req, path=path, body=json.dumps(req_body),
method="POST")
response = req.get_response(self.app)
if response.status_int != 202:
raise base.exception_from_response(response)
n = network.NetworkResource(title="network", id=net_id)
c = compute.ComputeResource(title="Compute", id=server_id)
l = os_network.OSNetworkInterface(c, n, "mac", ip["ip"])
return collection.Collection(resources=[l])

def delete(self, req, id):
iface = self._get_interface_from_id(req, id)
if iface.target.id == "fixed":
raise exception.Invalid()

# remove floating IP
tenant_id = req.environ["keystone.token_auth"].user.project_id
req_body = {"removeFloatingIp": {"address": iface.address}}
path = "/%s/servers/%s/action" % (tenant_id, iface.source.id)
req = self._get_req(req, path=path, body=json.dumps(req_body),
method="POST")
response = req.get_response(self.app)
if response.status_int != 202:
raise base.exception_from_response(response)

# release IP
path = "/%s/os-floating-ips/%s" % (tenant_id, iface.ip_id)
req = self._get_req(req, path=path, body=json.dumps(req_body),
method="DELETE")
response = req.get_response(self.app)
if response.status_int != 202:
raise base.exception_from_response(response)
return []
2 changes: 2 additions & 0 deletions ooi/api/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,5 @@ def show(self, id, req):
st = storage.StorageResource(title=v["displayName"], id=v["id"],
size=v["size"], state=state)
return [st]

# TODO(enolfc): delete, create
1 change: 1 addition & 0 deletions ooi/api/storage_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ def delete(self, req, id):
response = req.get_response(self.app)
if response.status_int not in [202]:
raise base.exception_from_response(response)
return []
27 changes: 26 additions & 1 deletion ooi/openstack/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,41 @@ class OSNetworkInterface(network_link.NetworkInterface):
"occi.networkinterface.gateway",
"occi.networkinterface.allocation"])

def __init__(self, source, target, mac, address):
def __init__(self, source, target, mac, address, ip_id=None):
link_id = '_'.join([source.id, address])
mixins = [network_link.ip_network_interface]
super(OSNetworkInterface, self).__init__(mixins, source, target,
link_id, "eth0", mac,
"active")
self.ip_id = ip_id
self.attributes["occi.networkinterface.address"] = (
attr.MutableAttribute("occi.networkinterface.address", address))
self.attributes["occi.networkinterface.gateway"] = (
attr.MutableAttribute("occi.networkinterface.gateway", None))
self.attributes["occi.networkinterface.allocation"] = (
attr.MutableAttribute("occi.networkinterface.allocation",
"dynamic"))

@property
def address(self):
return self.attributes["occi.networkinterface.address"].value

@address.setter
def address(self, value):
self.attributes["occi.networkinterface.address"].value = value

@property
def gateway(self):
return self.attributes["occi.networkinterface.gateway"].value

@gateway.setter
def gateway(self, value):
self.attributes["occi.networkinterface.gateway"].value = value

@property
def allocation(self):
return self.attributes["occi.networkinterface.allocation"].value

@allocation.setter
def allocation(self, value):
self.attributes["occi.networkinterface.allocation"].value = value
22 changes: 21 additions & 1 deletion ooi/tests/fakes.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@

linked_vm_id = uuid.uuid4().hex

allocated_ip = "192.168.253.23"

floating_ips = {
tenants["foo"]["id"]: [],
tenants["bar"]["id"]: [],
Expand Down Expand Up @@ -447,22 +449,40 @@ def _do_create_attachment(self, req):
"device": "/dev/vdb"}}
return create_fake_json_resp(v, 202)

def _do_allocate_ip(self, req):
body = req.json_body.copy()
pool = body.popitem()
tenant = req.path_info.split('/')[1]
for p in pools[tenant]:
if p["name"] == pool[1]:
break
else:
exc = webob.exc.HTTPNotFound()
return FakeOpenStackFault(exc)
ip = {"floating_ip": {"ip": allocated_ip}}
return create_fake_json_resp(ip, 202)

def _do_post(self, req):
if req.path_info.endswith("servers"):
return self._do_create_server(req)
elif req.path_info.endswith("action"):
body = req.json_body.copy()
action = body.popitem()
if action[0] in ["os-start", "os-stop", "reboot"]:
if action[0] in ["os-start", "os-stop", "reboot",
"addFloatingIp", "removeFloatingIp"]:
return self._get_from_routes(req)
elif req.path_info.endswith("os-volume_attachments"):
return self._do_create_attachment(req)
elif req.path_info.endswith("os-floating-ips"):
return self._do_allocate_ip(req)
raise Exception

def _do_delete(self, req):
self._do_get(req)
if "os-volume_attachments" in req.path_info:
return create_fake_json_resp({}, 202)
if "os-floating-ips" in req.path_info:
return create_fake_json_resp({}, 202)
raise Exception

def _do_get(self, req):
Expand Down
1 change: 0 additions & 1 deletion ooi/tests/middleware/test_compute_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def test_list_vms(self):

for url in ("/compute/", "/compute"):
req = self._build_req(url, tenant["id"], method="GET")

resp = req.get_response(app)

self.assertEqual(200, resp.status_code)
Expand Down
2 changes: 1 addition & 1 deletion ooi/tests/middleware/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,4 @@ def assertResultIncludesLink(self, link_id, source, target, result):
attrs = set([s.strip() for s in val.split(";")])
if expected_attrs.issubset(attrs):
return
self.fail("Failed to find %s in %s." % expected_attrs, result)
self.fail("Failed to find %s in %s." % (expected_attrs, result))
Loading

0 comments on commit c098e32

Please sign in to comment.