Files
occi-os/api/compute/openstack.py
Thijs Metsch 18e93aff73 minor fixes
2012-07-04 11:04:17 +02:00

281 lines
9.8 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2012, Intel Performance Learning Solutions Ltd.
#
# 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.
"""
The compute resource backend for OpenStack.
"""
#pylint: disable=W0232,R0201
import random
from occi import backend
from api.extensions import openstack
from api.extensions import occi_future
from nova_glue import vm, security
from nova_glue import net
def get_extensions():
"""
Retrieve the OS specific extensions.
"""
return [
{
'categories': [openstack.OS_CHG_PWD,
openstack.OS_REVERT_RESIZE,
openstack.OS_CONFIRM_RESIZE,
openstack.OS_CREATE_IMAGE,
openstack.OS_ALLOC_FLOATING_IP,
openstack.OS_DEALLOC_FLOATING_IP, ],
'handler': OsComputeActionBackend(),
},
{
'categories': [openstack.OS_KEY_PAIR_EXT,
openstack.OS_ADMIN_PWD_EXT,
openstack.OS_ACCESS_IP_EXT,
openstack.OS_FLOATING_IP_EXT, ],
'handler': backend.MixinBackend(),
},
{
'categories': [occi_future.CONSOLE_LINK,
occi_future.SSH_CONSOLE,
occi_future.VNC_CONSOLE, ],
'handler': backend.KindBackend(),
},
{
'categories': [occi_future.SEC_RULE, ],
'handler': SecurityRuleBackend(),
},
{
'categories': [occi_future.SEC_GROUP, ],
'handler': SecurityGroupBackend(),
},
]
class OsComputeActionBackend(backend.ActionBackend):
"""
The OpenStackCompute backend.
"""
def action(self, entity, action, attributes, extras):
"""
This is called by pyssf when an action request is issued.
"""
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
if action == openstack.OS_CHG_PWD:
if 'org.openstack.credentials.admin_pwd' not in attributes:
msg = 'org.openstack.credentials.admin_pwd was not supplied'\
' in the request.'
raise AttributeError(msg)
new_password = attributes['org.openstack.credentials.admin_pwd']
vm.set_password_for_vm(uid, new_password, context)
elif action == openstack.OS_REVERT_RESIZE:
vm.revert_resize_vm(uid, context)
entity.attributes['occi.compute.state'] = 'inactive'
elif action == openstack.OS_CONFIRM_RESIZE:
vm.confirm_resize_vm(uid, context)
entity.attributes['occi.compute.state'] = 'active'
elif action == openstack.OS_CREATE_IMAGE:
if 'org.openstack.snapshot.image_name' not in attributes:
raise AttributeError('Missing image name')
image_name = attributes['org.openstack.snapshot.image_name']
vm.snapshot_vm(uid, image_name, context)
elif action == openstack.OS_ALLOC_FLOATING_IP:
for mixin in entity.mixins:
if mixin == openstack.OS_FLOATING_IP_EXT:
#TODO(dizz): implement support for multiple floating ips
# needs support in pyssf for URI in link
raise AttributeError('There is already a floating IP '
'assigned to the VM')
address = net.add_flaoting_ip_to_vm(uid, attributes, context)
# once the address is allocated we need to reflect that fact
# on the resource holding it.
entity.mixins.append(openstack.OS_FLOATING_IP_EXT)
entity.attributes['org.openstack.network.floating.ip'] = address
elif action == openstack.OS_DEALLOC_FLOATING_IP:
address = entity.attributes['org.openstack.network.floating.ip']
net.remove_floating_ip(uid, address, context)
# remove the mixin
for mixin in entity.mixins:
if mixin == openstack.OS_FLOATING_IP_EXT:
entity.mixins.remove(mixin)
entity.attributes.pop('org.openstack.network.floating.ip')
else:
raise AttributeError('Not an applicable action.')
# The same approach can be used to create and delete VM images.
class SecurityGroupBackend(backend.UserDefinedMixinBackend):
"""
Security Group backend.
"""
def init_sec_group(self, category, extras):
"""
Creates the security group as specified in the request.
"""
#do not recreate default openstack security groups
if (category.scheme ==
'http://schemas.openstack.org/infrastructure/security/group#'):
return
context = extras['nova_ctx']
group_name = category.term.strip()
group_description = (category.title.strip()
if category.title else group_name)
security.create_group(group_name, group_description, context)
def destroy(self, category, extras):
"""
Deletes the specified security group.
"""
context = extras['nova_ctx']
security_group = security.retrieve_group(category.term,
extras['nova_ctx'].project_id,
extras['nova_ctx'])
security.remove_group(security_group, context)
class SecurityRuleBackend(backend.KindBackend):
"""
Security rule backend.
"""
def create(self, entity, extras):
"""
Creates a security rule.
The group to add the rule to must exist.
In OCCI-speak this means the mixin must be supplied with the request
"""
sec_mixin = get_sec_mixin(entity)
context = extras['nova_ctx']
security_group = security.retrieve_group(sec_mixin.term,
extras['nova_ctx']
.project_id, context)
sg_rule = make_sec_rule(entity, security_group['id'])
if security_group_rule_exists(security_group, sg_rule):
#This rule already exists in group
msg = ('This rule already exists in group. %s') %\
str(security_group)
raise AttributeError(msg)
security.create_rule(sg_rule, context)
def delete(self, entity, extras):
"""
Deletes the security rule.
"""
context = extras['nova_ctx']
rule = security.get_rule(entity.attributes['occi.core.id'], context)
security.remove_rule(rule, context)
def make_sec_rule(entity, sec_grp_id):
"""
Create and validate the security rule.
"""
name = random.randrange(0, 99999999)
sg_rule = {'id': name}
entity.attributes['occi.core.id'] = str(sg_rule['id'])
sg_rule['parent_group_id'] = sec_grp_id
prot = \
entity.attributes['occi.network.security.protocol'].lower().strip()
if prot in ('tcp', 'udp', 'icmp'):
sg_rule['protocol'] = prot
else:
raise AttributeError('Invalid protocol defined.')
from_p = entity.attributes['occi.network.security.to'].strip()
from_p = int(from_p)
if (type(from_p) is int) and 0 < from_p <= 65535:
sg_rule['from_port'] = from_p
else:
raise AttributeError('No valid from port defined.')
to_p = entity.attributes['occi.network.security.to'].strip()
to_p = int(to_p)
if (type(to_p) is int) and 0 < to_p <= 65535:
sg_rule['to_port'] = to_p
else:
raise AttributeError('No valid to port defined.')
if from_p > to_p:
raise AttributeError('From port is bigger than to port defined.')
cidr = entity.attributes['occi.network.security.range'].strip()
if len(cidr) <= 0:
cidr = '0.0.0.0/0'
# TODO(dizz): find corresponding call in master!
#if utils.is_valid_cidr(cidr):
if True:
sg_rule['cidr'] = cidr
else:
raise AttributeError('No valid CIDR defined.')
sg_rule['group'] = {}
return sg_rule
def get_sec_mixin(entity):
"""
Get the security mixin of the supplied entity.
"""
sec_mixin_present = 0
sec_mixin = None
for mixin in entity.mixins:
if occi_future.SEC_GROUP in mixin.related:
sec_mixin = mixin
sec_mixin_present += 1
if not sec_mixin_present:
# no mixin of the type security group was found
msg = 'No security group mixin was found'
raise AttributeError(msg)
if sec_mixin_present > 1:
msg = 'More than one security group mixin was found'
raise AttributeError(msg)
return sec_mixin
def security_group_rule_exists(security_group, values):
"""
Indicates whether the specified rule values are already
defined in the given security group.
"""
# Taken directly from security_groups.py as that method is not
# directly import-able.
for rule in security_group['rules']:
is_duplicate = True
keys = ('group_id', 'cidr', 'from_port', 'to_port', 'protocol')
for key in keys:
if rule.get(key) != values.get(key):
is_duplicate = False
break
if is_duplicate:
return True
return False