326 lines
12 KiB
Python
326 lines
12 KiB
Python
# 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 uuid
|
|
|
|
import webob.exc
|
|
|
|
import ooi.api.base
|
|
import ooi.api.helpers
|
|
from ooi import exception
|
|
from ooi.occi.core import collection
|
|
from ooi.occi.infrastructure import compute
|
|
from ooi.occi.infrastructure import contextualization
|
|
from ooi.occi.infrastructure import network
|
|
from ooi.occi.infrastructure import storage
|
|
from ooi.occi.infrastructure import storage_link
|
|
from ooi.occi import validator as occi_validator
|
|
from ooi.openstack import contextualization as os_contextualization
|
|
from ooi.openstack import helpers
|
|
from ooi.openstack import network as os_network
|
|
from ooi.openstack import templates
|
|
|
|
|
|
def _create_network_link(addr, comp, net_id):
|
|
net = network.NetworkResource(title="network", id=net_id)
|
|
return os_network.OSNetworkInterface(comp, net,
|
|
addr["OS-EXT-IPS-MAC:mac_addr"],
|
|
addr["addr"])
|
|
|
|
|
|
class Controller(ooi.api.base.Controller):
|
|
def __init__(self, *args, **kwargs):
|
|
super(Controller, self).__init__(*args, **kwargs)
|
|
self.compute_actions = compute.ComputeResource.actions
|
|
self.os_helper = ooi.api.helpers.OpenStackHelper(
|
|
self.app,
|
|
self.openstack_version
|
|
)
|
|
|
|
def _get_compute_resources(self, servers):
|
|
occi_compute_resources = []
|
|
if servers:
|
|
for s in servers:
|
|
s = compute.ComputeResource(title=s["name"], id=s["id"])
|
|
occi_compute_resources.append(s)
|
|
|
|
return occi_compute_resources
|
|
|
|
def index(self, req):
|
|
servers = self.os_helper.index(req)
|
|
occi_compute_resources = self._get_compute_resources(servers)
|
|
|
|
return collection.Collection(resources=occi_compute_resources)
|
|
|
|
def run_action(self, req, id, body):
|
|
action = req.GET.get("action", None)
|
|
occi_actions = [a.term for a in compute.ComputeResource.actions]
|
|
|
|
if action is None or action not in occi_actions:
|
|
raise exception.InvalidAction(action=action)
|
|
|
|
parser = req.get_parser()(req.headers, req.body)
|
|
obj = parser.parse()
|
|
|
|
server = self.os_helper.get_server(req, id)
|
|
|
|
if action == "stop":
|
|
scheme = {"category": compute.stop}
|
|
elif action == "start":
|
|
scheme = {"category": compute.start}
|
|
if server["status"] == "SUSPENDED":
|
|
action = "resume"
|
|
elif server["status"] == "PAUSED":
|
|
action = "unpause"
|
|
elif action == "restart":
|
|
scheme = {"category": compute.restart}
|
|
elif action == "suspend":
|
|
scheme = {"category": compute.suspend}
|
|
else:
|
|
raise exception.NotImplemented
|
|
|
|
validator = occi_validator.Validator(obj)
|
|
validator.validate(scheme)
|
|
|
|
self.os_helper.run_action(req, action, id)
|
|
return []
|
|
|
|
def _build_block_mapping(self, req, obj):
|
|
mappings = []
|
|
links = obj.get("links", {})
|
|
for l in links.get(storage.StorageResource.kind.type_id, []):
|
|
_, vol_id = ooi.api.helpers.get_id_with_kind(
|
|
req,
|
|
l.get("target"),
|
|
storage.StorageResource.kind)
|
|
mapping = {
|
|
"source_type": "volume",
|
|
"uuid": vol_id,
|
|
"delete_on_termination": False,
|
|
}
|
|
try:
|
|
device_name = l['attributes']['occi.storagelink.deviceid']
|
|
mapping['device_name'] = device_name
|
|
except KeyError:
|
|
pass
|
|
mappings.append(mapping)
|
|
# this needs to be there if we have a mapping
|
|
if mappings:
|
|
image = obj["schemes"][templates.OpenStackOSTemplate.scheme][0]
|
|
mappings.insert(0, {
|
|
"source_type": "image",
|
|
"destination_type": "local",
|
|
"boot_index": 0,
|
|
"delete_on_termination": True,
|
|
"uuid": image,
|
|
})
|
|
return mappings
|
|
|
|
def _get_network_from_req(self, req, obj):
|
|
networks = []
|
|
links = obj.get("links", {})
|
|
for l in links.get(network.NetworkResource.kind.type_id, []):
|
|
_, net_id = ooi.api.helpers.get_id_with_kind(
|
|
req,
|
|
l.get("target"),
|
|
network.NetworkResource.kind)
|
|
net = {'uuid': net_id}
|
|
networks.append(net)
|
|
return networks
|
|
|
|
def create(self, req, body):
|
|
parser = req.get_parser()(req.headers, req.body)
|
|
scheme = {
|
|
"category": compute.ComputeResource.kind,
|
|
"mixins": [
|
|
templates.OpenStackOSTemplate,
|
|
templates.OpenStackResourceTemplate,
|
|
],
|
|
"optional_mixins": [
|
|
contextualization.user_data,
|
|
contextualization.ssh_key,
|
|
os_contextualization.user_data,
|
|
os_contextualization.public_key,
|
|
],
|
|
"optional_links": [
|
|
storage.StorageResource.kind,
|
|
network.NetworkResource.kind,
|
|
]
|
|
|
|
}
|
|
obj = parser.parse()
|
|
validator = occi_validator.Validator(obj)
|
|
validator.validate(scheme)
|
|
|
|
attrs = obj.get("attributes", {})
|
|
name = attrs.get("occi.core.title", "OCCI_VM")
|
|
image = obj["schemes"][templates.OpenStackOSTemplate.scheme][0]
|
|
flavor = obj["schemes"][templates.OpenStackResourceTemplate.scheme][0]
|
|
user_data, key_name, key_data = None, None, None
|
|
create_key, create_key_tmp = False, False
|
|
# TODO(enolfc): deprecate OS Contextualization in the future
|
|
# meanwhile, raise 409 conflict if both contextualizations types appear
|
|
if (os_contextualization.user_data.scheme in obj["schemes"] and
|
|
contextualization.user_data.scheme in obj["schemes"]):
|
|
raise exception.OCCIMixinConflict()
|
|
|
|
if os_contextualization.user_data.scheme in obj["schemes"]:
|
|
user_data = attrs.get("org.openstack.compute.user_data")
|
|
if contextualization.user_data.scheme in obj["schemes"]:
|
|
user_data = attrs.get("occi.compute.user_data")
|
|
|
|
if (os_contextualization.public_key.scheme in obj["schemes"] and
|
|
contextualization.ssh_key.scheme in obj["schemes"]):
|
|
raise exception.OCCIMixinConflict()
|
|
|
|
key_name = key_data = None
|
|
if os_contextualization.public_key.scheme in obj["schemes"]:
|
|
key_name = attrs.get("org.openstack.credentials.publickey.name")
|
|
key_data = attrs.get("org.openstack.credentials.publickey.data")
|
|
|
|
if contextualization.ssh_key.scheme in obj["schemes"]:
|
|
if key_data or key_name:
|
|
raise exception.OCCIMixinConflict()
|
|
key_data = attrs.get("occi.credentials.ssh_key")
|
|
|
|
if key_name and key_data:
|
|
create_key = True
|
|
elif not key_name and key_data:
|
|
# NOTE(orviz) To be occi-os compliant, not
|
|
# raise exception.MissingKeypairName
|
|
key_name = uuid.uuid4().hex
|
|
create_key = True
|
|
create_key_tmp = True
|
|
|
|
if create_key:
|
|
# add keypair: if key_name already exists, a 409 HTTP code
|
|
# will be returned by OpenStack
|
|
self.os_helper.keypair_create(req, key_name,
|
|
public_key=key_data)
|
|
|
|
block_device_mapping_v2 = self._build_block_mapping(req, obj)
|
|
networks = self._get_network_from_req(req, obj)
|
|
server = self.os_helper.create_server(
|
|
req,
|
|
name,
|
|
image,
|
|
flavor,
|
|
user_data=user_data,
|
|
key_name=key_name,
|
|
block_device_mapping_v2=block_device_mapping_v2,
|
|
networks=networks
|
|
)
|
|
# The returned JSON does not contain the server name
|
|
server["name"] = name
|
|
occi_compute_resources = self._get_compute_resources([server])
|
|
|
|
if create_key_tmp:
|
|
self.os_helper.keypair_delete(req, key_name)
|
|
|
|
return collection.Collection(resources=occi_compute_resources)
|
|
|
|
def show(self, req, id):
|
|
# get info from server
|
|
s = self.os_helper.get_server(req, id)
|
|
|
|
# get info from flavor
|
|
flavor = self.os_helper.get_flavor(req, s["flavor"]["id"])
|
|
res_tpl = templates.OpenStackResourceTemplate(flavor["id"],
|
|
flavor["name"],
|
|
flavor["vcpus"],
|
|
flavor["ram"],
|
|
flavor["disk"])
|
|
|
|
# get info from image
|
|
img_id = s["image"]["id"]
|
|
try:
|
|
image = self.os_helper.get_image(req, img_id)
|
|
except webob.exc.HTTPNotFound:
|
|
image = {
|
|
"id": img_id,
|
|
"name": "None (Image with ID '%s' not found)" % img_id,
|
|
}
|
|
|
|
os_tpl = templates.OpenStackOSTemplate(image["id"],
|
|
image["name"])
|
|
|
|
# build the compute object
|
|
comp = compute.ComputeResource(title=s["name"], id=s["id"],
|
|
cores=flavor["vcpus"],
|
|
hostname=s["name"],
|
|
memory=flavor["ram"],
|
|
state=helpers.vm_state(s["status"]),
|
|
mixins=[os_tpl, res_tpl])
|
|
|
|
# storage links
|
|
vols = self.os_helper.get_server_volumes_link(req, s["id"])
|
|
for v in vols:
|
|
st = storage.StorageResource(title="storage", id=v["volumeId"])
|
|
comp.add_link(storage_link.StorageLink(comp, st,
|
|
deviceid=v["device"]))
|
|
|
|
# network links
|
|
addresses = s.get("addresses", {})
|
|
if addresses:
|
|
for addr_set in addresses.values():
|
|
for addr in addr_set:
|
|
# TODO(jorgesece): add pool information
|
|
if addr["OS-EXT-IPS:type"] == "floating":
|
|
net_id = helpers.PUBLIC_NETWORK
|
|
else:
|
|
try:
|
|
net_id = self.os_helper.get_network_id(
|
|
req, addr['OS-EXT-IPS-MAC:mac_addr'], id
|
|
)
|
|
except webob.exc.HTTPNotFound:
|
|
net_id = "FIXED"
|
|
comp.add_link(_create_network_link(addr, comp, net_id))
|
|
|
|
return comp
|
|
|
|
def _get_server_floating_ips(self, req, server_id):
|
|
s = self.os_helper.get_server(req, server_id)
|
|
addresses = s.get("addresses", {})
|
|
floating_ips = []
|
|
if addresses:
|
|
for addr_set in addresses.values():
|
|
for addr in addr_set:
|
|
if addr["OS-EXT-IPS:type"] == "floating":
|
|
floating_ips.append(addr["addr"])
|
|
return floating_ips
|
|
|
|
def _release_floating_ips(self, req, server_id):
|
|
server_ips = self._get_server_floating_ips(req, server_id)
|
|
if server_ips:
|
|
floating_ips = self.os_helper.get_floating_ips(req)
|
|
for server_ip in server_ips:
|
|
for ip in floating_ips:
|
|
if server_ip == ip["ip"]:
|
|
self.os_helper.remove_floating_ip(req, server_id,
|
|
ip["ip"])
|
|
self.os_helper.release_floating_ip(req, ip["id"])
|
|
|
|
def _delete(self, req, server_ids):
|
|
for server_id in server_ids:
|
|
self._release_floating_ips(req, server_id)
|
|
self.os_helper.delete(req, server_id)
|
|
return []
|
|
|
|
def delete(self, req, id):
|
|
return self._delete(req, [id])
|
|
|
|
def delete_all(self, req):
|
|
ids = [s["id"] for s in self.os_helper.index(req)]
|
|
return self._delete(req, ids)
|