741 lines
24 KiB
Python
741 lines
24 KiB
Python
# Copyright 2015 Spanish National Research Council
|
|
# Copyright 2015 LIP - INDIGO-DataCloud
|
|
#
|
|
# 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 re
|
|
import uuid
|
|
|
|
import webob.dec
|
|
import webob.exc
|
|
|
|
from ooi import utils
|
|
import ooi.wsgi
|
|
|
|
|
|
application_url = "https://foo.example.org:8774/ooiv1"
|
|
|
|
tenants = {
|
|
"foo": {"id": uuid.uuid4().hex,
|
|
"name": "foo"},
|
|
"bar": {"id": uuid.uuid4().hex,
|
|
"name": "bar"},
|
|
"baz": {"id": uuid.uuid4().hex,
|
|
"name": "baz"},
|
|
}
|
|
|
|
flavors = {
|
|
1: {
|
|
"id": 1,
|
|
"name": "foo",
|
|
"vcpus": 2,
|
|
"ram": 256,
|
|
"disk": 10,
|
|
},
|
|
2: {
|
|
"id": 2,
|
|
"name": "bar",
|
|
"vcpus": 4,
|
|
"ram": 2014,
|
|
"disk": 20,
|
|
}
|
|
}
|
|
|
|
images = {
|
|
"foo": {
|
|
"id": "foo",
|
|
"name": "foo",
|
|
},
|
|
"bar": {
|
|
"id": "bar",
|
|
"name": "bar",
|
|
}
|
|
}
|
|
|
|
volumes = {
|
|
tenants["foo"]["id"]: [
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"displayName": "foo",
|
|
"size": 2,
|
|
"status": "available",
|
|
"attachments": [],
|
|
},
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"displayName": "bar",
|
|
"size": 3,
|
|
"status": "available",
|
|
"attachments": [],
|
|
},
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"displayName": "baz",
|
|
"size": 5,
|
|
"status": "available",
|
|
"attachments": [],
|
|
},
|
|
],
|
|
tenants["bar"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"displayName": "volume",
|
|
"size": 5,
|
|
"status": "in-use",
|
|
},
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"displayName": "volume-nodev",
|
|
"size": 6,
|
|
"status": "in-use",
|
|
},
|
|
],
|
|
}
|
|
|
|
pools = {
|
|
tenants["foo"]["id"]: [
|
|
{
|
|
"id": "foo",
|
|
"name": "foo",
|
|
},
|
|
{
|
|
"id": "bar",
|
|
"name": "bar",
|
|
}
|
|
],
|
|
tenants["bar"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{
|
|
"id": "public",
|
|
"name": "public",
|
|
},
|
|
],
|
|
}
|
|
|
|
linked_vm_id = uuid.uuid4().hex
|
|
|
|
allocated_ip = "192.168.253.23"
|
|
|
|
floating_ips = {
|
|
tenants["foo"]["id"]: [],
|
|
tenants["bar"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{
|
|
"fixed_ip": "10.0.0.2",
|
|
"id": uuid.uuid4().hex,
|
|
"instance_id": linked_vm_id,
|
|
"ip": "192.168.253.1",
|
|
"pool": pools[tenants["baz"]["id"]][0]["name"],
|
|
},
|
|
{
|
|
"fixed_ip": None,
|
|
"id": uuid.uuid4().hex,
|
|
"instance_id": None,
|
|
"ip": "192.168.253.2",
|
|
"pool": pools[tenants["baz"]["id"]][0]["name"],
|
|
},
|
|
],
|
|
}
|
|
|
|
networks = {
|
|
tenants["foo"]["id"]: [],
|
|
tenants["bar"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{"id": uuid.uuid4().hex},
|
|
{"id": uuid.uuid4().hex}
|
|
]
|
|
}
|
|
|
|
ports = {
|
|
tenants["foo"]["id"]: [
|
|
{
|
|
"port_id": uuid.uuid4().hex,
|
|
"fixed_ips":
|
|
[{"ip_address": uuid.uuid4().hex}],
|
|
"mac_addr": uuid.uuid4().hex,
|
|
"port_state": "DOWN",
|
|
"net_id": uuid.uuid4().hex,
|
|
"server_id": linked_vm_id
|
|
},
|
|
],
|
|
tenants["bar"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{
|
|
"port_id": uuid.uuid4().hex,
|
|
"fixed_ips": [
|
|
{"ip_address": "192.168.253.1"}
|
|
],
|
|
"mac_addr": uuid.uuid4().hex,
|
|
"port_state": "ACTIVE",
|
|
"net_id": uuid.uuid4().hex,
|
|
"server_id": linked_vm_id
|
|
},
|
|
|
|
],
|
|
}
|
|
|
|
servers = {
|
|
tenants["foo"]["id"]: [
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"name": "foo",
|
|
"flavor": {"id": flavors[1]["id"]},
|
|
"image": {"id": images["foo"]["id"]},
|
|
"status": "ACTIVE",
|
|
"security_groups":[
|
|
{"name": "group1"},
|
|
{"name": "group2"}
|
|
]
|
|
},
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"name": "bar",
|
|
"flavor": {"id": flavors[2]["id"]},
|
|
"image": {"id": images["bar"]["id"]},
|
|
"status": "SHUTOFF",
|
|
"security_groups":[
|
|
{"name": "group1"}
|
|
]
|
|
},
|
|
{
|
|
"id": uuid.uuid4().hex,
|
|
"name": "baz",
|
|
"flavor": {"id": flavors[1]["id"]},
|
|
"image": {"id": images["bar"]["id"]},
|
|
"status": "ERROR",
|
|
"security_groups":[
|
|
{"name": "group2"}
|
|
]
|
|
},
|
|
],
|
|
tenants["bar"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{
|
|
"id": linked_vm_id,
|
|
"name": "withvolume",
|
|
"flavor": {"id": flavors[1]["id"]},
|
|
"image": {"id": images["bar"]["id"]},
|
|
"status": "ACTIVE",
|
|
"os-extended-volumes:volumes_attached": [
|
|
{"id": volumes[tenants["baz"]["id"]][0]["id"]},
|
|
{"id": volumes[tenants["baz"]["id"]][1]["id"]}
|
|
],
|
|
"addresses": {
|
|
"private": [
|
|
{"addr": (
|
|
(ports[tenants["baz"]["id"]]
|
|
[0]["fixed_ips"][0]["ip_address"])
|
|
),
|
|
"OS-EXT-IPS:type": "fixed",
|
|
"OS-EXT-IPS-MAC:mac_addr": (
|
|
ports[tenants["baz"]["id"]][0]["mac_addr"]
|
|
)
|
|
},
|
|
{"addr": floating_ips[tenants["baz"]["id"]][0]["ip"],
|
|
"OS-EXT-IPS:type": "floating",
|
|
"OS-EXT-IPS-MAC:mac_addr": "1234"},
|
|
]
|
|
},
|
|
"security_groups":[
|
|
{"name": "group1"}
|
|
]
|
|
}
|
|
],
|
|
}
|
|
|
|
# avoid circular definition of attachments
|
|
volumes[tenants["baz"]["id"]][0]["attachments"] = [{
|
|
# how consistent can OpenStack be!
|
|
# depending on using /servers/os-volume_attachments
|
|
# or /os-volumes it will return different field names
|
|
"server_id": servers[tenants["baz"]["id"]][0]["id"],
|
|
"serverId": servers[tenants["baz"]["id"]][0]["id"],
|
|
"attachment_id": uuid.uuid4().hex,
|
|
"volumeId": volumes[tenants["baz"]["id"]][0]["id"],
|
|
"volume_id": volumes[tenants["baz"]["id"]][0]["id"],
|
|
"device": "/dev/vdb",
|
|
"id": volumes[tenants["baz"]["id"]][0]["id"],
|
|
}]
|
|
|
|
volumes[tenants["baz"]["id"]][1]["attachments"] = [{
|
|
"server_id": servers[tenants["baz"]["id"]][0]["id"],
|
|
"serverId": servers[tenants["baz"]["id"]][0]["id"],
|
|
"attachment_id": uuid.uuid4().hex,
|
|
"volumeId": volumes[tenants["baz"]["id"]][1]["id"],
|
|
"volume_id": volumes[tenants["baz"]["id"]][1]["id"],
|
|
"id": volumes[tenants["baz"]["id"]][0]["id"],
|
|
}]
|
|
|
|
security_groups = {
|
|
tenants["foo"]["id"]: [],
|
|
tenants["baz"]["id"]: [
|
|
{
|
|
"name": "group1",
|
|
"id": uuid.uuid4().hex,
|
|
"description": "group one",
|
|
"rules": [
|
|
{"from_port": 443,
|
|
"to_port": 443, "ip_range": {"cidr": "10.0.0.0/32"},
|
|
"ip_protocol": "tcp"},
|
|
{"from_port": "1000",
|
|
"to_port": 2000, "ip_range": {"cidr": "11.0.0.0/32"},
|
|
"ip_protocol": "udp"},
|
|
]
|
|
},
|
|
{
|
|
"name": "group2",
|
|
"id": uuid.uuid4().hex,
|
|
"description": "group two",
|
|
"rules": [
|
|
{"from_port": 80,
|
|
"to_port": 80, "ip_range": {"cidr": "10.0.0.0/32"},
|
|
"ip_protocol": "tcp"},
|
|
{"from_port": "4000",
|
|
"to_port": 7000, "ip_range": {"cidr": "13.0.0.0/32"},
|
|
"ip_protocol": "udp"},
|
|
]
|
|
}
|
|
|
|
],
|
|
tenants["bar"]["id"]: [
|
|
{
|
|
"name": "group3",
|
|
"id": uuid.uuid4().hex,
|
|
"description": "group three",
|
|
"rules": [
|
|
{"from_port": 443,
|
|
"to_port": 443, "ip_range": {"cidr": "10.0.0.0/32"},
|
|
"ip_protocol": "tcp"},
|
|
{"from_port": "1000",
|
|
"to_port": 2000, "ip_range": {"cidr": "11.0.0.0/32"},
|
|
"ip_protocol": "udp"},
|
|
]
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
def fake_query_results():
|
|
cats = []
|
|
# OCCI Core
|
|
cats.append(
|
|
'link; '
|
|
'scheme="http://schemas.ogf.org/occi/core#"; '
|
|
'class="kind"; title="link"; '
|
|
'location="%s/link/"' % application_url)
|
|
cats.append(
|
|
'resource; '
|
|
'scheme="http://schemas.ogf.org/occi/core#"; '
|
|
'class="kind"; title="resource"; '
|
|
'rel="http://schemas.ogf.org/occi/core#entity"; '
|
|
'location="%s/resource/"' % application_url)
|
|
cats.append(
|
|
'entity; '
|
|
'scheme="http://schemas.ogf.org/occi/core#"; '
|
|
'class="kind"; title="entity"; '
|
|
'location="%s/entity/"' % application_url)
|
|
|
|
# OCCI Infrastructure Compute
|
|
cats.append(
|
|
'compute; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="kind"; title="compute resource"; '
|
|
'rel="http://schemas.ogf.org/occi/core#resource"; '
|
|
'location="%s/compute/"' % application_url)
|
|
cats.append(
|
|
'start; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#"; '
|
|
'class="action"; title="start compute instance"')
|
|
cats.append(
|
|
'stop; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#"; '
|
|
'class="action"; title="stop compute instance"')
|
|
cats.append(
|
|
'restart; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#"; '
|
|
'class="action"; title="restart compute instance"')
|
|
cats.append(
|
|
'suspend; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#"; '
|
|
'class="action"; title="suspend compute instance"')
|
|
|
|
# OCCI Templates
|
|
cats.append(
|
|
'os_tpl; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="mixin"; title="OCCI OS Template"; '
|
|
'location="%s/os_tpl/"' % application_url)
|
|
cats.append(
|
|
'resource_tpl; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="mixin"; title="OCCI Resource Template"; '
|
|
'location="%s/resource_tpl/"' % application_url)
|
|
|
|
# OpenStack Images
|
|
cats.append(
|
|
'bar; '
|
|
'scheme="http://schemas.openstack.org/template/os#"; '
|
|
'class="mixin"; title="bar"; '
|
|
'rel="http://schemas.ogf.org/occi/infrastructure#os_tpl"; '
|
|
'location="%s/os_tpl/bar"' % application_url)
|
|
cats.append(
|
|
'foo; '
|
|
'scheme="http://schemas.openstack.org/template/os#"; '
|
|
'class="mixin"; title="foo"; '
|
|
'rel="http://schemas.ogf.org/occi/infrastructure#os_tpl"; '
|
|
'location="%s/os_tpl/foo"' % application_url)
|
|
|
|
# OpenStack Flavors
|
|
cats.append(
|
|
'1; '
|
|
'scheme="http://schemas.openstack.org/template/resource#"; '
|
|
'class="mixin"; title="Flavor: foo"; '
|
|
'rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl"; '
|
|
'location="%s/resource_tpl/1"' % application_url)
|
|
cats.append(
|
|
'2; '
|
|
'scheme="http://schemas.openstack.org/template/resource#"; '
|
|
'class="mixin"; title="Flavor: bar"; '
|
|
'rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl"; '
|
|
'location="%s/resource_tpl/2"' % application_url)
|
|
|
|
# OCCI Infrastructure Network
|
|
cats.append(
|
|
'network; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="kind"; title="network resource"; '
|
|
'rel="http://schemas.ogf.org/occi/core#resource"; '
|
|
'location="%s/network/"' % application_url)
|
|
cats.append(
|
|
'ipnetwork; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/network#"; '
|
|
'class="mixin"; title="IP Networking Mixin"')
|
|
cats.append(
|
|
'up; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/network/action#"; '
|
|
'class="action"; title="up network instance"')
|
|
cats.append(
|
|
'down; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/network/action#"; '
|
|
'class="action"; title="down network instance"')
|
|
cats.append(
|
|
'networkinterface; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="kind"; title="network link resource"; '
|
|
'rel="http://schemas.ogf.org/occi/core#link"; '
|
|
'location="%s/networklink/"' % application_url)
|
|
cats.append(
|
|
'ipnetworkinterface; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/'
|
|
'networkinterface#"; '
|
|
'class="mixin"; title="IP Network interface Mixin"')
|
|
|
|
# OCCI Infrastructure Storage
|
|
cats.append(
|
|
'storage; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="kind"; title="storage resource"; '
|
|
'rel="http://schemas.ogf.org/occi/core#resource"; '
|
|
'location="%s/storage/"' % application_url)
|
|
cats.append(
|
|
'storagelink; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure#"; '
|
|
'class="kind"; title="storage link resource"; '
|
|
'rel="http://schemas.ogf.org/occi/core#link"; '
|
|
'location="%s/storagelink/"' % application_url)
|
|
cats.append(
|
|
'offline; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#"; '
|
|
'class="action"; title="offline storage instance"')
|
|
cats.append(
|
|
'online; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#"; '
|
|
'class="action"; title="online storage instance"')
|
|
cats.append(
|
|
'backup; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#"; '
|
|
'class="action"; title="backup storage instance"')
|
|
cats.append(
|
|
'resize; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#"; '
|
|
'class="action"; title="resize storage instance"')
|
|
cats.append(
|
|
'snapshot; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#"; '
|
|
'class="action"; title="snapshot storage instance"')
|
|
|
|
# OpenStack contextualization
|
|
cats.append(
|
|
'user_data; '
|
|
'scheme="http://schemas.openstack.org/compute/instance#"; '
|
|
'class="mixin"; title="Contextualization extension - user_data"')
|
|
cats.append(
|
|
'public_key; '
|
|
'scheme="http://schemas.openstack.org/instance/credentials#"; '
|
|
'class="mixin"; title="Contextualization extension - public_key"')
|
|
|
|
# OCCI contextualization
|
|
cats.append(
|
|
'user_data; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/compute#"; '
|
|
'class="mixin"; title="Contextualization mixin"')
|
|
cats.append(
|
|
'ssh_key; '
|
|
'scheme="http://schemas.ogf.org/occi/infrastructure/credentials#"; '
|
|
'class="mixin"; title="Credentials mixin"')
|
|
|
|
result = []
|
|
for c in cats:
|
|
result.append(("Category", c))
|
|
return result
|
|
|
|
|
|
class FakeOpenStackFault(ooi.wsgi.Fault):
|
|
_fault_names = {
|
|
400: "badRequest",
|
|
401: "unauthorized",
|
|
403: "forbidden",
|
|
404: "itemNotFound",
|
|
405: "badMethod",
|
|
406: "notAceptable",
|
|
409: "conflictingRequest",
|
|
413: "overLimit",
|
|
415: "badMediaType",
|
|
429: "overLimit",
|
|
501: "notImplemented",
|
|
503: "serviceUnavailable"}
|
|
|
|
@webob.dec.wsgify()
|
|
def __call__(self, req):
|
|
code = self.wrapped_exc.status_int
|
|
fault_name = self._fault_names.get(code)
|
|
explanation = self.wrapped_exc.explanation
|
|
fault_data = {
|
|
fault_name: {
|
|
'code': code,
|
|
'message': explanation}}
|
|
self.wrapped_exc.body = utils.utf8(json.dumps(fault_data))
|
|
self.wrapped_exc.content_type = "application/json"
|
|
return self.wrapped_exc
|
|
|
|
|
|
class FakeApp(object):
|
|
"""Poor man's fake application."""
|
|
|
|
def __init__(self):
|
|
self.routes = {}
|
|
|
|
for tenant in tenants.values():
|
|
path = "/%s" % tenant["id"]
|
|
|
|
self._populate(path, "server", servers[tenant["id"]], actions=True)
|
|
self._populate(path, "volume", volumes[tenant["id"]], "os-volumes")
|
|
self._populate(path, "floating_ip_pool", pools[tenant["id"]],
|
|
"os-floating-ip-pools")
|
|
self._populate(path, "floating_ip", floating_ips[tenant["id"]],
|
|
"os-floating-ips")
|
|
self._populate_server_links(path, "os-interface",
|
|
"interfaceAttachments",
|
|
servers[tenant["id"]],
|
|
ports[tenant["id"]])
|
|
self._populate_server_links(path, "os-security-groups",
|
|
"security_groups",
|
|
servers[tenant["id"]],
|
|
security_groups[tenant["id"]])
|
|
self._populate(path, "security_group",
|
|
security_groups[tenant["id"]],
|
|
"os-security-groups")
|
|
# NOTE(aloga): dict_values un Py3 is not serializable in JSON
|
|
self._populate(path, "image", list(images.values()))
|
|
self._populate(path, "flavor", list(flavors.values()))
|
|
self._populate_attached_volumes(path, servers[tenant["id"]],
|
|
volumes[tenant["id"]])
|
|
|
|
def _populate(self, path_base, obj_name, obj_list,
|
|
objs_path=None, actions=[]):
|
|
objs_name = "%ss" % obj_name
|
|
if objs_path:
|
|
path = "%s/%s" % (path_base, objs_path)
|
|
else:
|
|
path = "%s/%s" % (path_base, objs_name)
|
|
objs_details_path = "%s/detail" % path
|
|
self.routes[path] = create_fake_json_resp({objs_name: obj_list})
|
|
self.routes[objs_details_path] = create_fake_json_resp(
|
|
{objs_name: obj_list})
|
|
|
|
for o in obj_list:
|
|
obj_path = "%s/%s" % (path, o["id"])
|
|
self.routes[obj_path] = create_fake_json_resp({obj_name: o})
|
|
|
|
if actions:
|
|
action_path = "%s/action" % obj_path
|
|
self.routes[action_path] = webob.Response(status=202)
|
|
|
|
def _populate_attached_volumes(self, path, server_list, vol_list):
|
|
for s in server_list:
|
|
attachments = []
|
|
if "os-extended-volumes:volumes_attached" in s:
|
|
for attach in s["os-extended-volumes:volumes_attached"]:
|
|
for v in vol_list:
|
|
if attach["id"] == v["id"]:
|
|
attachments.append(v["attachments"][0])
|
|
path_base = "%s/servers/%s/os-volume_attachments" % (path, s["id"])
|
|
self.routes[path_base] = create_fake_json_resp(
|
|
{"volumeAttachments": attachments}
|
|
)
|
|
for attach in attachments:
|
|
obj_path = "%s/%s" % (path_base, attach["id"])
|
|
self.routes[obj_path] = create_fake_json_resp(
|
|
{"volumeAttachment": attach})
|
|
|
|
def _populate_server_links(self, path, resource, obj,
|
|
servers_list, link_list):
|
|
if servers_list:
|
|
for s in servers_list:
|
|
list_obj = []
|
|
path_base = "%s/servers/%s/%s" % (
|
|
path,
|
|
s["id"],
|
|
resource
|
|
)
|
|
for l in link_list:
|
|
list_obj.append(l)
|
|
self.routes[path_base] = create_fake_json_resp(
|
|
{obj: list_obj})
|
|
|
|
@webob.dec.wsgify()
|
|
def __call__(self, req):
|
|
if req.method == "GET":
|
|
return self._do_get(req)
|
|
elif req.method == "POST":
|
|
return self._do_post(req)
|
|
elif req.method == "DELETE":
|
|
return self._do_delete(req)
|
|
|
|
def _do_create_server(self, req):
|
|
# TODO(enolfc): this should check the json is
|
|
# semantically correct
|
|
s = {"server": {"id": "foo",
|
|
"name": "foo",
|
|
"flavor": {"id": "1"},
|
|
"image": {"id": "2"},
|
|
"status": "ACTIVE"}}
|
|
return create_fake_json_resp(s)
|
|
|
|
def _do_create_volume(self, req):
|
|
# TODO(enolfc): this should check the json is
|
|
# semantically correct
|
|
s = {"volume": {"id": "foo",
|
|
"displayName": "foo",
|
|
"size": 1,
|
|
"status": "on-line"}}
|
|
return create_fake_json_resp(s)
|
|
|
|
def _do_create_attachment(self, req):
|
|
v = {"volumeAttachment": {"serverId": "foo",
|
|
"volumeId": "bar",
|
|
"device": "/dev/vdb"}}
|
|
return create_fake_json_resp(v, 202)
|
|
|
|
def _do_allocate_ip(self, req):
|
|
tenant = req.path_info.split('/')[1]
|
|
body = req.json_body.copy()
|
|
pool = body.popitem()
|
|
if pool[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, "id": 1}}
|
|
return create_fake_json_resp(ip, 202)
|
|
|
|
def _do_create_port(self, req):
|
|
req_content = req.path_info.split('/')
|
|
tenant = req_content[1]
|
|
server = req_content[3]
|
|
body = req.json_body.copy()
|
|
net = body["interfaceAttachment"]["net_id"]
|
|
port = ports[tenant]
|
|
p = {"interfaceAttachment": {
|
|
"port_id": uuid.uuid4().hex,
|
|
"fixed_ips":
|
|
[{"ip_address":
|
|
port[0]["fixed_ips"]
|
|
[0]["ip_address"]
|
|
}],
|
|
"mac_addr": port[0]["mac_addr"],
|
|
"port_state": "DOWN",
|
|
"net_id": net,
|
|
"server_id": server
|
|
}}
|
|
return create_fake_json_resp(p, 200)
|
|
|
|
def _do_post(self, req):
|
|
if req.path_info.endswith("servers"):
|
|
return self._do_create_server(req)
|
|
if req.path_info.endswith("os-volumes"):
|
|
return self._do_create_volume(req)
|
|
elif req.path_info.endswith("action"):
|
|
body = req.json_body.copy()
|
|
action = body.popitem()
|
|
if action[0] in ["os-start", "os-stop", "reboot",
|
|
"addFloatingIp", "removeFloatingIp",
|
|
"removeSecurityGroup",
|
|
"addSecurityGroup"]:
|
|
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)
|
|
elif req.path_info.endswith("os-interface"):
|
|
return self._do_create_port(req)
|
|
raise Exception
|
|
|
|
def _do_delete(self, req):
|
|
self._do_get(req)
|
|
tested_paths = {
|
|
r"/[^/]+/servers/[^/]+/os-volume_attachments/[^/]+$": 202,
|
|
r"/[^/]+/os-floating-ips/[^/]+$": 202,
|
|
r"/[^/]+/servers/[^/]+$": 204,
|
|
r"/[^/]+/os-volumes/[^/]+$": 202,
|
|
r"/[^/]+/servers/[^/]+/os-interface/[^/]+$": 204,
|
|
}
|
|
for p, st in tested_paths.items():
|
|
if re.match(p, req.path_info):
|
|
return create_fake_json_resp({}, st)
|
|
raise Exception
|
|
|
|
def _do_get(self, req):
|
|
return self._get_from_routes(req)
|
|
|
|
def _get_from_routes(self, req):
|
|
try:
|
|
ret = self.routes[req.path_info]
|
|
except KeyError:
|
|
exc = webob.exc.HTTPNotFound()
|
|
ret = FakeOpenStackFault(exc)
|
|
return ret
|
|
|
|
|
|
def create_fake_json_resp(data, status=200):
|
|
r = webob.Response()
|
|
r.headers["Content-Type"] = "application/json"
|
|
r.charset = "utf8"
|
|
r.body = json.dumps(data).encode("utf8")
|
|
r.status_code = status
|
|
return r
|