Commit 5c7fc690 authored by Christos Stavrakakis's avatar Christos Stavrakakis

cyclades: Move common code to seperate function

Move common between API method and management command for creating a
server to a separate function.
parent 3c2d64d8
......@@ -31,19 +31,13 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import json
from optparse import make_option
from django.db import transaction
from django.core.management.base import BaseCommand, CommandError
from synnefo.management import common
from synnefo.db.models import VirtualMachine
from synnefo.logic.backend import create_instance
from synnefo.logic.backend_allocator import BackendAllocator
from synnefo.api import util
from synnefo.api.servers import server_created
from synnefo import quotas
from synnefo.api.servers import do_create_server
HELP_MSG = """
......@@ -77,7 +71,6 @@ class Command(BaseCommand):
help="Password for the new server")
)
@transaction.commit_manually
def handle(self, *args, **options):
if args:
raise CommandError("Command doesn't accept any arguments")
......@@ -97,84 +90,13 @@ class Command(BaseCommand):
raise CommandError("password is mandatory")
if not flavor_id:
raise CommandError("flavor-id is mandatory")
# Get Flavor
if flavor_id:
flavor = common.get_flavor(flavor_id)
if image_id:
img = common.get_image(image_id, user_id)
properties = img.get('properties', {})
image = {}
image['backend_id'] = img['location']
image['format'] = img['disk_format']
image['metadata'] = dict((key.upper(), val)
for key, val in properties.items())
else:
if not image_id:
raise CommandError("image-id is mandatory")
# Fix flavor for archipelago
disk_template, provider = util.get_flavor_provider(flavor)
if provider:
flavor.disk_template = disk_template
flavor.disk_provider = provider
flavor.disk_origin = None
if provider == 'vlmc':
flavor.disk_origin = image['checksum']
image['backend_id'] = 'null'
else:
flavor.disk_provider = None
try:
# Get Backend
if backend_id:
backend = common.get_backend(backend_id)
else:
ballocator = BackendAllocator()
backend = ballocator.allocate(user_id, flavor)
if not backend:
raise CommandError("Can not allocate VM")
# Get Public address
(network, address) = util.allocate_public_address(backend)
if address is None:
raise CommandError("Can not allocate a public address."
" No available public network.")
nic = {'ip': address, 'network': network.backend_id}
# Create the VM in DB
vm = VirtualMachine.objects.create(name=name,
backend=backend,
userid=user_id,
imageid=image_id,
flavor=flavor)
# dispatch server created signal
server_created.send(sender=vm, created_vm_params={
'img_id': image['backend_id'],
'img_passwd': password,
'img_format': str(image['format']),
'img_personality': '[]',
'img_properties': json.dumps(image['metadata']),
})
quotas.issue_and_accept_commission(vm)
except:
transaction.rollback()
raise
else:
transaction.commit()
try:
# Create the instance in Backend
jobID = create_instance(vm, nic, flavor, image)
flavor = common.get_flavor(flavor_id)
image = common.get_image(image_id, user_id)
if backend_id:
backend = common.get_backend(backend_id)
vm.backendjobid = jobID
vm.save()
self.stdout.write("Creating VM %s with IP %s in Backend %s."
" JobID: %s\n" % (vm, address, backend, jobID))
except:
transaction.rollback()
raise
else:
transaction.commit()
do_create_server(user_id, name, password, flavor, image,
backend=backend)
......@@ -245,11 +245,6 @@ def list_servers(request, detail=False):
@api.api_method(http_method='POST', user_required=True, logger=log)
# Use manual transactions. Backend and IP pool allocations need exclusive
# access (SELECT..FOR UPDATE). Running create_server with commit_on_success
# would result in backends and public networks to be locked until the job is
# sent to the Ganeti backend.
@transaction.commit_manually
def create_server(request):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
......@@ -260,44 +255,62 @@ def create_server(request):
# badRequest (400),
# serverCapacityUnavailable (503),
# overLimit (413)
req = utils.get_request_dict(request)
log.info('create_server %s', req)
user_id = request.user_uniq
try:
req = utils.get_request_dict(request)
log.info('create_server %s', req)
user_id = request.user_uniq
server = req['server']
name = server['name']
metadata = server.get('metadata', {})
assert isinstance(metadata, dict)
image_id = server['imageRef']
flavor_id = server['flavorRef']
personality = server.get('personality', [])
assert isinstance(personality, list)
except (KeyError, AssertionError):
raise faults.BadRequest("Malformed request")
# Verify that personalities are well-formed
util.verify_personality(personality)
# Get image information
image = util.get_image_dict(image_id, user_id)
# Get flavor (ensure it is active)
flavor = util.get_flavor(flavor_id, include_deleted=False)
# Generate password
password = util.random_password()
vm = do_create_server(user_id, name, password, flavor, image,
metadata=metadata, personality=personality)
server = vm_to_dict(vm, detail=True)
server['status'] = 'BUILD'
server['adminPass'] = password
response = render_server(request, server, status=202)
return response
@transaction.commit_manually
def do_create_server(userid, name, password, flavor, image, metadata={},
personality=[], network=None, backend=None):
if backend is None:
# Allocate backend to host the server. Commit after allocation to
# release the locks hold by the backend allocator.
try:
server = req['server']
name = server['name']
metadata = server.get('metadata', {})
assert isinstance(metadata, dict)
image_id = server['imageRef']
flavor_id = server['flavorRef']
personality = server.get('personality', [])
assert isinstance(personality, list)
except (KeyError, AssertionError):
raise faults.BadRequest("Malformed request")
# Verify that personalities are well-formed
util.verify_personality(personality)
# Get image information
image = util.get_image_dict(image_id, user_id)
# Get flavor (ensure it is active)
flavor = util.get_flavor(flavor_id, include_deleted=False)
# Allocate VM to backend
backend_allocator = BackendAllocator()
backend = backend_allocator.allocate(request.user_uniq, flavor)
if backend is None:
log.error("No available backends for VM with flavor %s", flavor)
raise faults.ServiceUnavailable("No available backends")
except:
transaction.rollback()
raise
else:
transaction.commit()
backend_allocator = BackendAllocator()
backend = backend_allocator.allocate(userid, flavor)
if backend is None:
log.error("No available backend for VM with flavor %s", flavor)
raise faults.ServiceUnavailable("No available backends")
except:
transaction.rollback()
raise
else:
transaction.commit()
# Fix flavor for archipelago
password = util.random_password()
disk_template, provider = util.get_flavor_provider(flavor)
if provider:
flavor.disk_template = disk_template
......@@ -310,17 +323,20 @@ def create_server(request):
flavor.disk_provider = None
try:
# Allocate IP from public network
(network, address) = util.get_public_ip(backend)
nic = {'ip': address, 'network': network.backend_id}
if network is None:
# Allocate IP from public network
(network, address) = util.get_public_ip(backend)
nic = {'ip': address, 'network': network.backend_id}
else:
address = util.get_network_free_address(network)
# We must save the VM instance now, so that it gets a valid
# vm.backend_vm_id.
vm = VirtualMachine.objects.create(
name=name,
backend=backend,
userid=user_id,
imageid=image_id,
userid=userid,
imageid=image["id"],
flavor=flavor,
action="CREATE")
......@@ -364,7 +380,7 @@ def create_server(request):
vm.save()
transaction.commit()
log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s",
user_id, vm, nic, backend, str(jobID))
userid, vm, nic, backend, str(jobID))
except GanetiApiError as e:
log.exception("Can not communicate to backend %s: %s.",
backend, e)
......@@ -380,13 +396,7 @@ def create_server(request):
transaction.rollback()
raise
server = vm_to_dict(vm, detail=True)
server['status'] = 'BUILD'
server['adminPass'] = password
response = render_server(request, server, status=202)
return response
return vm
@api.api_method(http_method='GET', user_required=True, logger=log)
......
......@@ -172,6 +172,8 @@ class ServerCreateAPITest(BaseAPITest):
if a valid request has been speficied."""
mimage.return_value = {'location': 'pithos://foo',
'checksum': '1234',
"id": 1,
"name": "test_image",
'disk_format': 'diskdump'}
mrapi().CreateInstance.return_value = 12
flavor = mfactory.FlavorFactory()
......
......@@ -158,6 +158,8 @@ def get_image_dict(image_id, user_id):
image = {}
img = get_image(image_id, user_id)
properties = img.get('properties', {})
image["id"] = img["id"]
image["name"] = img["name"]
image['backend_id'] = img['location']
image['format'] = img['disk_format']
image['metadata'] = dict((key.upper(), val)
......
......@@ -35,8 +35,7 @@ from django.core.management import CommandError
from synnefo.db.models import Backend, VirtualMachine, Network, Flavor
from snf_django.lib.api import faults
from synnefo.api.util import get_image as backend_get_image
from synnefo.api.util import validate_network_params
from synnefo.api import util
from synnefo.logic.rapi import GanetiApiError, GanetiRapiClient
from synnefo.logic.utils import (id_from_instance_name,
id_from_network_name)
......@@ -59,7 +58,7 @@ def validate_network_info(options):
gateway6 = options['gateway6']
try:
validate_network_params(subnet, gateway)
util.validate_network_params(subnet, gateway)
except (faults.BadRequest, faults.OverLimit) as e:
raise CommandError(e)
......@@ -81,7 +80,7 @@ def get_backend(backend_id):
def get_image(image_id, user_id):
if image_id:
try:
return backend_get_image(image_id, user_id)
return util.get_image_dict(image_id, user_id)
except faults.ItemNotFound:
raise CommandError("Image with ID %s not found."
" Use snf-manage image-list to find"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment