Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / servers.py @ adc46059

History | View | Annotate | Download (20.5 kB)

1 6ef51e9f Giorgos Verigakis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 ce55f211 Kostas Papadimitriou
#
3 adee02b8 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 adee02b8 Giorgos Verigakis
# without modification, are permitted provided that the following
5 adee02b8 Giorgos Verigakis
# conditions are met:
6 ce55f211 Kostas Papadimitriou
#
7 adee02b8 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 adee02b8 Giorgos Verigakis
#      disclaimer.
10 ce55f211 Kostas Papadimitriou
#
11 adee02b8 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 adee02b8 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 adee02b8 Giorgos Verigakis
#      provided with the distribution.
15 ce55f211 Kostas Papadimitriou
#
16 adee02b8 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 adee02b8 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 adee02b8 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 adee02b8 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 adee02b8 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 adee02b8 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 adee02b8 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 adee02b8 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 adee02b8 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 adee02b8 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 adee02b8 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 adee02b8 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 ce55f211 Kostas Papadimitriou
#
29 adee02b8 Giorgos Verigakis
# The views and conclusions contained in the software and
30 adee02b8 Giorgos Verigakis
# documentation are those of the authors and should not be
31 adee02b8 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 adee02b8 Giorgos Verigakis
# or implied, of GRNET S.A.
33 7e2f9d4b Giorgos Verigakis
34 fc443bcd Giorgos Verigakis
from base64 import b64decode
35 2035039b Giorgos Verigakis
from logging import getLogger
36 fc443bcd Giorgos Verigakis
37 ccd0d474 Giorgos Verigakis
from django.conf import settings
38 d8e50a39 Giorgos Verigakis
from django.conf.urls.defaults import patterns
39 4dba0480 Christos Stavrakakis
from django.db import transaction
40 7e2f9d4b Giorgos Verigakis
from django.http import HttpResponse
41 7e2f9d4b Giorgos Verigakis
from django.template.loader import render_to_string
42 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
43 7e2f9d4b Giorgos Verigakis
44 ccd0d474 Giorgos Verigakis
from synnefo.api import faults, util
45 0971fa71 Markos Gogoulos
from synnefo.api.actions import server_actions
46 d8e50a39 Giorgos Verigakis
from synnefo.api.common import method_not_allowed
47 f5b4f2a3 Christos Stavrakakis
from synnefo.db.models import Backend, VirtualMachine, VirtualMachineMetadata
48 529178b1 Giorgos Verigakis
from synnefo.logic.backend import create_instance, delete_instance
49 d8e50a39 Giorgos Verigakis
from synnefo.logic.utils import get_rsapi_state
50 529178b1 Giorgos Verigakis
from synnefo.util.rapi import GanetiApiError
51 cc7c0f44 Christos Stavrakakis
from synnefo.logic.backend_allocator import BackendAllocator
52 cc7c0f44 Christos Stavrakakis
53 cc7c0f44 Christos Stavrakakis
from django.utils import importlib
54 cc7c0f44 Christos Stavrakakis
55 7e2f9d4b Giorgos Verigakis
56 9e98ba3c Giorgos Verigakis
57 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.api')
58 7e2f9d4b Giorgos Verigakis
59 7e2f9d4b Giorgos Verigakis
urlpatterns = patterns('synnefo.api.servers',
60 7e2f9d4b Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
61 7e2f9d4b Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_servers', {'detail': True}),
62 7e2f9d4b Giorgos Verigakis
    (r'^/(\d+)(?:.json|.xml)?$', 'server_demux'),
63 b016b476 Giorgos Verigakis
    (r'^/(\d+)/action(?:.json|.xml)?$', 'server_action'),
64 b016b476 Giorgos Verigakis
    (r'^/(\d+)/ips(?:.json|.xml)?$', 'list_addresses'),
65 b016b476 Giorgos Verigakis
    (r'^/(\d+)/ips/(.+?)(?:.json|.xml)?$', 'list_addresses_by_network'),
66 d8e50a39 Giorgos Verigakis
    (r'^/(\d+)/meta(?:.json|.xml)?$', 'metadata_demux'),
67 d8e50a39 Giorgos Verigakis
    (r'^/(\d+)/meta/(.+?)(?:.json|.xml)?$', 'metadata_item_demux'),
68 c738c935 Giorgos Verigakis
    (r'^/(\d+)/stats(?:.json|.xml)?$', 'server_stats'),
69 7e2f9d4b Giorgos Verigakis
)
70 7e2f9d4b Giorgos Verigakis
71 7e2f9d4b Giorgos Verigakis
72 7e2f9d4b Giorgos Verigakis
def demux(request):
73 7e2f9d4b Giorgos Verigakis
    if request.method == 'GET':
74 7e2f9d4b Giorgos Verigakis
        return list_servers(request)
75 7e2f9d4b Giorgos Verigakis
    elif request.method == 'POST':
76 7e2f9d4b Giorgos Verigakis
        return create_server(request)
77 7e2f9d4b Giorgos Verigakis
    else:
78 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
79 7e2f9d4b Giorgos Verigakis
80 55cd40be Vangelis Koukis
81 7e2f9d4b Giorgos Verigakis
def server_demux(request, server_id):
82 7e2f9d4b Giorgos Verigakis
    if request.method == 'GET':
83 7e2f9d4b Giorgos Verigakis
        return get_server_details(request, server_id)
84 7e2f9d4b Giorgos Verigakis
    elif request.method == 'PUT':
85 7e2f9d4b Giorgos Verigakis
        return update_server_name(request, server_id)
86 7e2f9d4b Giorgos Verigakis
    elif request.method == 'DELETE':
87 7e2f9d4b Giorgos Verigakis
        return delete_server(request, server_id)
88 7e2f9d4b Giorgos Verigakis
    else:
89 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
90 d8e50a39 Giorgos Verigakis
91 55cd40be Vangelis Koukis
92 d8e50a39 Giorgos Verigakis
def metadata_demux(request, server_id):
93 d8e50a39 Giorgos Verigakis
    if request.method == 'GET':
94 d8e50a39 Giorgos Verigakis
        return list_metadata(request, server_id)
95 d8e50a39 Giorgos Verigakis
    elif request.method == 'POST':
96 d8e50a39 Giorgos Verigakis
        return update_metadata(request, server_id)
97 d8e50a39 Giorgos Verigakis
    else:
98 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
99 d8e50a39 Giorgos Verigakis
100 55cd40be Vangelis Koukis
101 d8e50a39 Giorgos Verigakis
def metadata_item_demux(request, server_id, key):
102 d8e50a39 Giorgos Verigakis
    if request.method == 'GET':
103 d8e50a39 Giorgos Verigakis
        return get_metadata_item(request, server_id, key)
104 d8e50a39 Giorgos Verigakis
    elif request.method == 'PUT':
105 d8e50a39 Giorgos Verigakis
        return create_metadata_item(request, server_id, key)
106 d8e50a39 Giorgos Verigakis
    elif request.method == 'DELETE':
107 d8e50a39 Giorgos Verigakis
        return delete_metadata_item(request, server_id, key)
108 d8e50a39 Giorgos Verigakis
    else:
109 d8e50a39 Giorgos Verigakis
        return method_not_allowed(request)
110 7e2f9d4b Giorgos Verigakis
111 08b079e2 Stavros Sachtouris
112 7fede91e Christos Stavrakakis
def nic_to_dict(nic):
113 7fede91e Christos Stavrakakis
    d = {'id': util.construct_nic_id(nic),
114 7fede91e Christos Stavrakakis
         'network_id': str(nic.network.id),
115 7fede91e Christos Stavrakakis
         'mac_address': nic.mac,
116 7fede91e Christos Stavrakakis
         'ipv4': nic.ipv4 if nic.ipv4 else None,
117 7fede91e Christos Stavrakakis
         'ipv6': nic.ipv6 if nic.ipv6 else None}
118 47c66641 Dimitris Aragiorgis
119 207b70d5 Giorgos Verigakis
    if nic.firewall_profile:
120 207b70d5 Giorgos Verigakis
        d['firewallProfile'] = nic.firewall_profile
121 d44c236b Giorgos Verigakis
    return d
122 b016b476 Giorgos Verigakis
123 55cd40be Vangelis Koukis
124 d8e50a39 Giorgos Verigakis
def vm_to_dict(vm, detail=False):
125 d8e50a39 Giorgos Verigakis
    d = dict(id=vm.id, name=vm.name)
126 7e2f9d4b Giorgos Verigakis
    if detail:
127 d8e50a39 Giorgos Verigakis
        d['status'] = get_rsapi_state(vm)
128 19da4325 Vangelis Koukis
        d['progress'] = 100 if get_rsapi_state(vm) == 'ACTIVE' \
129 19da4325 Vangelis Koukis
                        else vm.buildpercentage
130 d8e50a39 Giorgos Verigakis
        d['hostId'] = vm.hostid
131 b36f78fa Giorgos Verigakis
        d['updated'] = util.isoformat(vm.updated)
132 b36f78fa Giorgos Verigakis
        d['created'] = util.isoformat(vm.created)
133 d8e50a39 Giorgos Verigakis
        d['flavorRef'] = vm.flavor.id
134 936afb7b Giorgos Verigakis
        d['imageRef'] = vm.imageid
135 ce55f211 Kostas Papadimitriou
136 7cc3c7d9 Giorgos Verigakis
        metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
137 7e2f9d4b Giorgos Verigakis
        if metadata:
138 b9a77976 Giorgos Verigakis
            d['metadata'] = {'values': metadata}
139 aa197ee4 Vangelis Koukis
140 08b079e2 Stavros Sachtouris
        attachments = [nic_to_dict(nic) for nic in vm.nics.all()]
141 08b079e2 Stavros Sachtouris
        if attachments:
142 08b079e2 Stavros Sachtouris
            d['attachments'] = {'values': attachments}
143 7e2f9d4b Giorgos Verigakis
    return d
144 7e2f9d4b Giorgos Verigakis
145 d8e50a39 Giorgos Verigakis
146 d8e50a39 Giorgos Verigakis
def render_server(request, server, status=200):
147 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
148 b36f78fa Giorgos Verigakis
        data = render_to_string('server.xml', {
149 b36f78fa Giorgos Verigakis
            'server': server,
150 b36f78fa Giorgos Verigakis
            'is_root': True})
151 7e2f9d4b Giorgos Verigakis
    else:
152 d8e50a39 Giorgos Verigakis
        data = json.dumps({'server': server})
153 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=status)
154 aa197ee4 Vangelis Koukis
155 7e2f9d4b Giorgos Verigakis
156 b36f78fa Giorgos Verigakis
@util.api_method('GET')
157 7e2f9d4b Giorgos Verigakis
def list_servers(request, detail=False):
158 7e2f9d4b Giorgos Verigakis
    # Normal Response Codes: 200, 203
159 7e2f9d4b Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
160 7e2f9d4b Giorgos Verigakis
    #                       serviceUnavailable (503),
161 7e2f9d4b Giorgos Verigakis
    #                       unauthorized (401),
162 7e2f9d4b Giorgos Verigakis
    #                       badRequest (400),
163 7e2f9d4b Giorgos Verigakis
    #                       overLimit (413)
164 ce55f211 Kostas Papadimitriou
165 9e98ba3c Giorgos Verigakis
    log.debug('list_servers detail=%s', detail)
166 e524ed5f Kostas Papadimitriou
    user_vms = VirtualMachine.objects.filter(userid=request.user_uniq)
167 ce55f211 Kostas Papadimitriou
168 8b2515a9 Christos Stavrakakis
    since = util.isoparse(request.GET.get('changes-since'))
169 d8e50a39 Giorgos Verigakis
    if since:
170 2987cd8a Giorgos Verigakis
        user_vms = user_vms.filter(updated__gte=since)
171 d8e50a39 Giorgos Verigakis
        if not user_vms:
172 d8e50a39 Giorgos Verigakis
            return HttpResponse(status=304)
173 e524ed5f Kostas Papadimitriou
    else:
174 1d7c75e4 Kostas Papadimitriou
        user_vms = user_vms.filter(deleted=False)
175 ce55f211 Kostas Papadimitriou
176 d8e50a39 Giorgos Verigakis
    servers = [vm_to_dict(server, detail) for server in user_vms]
177 aa197ee4 Vangelis Koukis
178 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
179 b36f78fa Giorgos Verigakis
        data = render_to_string('list_servers.xml', {
180 b36f78fa Giorgos Verigakis
            'servers': servers,
181 b36f78fa Giorgos Verigakis
            'detail': detail})
182 7e2f9d4b Giorgos Verigakis
    else:
183 c36934a7 Giorgos Verigakis
        data = json.dumps({'servers': {'values': servers}})
184 aa197ee4 Vangelis Koukis
185 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=200)
186 7e2f9d4b Giorgos Verigakis
187 55cd40be Vangelis Koukis
188 b36f78fa Giorgos Verigakis
@util.api_method('POST')
189 adc46059 Christos Stavrakakis
# Use manual transactions. Backend and IP pool allocations need exclusive
190 adc46059 Christos Stavrakakis
# access (SELECT..FOR UPDATE). Running create_server with commit_on_success
191 adc46059 Christos Stavrakakis
# would result in backends and public networks to be locked until the job is
192 adc46059 Christos Stavrakakis
# sent to the Ganeti backend.
193 adc46059 Christos Stavrakakis
@transaction.commit_manually
194 7e2f9d4b Giorgos Verigakis
def create_server(request):
195 7e2f9d4b Giorgos Verigakis
    # Normal Response Code: 202
196 7e2f9d4b Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
197 7e2f9d4b Giorgos Verigakis
    #                       serviceUnavailable (503),
198 7e2f9d4b Giorgos Verigakis
    #                       unauthorized (401),
199 7e2f9d4b Giorgos Verigakis
    #                       badMediaType(415),
200 7e2f9d4b Giorgos Verigakis
    #                       itemNotFound (404),
201 7e2f9d4b Giorgos Verigakis
    #                       badRequest (400),
202 7e2f9d4b Giorgos Verigakis
    #                       serverCapacityUnavailable (503),
203 7e2f9d4b Giorgos Verigakis
    #                       overLimit (413)
204 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
205 9e98ba3c Giorgos Verigakis
    log.debug('create_server %s', req)
206 ce55f211 Kostas Papadimitriou
207 7e2f9d4b Giorgos Verigakis
    try:
208 7e2f9d4b Giorgos Verigakis
        server = req['server']
209 7e2f9d4b Giorgos Verigakis
        name = server['name']
210 bc855ce7 Giorgos Verigakis
        metadata = server.get('metadata', {})
211 bc855ce7 Giorgos Verigakis
        assert isinstance(metadata, dict)
212 529178b1 Giorgos Verigakis
        image_id = server['imageRef']
213 529178b1 Giorgos Verigakis
        flavor_id = server['flavorRef']
214 0419e0c1 Giorgos Verigakis
        personality = server.get('personality', [])
215 2b1db26f Giorgos Verigakis
        assert isinstance(personality, list)
216 bc855ce7 Giorgos Verigakis
    except (KeyError, AssertionError):
217 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
218 ce55f211 Kostas Papadimitriou
219 fc443bcd Giorgos Verigakis
    if len(personality) > settings.MAX_PERSONALITY:
220 fc443bcd Giorgos Verigakis
        raise faults.OverLimit("Maximum number of personalities exceeded")
221 ce55f211 Kostas Papadimitriou
222 fc443bcd Giorgos Verigakis
    for p in personality:
223 22a3918c Giorgos Verigakis
        # Verify that personalities are well-formed
224 fc443bcd Giorgos Verigakis
        try:
225 fc443bcd Giorgos Verigakis
            assert isinstance(p, dict)
226 22a3918c Giorgos Verigakis
            keys = set(p.keys())
227 22a3918c Giorgos Verigakis
            allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
228 22a3918c Giorgos Verigakis
            assert keys.issubset(allowed)
229 fc443bcd Giorgos Verigakis
            contents = p['contents']
230 fc443bcd Giorgos Verigakis
            if len(contents) > settings.MAX_PERSONALITY_SIZE:
231 fc443bcd Giorgos Verigakis
                # No need to decode if contents already exceed limit
232 fc443bcd Giorgos Verigakis
                raise faults.OverLimit("Maximum size of personality exceeded")
233 fc443bcd Giorgos Verigakis
            if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
234 fc443bcd Giorgos Verigakis
                raise faults.OverLimit("Maximum size of personality exceeded")
235 fc443bcd Giorgos Verigakis
        except AssertionError:
236 fc443bcd Giorgos Verigakis
            raise faults.BadRequest("Malformed personality in request")
237 ce55f211 Kostas Papadimitriou
238 3a9b3cde Giorgos Verigakis
    image = {}
239 4b3b8688 Giorgos Verigakis
    img = util.get_image(image_id, request.user_uniq)
240 6ef51e9f Giorgos Verigakis
    properties = img.get('properties', {})
241 6ef51e9f Giorgos Verigakis
    image['backend_id'] = img['location']
242 6ef51e9f Giorgos Verigakis
    image['format'] = img['disk_format']
243 6ef51e9f Giorgos Verigakis
    image['metadata'] = dict((key.upper(), val) \
244 6ef51e9f Giorgos Verigakis
                             for key, val in properties.items())
245 ce55f211 Kostas Papadimitriou
246 b36f78fa Giorgos Verigakis
    flavor = util.get_flavor(flavor_id)
247 b36f78fa Giorgos Verigakis
    password = util.random_password()
248 ce55f211 Kostas Papadimitriou
249 4b3b8688 Giorgos Verigakis
    count = VirtualMachine.objects.filter(userid=request.user_uniq,
250 6ef51e9f Giorgos Verigakis
                                          deleted=False).count()
251 ce55f211 Kostas Papadimitriou
252 ce55f211 Kostas Papadimitriou
    # get user limit
253 ce55f211 Kostas Papadimitriou
    vms_limit_for_user = \
254 ce55f211 Kostas Papadimitriou
        settings.VMS_USER_QUOTA.get(request.user_uniq,
255 ce55f211 Kostas Papadimitriou
                settings.MAX_VMS_PER_USER)
256 ce55f211 Kostas Papadimitriou
257 ce55f211 Kostas Papadimitriou
    if count >= vms_limit_for_user:
258 856804af Vangelis Koukis
        raise faults.OverLimit("Server count limit exceeded for your account.")
259 ce55f211 Kostas Papadimitriou
260 cc7c0f44 Christos Stavrakakis
    backend_allocator = BackendAllocator()
261 cc7c0f44 Christos Stavrakakis
    backend = backend_allocator.allocate(flavor)
262 adc46059 Christos Stavrakakis
263 cc7c0f44 Christos Stavrakakis
    if backend is None:
264 adc46059 Christos Stavrakakis
        transaction.rollback()
265 cc7c0f44 Christos Stavrakakis
        raise Exception
266 adc46059 Christos Stavrakakis
    transaction.commit()
267 adc46059 Christos Stavrakakis
268 adc46059 Christos Stavrakakis
    if settings.PUBLIC_ROUTED_USE_POOL:
269 adc46059 Christos Stavrakakis
        (network, address) = util.allocate_public_address(backend)
270 adc46059 Christos Stavrakakis
        if address is None:
271 adc46059 Christos Stavrakakis
            transaction.rollback()
272 adc46059 Christos Stavrakakis
            raise faults.OverLimit("Can not allocate IP for new machine."
273 adc46059 Christos Stavrakakis
                                   " Public networks are full.")
274 adc46059 Christos Stavrakakis
        transaction.commit()
275 adc46059 Christos Stavrakakis
        nic = {'ip': address, 'network': network.backend_id}
276 adc46059 Christos Stavrakakis
    else:
277 adc46059 Christos Stavrakakis
        nic = {'ip': 'pool', 'network': network.backend_id}
278 cc7c0f44 Christos Stavrakakis
279 cc7c0f44 Christos Stavrakakis
    # We must save the VM instance now, so that it gets a valid
280 cc7c0f44 Christos Stavrakakis
    # vm.backend_vm_id.
281 b36f78fa Giorgos Verigakis
    vm = VirtualMachine.objects.create(
282 b36f78fa Giorgos Verigakis
        name=name,
283 f5b4f2a3 Christos Stavrakakis
        backend=backend,
284 4b3b8688 Giorgos Verigakis
        userid=request.user_uniq,
285 3a9b3cde Giorgos Verigakis
        imageid=image_id,
286 b36f78fa Giorgos Verigakis
        flavor=flavor)
287 ce55f211 Kostas Papadimitriou
288 1c03e74e Vangelis Koukis
    try:
289 adc46059 Christos Stavrakakis
        jobID = create_instance(vm, nic, flavor, image, password, personality)
290 4f598167 Giorgos Verigakis
    except GanetiApiError:
291 1c03e74e Vangelis Koukis
        vm.delete()
292 510d53b5 Vangelis Koukis
        raise
293 aa197ee4 Vangelis Koukis
294 4161cb41 Christos Stavrakakis
    vm.backendjobid = jobID
295 4161cb41 Christos Stavrakakis
    vm.save()
296 4161cb41 Christos Stavrakakis
297 bc855ce7 Giorgos Verigakis
    for key, val in metadata.items():
298 b36f78fa Giorgos Verigakis
        VirtualMachineMetadata.objects.create(
299 b36f78fa Giorgos Verigakis
            meta_key=key,
300 b36f78fa Giorgos Verigakis
            meta_value=val,
301 b36f78fa Giorgos Verigakis
            vm=vm)
302 ce55f211 Kostas Papadimitriou
303 6ef51e9f Giorgos Verigakis
    log.info('User %s created vm with %s cpus, %s ram and %s storage',
304 4b3b8688 Giorgos Verigakis
             request.user_uniq, flavor.cpu, flavor.ram, flavor.disk)
305 ce55f211 Kostas Papadimitriou
306 d8e50a39 Giorgos Verigakis
    server = vm_to_dict(vm, detail=True)
307 d8e50a39 Giorgos Verigakis
    server['status'] = 'BUILD'
308 529178b1 Giorgos Verigakis
    server['adminPass'] = password
309 adc46059 Christos Stavrakakis
310 adc46059 Christos Stavrakakis
    respsone = render_server(request, server, status=202)
311 adc46059 Christos Stavrakakis
    transaction.commit()
312 adc46059 Christos Stavrakakis
313 adc46059 Christos Stavrakakis
    return respsone
314 7e2f9d4b Giorgos Verigakis
315 55cd40be Vangelis Koukis
316 b36f78fa Giorgos Verigakis
@util.api_method('GET')
317 7e2f9d4b Giorgos Verigakis
def get_server_details(request, server_id):
318 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
319 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
320 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
321 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
322 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
323 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
324 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
325 ce55f211 Kostas Papadimitriou
326 9e98ba3c Giorgos Verigakis
    log.debug('get_server_details %s', server_id)
327 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
328 d8e50a39 Giorgos Verigakis
    server = vm_to_dict(vm, detail=True)
329 d8e50a39 Giorgos Verigakis
    return render_server(request, server)
330 7e2f9d4b Giorgos Verigakis
331 55cd40be Vangelis Koukis
332 b36f78fa Giorgos Verigakis
@util.api_method('PUT')
333 c36934a7 Giorgos Verigakis
def update_server_name(request, server_id):
334 c36934a7 Giorgos Verigakis
    # Normal Response Code: 204
335 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
336 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
337 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
338 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
339 c36934a7 Giorgos Verigakis
    #                       badMediaType(415),
340 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
341 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
342 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
343 aa197ee4 Vangelis Koukis
344 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
345 9e98ba3c Giorgos Verigakis
    log.debug('update_server_name %s %s', server_id, req)
346 ce55f211 Kostas Papadimitriou
347 7e2f9d4b Giorgos Verigakis
    try:
348 7e2f9d4b Giorgos Verigakis
        name = req['server']['name']
349 529178b1 Giorgos Verigakis
    except (TypeError, KeyError):
350 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
351 aa197ee4 Vangelis Koukis
352 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
353 d8e50a39 Giorgos Verigakis
    vm.name = name
354 d8e50a39 Giorgos Verigakis
    vm.save()
355 aa197ee4 Vangelis Koukis
356 7e2f9d4b Giorgos Verigakis
    return HttpResponse(status=204)
357 7e2f9d4b Giorgos Verigakis
358 55cd40be Vangelis Koukis
359 b36f78fa Giorgos Verigakis
@util.api_method('DELETE')
360 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
361 7e2f9d4b Giorgos Verigakis
def delete_server(request, server_id):
362 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 204
363 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
364 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
365 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
366 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
367 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
368 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
369 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
370 ce55f211 Kostas Papadimitriou
371 9e98ba3c Giorgos Verigakis
    log.debug('delete_server %s', server_id)
372 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
373 529178b1 Giorgos Verigakis
    delete_instance(vm)
374 7e2f9d4b Giorgos Verigakis
    return HttpResponse(status=204)
375 b016b476 Giorgos Verigakis
376 55cd40be Vangelis Koukis
377 b36f78fa Giorgos Verigakis
@util.api_method('POST')
378 b016b476 Giorgos Verigakis
def server_action(request, server_id):
379 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
380 9e98ba3c Giorgos Verigakis
    log.debug('server_action %s %s', server_id, req)
381 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
382 b016b476 Giorgos Verigakis
    if len(req) != 1:
383 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
384 aa197ee4 Vangelis Koukis
385 b016b476 Giorgos Verigakis
    key = req.keys()[0]
386 d8e50a39 Giorgos Verigakis
    val = req[key]
387 aa197ee4 Vangelis Koukis
388 d8e50a39 Giorgos Verigakis
    try:
389 d8e50a39 Giorgos Verigakis
        assert isinstance(val, dict)
390 20a51b1e Giorgos Verigakis
        return server_actions[key](request, vm, req[key])
391 d8e50a39 Giorgos Verigakis
    except KeyError:
392 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Unknown action")
393 d8e50a39 Giorgos Verigakis
    except AssertionError:
394 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Invalid argument")
395 b016b476 Giorgos Verigakis
396 55cd40be Vangelis Koukis
397 b36f78fa Giorgos Verigakis
@util.api_method('GET')
398 b016b476 Giorgos Verigakis
def list_addresses(request, server_id):
399 b016b476 Giorgos Verigakis
    # Normal Response Codes: 200, 203
400 b016b476 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
401 b016b476 Giorgos Verigakis
    #                       serviceUnavailable (503),
402 b016b476 Giorgos Verigakis
    #                       unauthorized (401),
403 b016b476 Giorgos Verigakis
    #                       badRequest (400),
404 b016b476 Giorgos Verigakis
    #                       overLimit (413)
405 ce55f211 Kostas Papadimitriou
406 9e98ba3c Giorgos Verigakis
    log.debug('list_addresses %s', server_id)
407 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
408 d44c236b Giorgos Verigakis
    addresses = [nic_to_dict(nic) for nic in vm.nics.all()]
409 ce55f211 Kostas Papadimitriou
410 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
411 b016b476 Giorgos Verigakis
        data = render_to_string('list_addresses.xml', {'addresses': addresses})
412 b016b476 Giorgos Verigakis
    else:
413 b016b476 Giorgos Verigakis
        data = json.dumps({'addresses': {'values': addresses}})
414 aa197ee4 Vangelis Koukis
415 b016b476 Giorgos Verigakis
    return HttpResponse(data, status=200)
416 b016b476 Giorgos Verigakis
417 55cd40be Vangelis Koukis
418 b36f78fa Giorgos Verigakis
@util.api_method('GET')
419 b016b476 Giorgos Verigakis
def list_addresses_by_network(request, server_id, network_id):
420 b016b476 Giorgos Verigakis
    # Normal Response Codes: 200, 203
421 b016b476 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
422 b016b476 Giorgos Verigakis
    #                       serviceUnavailable (503),
423 b016b476 Giorgos Verigakis
    #                       unauthorized (401),
424 b016b476 Giorgos Verigakis
    #                       badRequest (400),
425 b016b476 Giorgos Verigakis
    #                       itemNotFound (404),
426 b016b476 Giorgos Verigakis
    #                       overLimit (413)
427 ce55f211 Kostas Papadimitriou
428 9e98ba3c Giorgos Verigakis
    log.debug('list_addresses_by_network %s %s', server_id, network_id)
429 4b3b8688 Giorgos Verigakis
    machine = util.get_vm(server_id, request.user_uniq)
430 4b3b8688 Giorgos Verigakis
    network = util.get_network(network_id, request.user_uniq)
431 b36f78fa Giorgos Verigakis
    nic = util.get_nic(machine, network)
432 d44c236b Giorgos Verigakis
    address = nic_to_dict(nic)
433 ce55f211 Kostas Papadimitriou
434 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
435 b016b476 Giorgos Verigakis
        data = render_to_string('address.xml', {'address': address})
436 b016b476 Giorgos Verigakis
    else:
437 b016b476 Giorgos Verigakis
        data = json.dumps({'network': address})
438 aa197ee4 Vangelis Koukis
439 b016b476 Giorgos Verigakis
    return HttpResponse(data, status=200)
440 d8e50a39 Giorgos Verigakis
441 55cd40be Vangelis Koukis
442 b36f78fa Giorgos Verigakis
@util.api_method('GET')
443 d8e50a39 Giorgos Verigakis
def list_metadata(request, server_id):
444 d8e50a39 Giorgos Verigakis
    # Normal Response Codes: 200, 203
445 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
446 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
447 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
448 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
449 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
450 ce55f211 Kostas Papadimitriou
451 9e98ba3c Giorgos Verigakis
    log.debug('list_server_metadata %s', server_id)
452 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
453 7cc3c7d9 Giorgos Verigakis
    metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
454 b36f78fa Giorgos Verigakis
    return util.render_metadata(request, metadata, use_values=True, status=200)
455 d8e50a39 Giorgos Verigakis
456 55cd40be Vangelis Koukis
457 b36f78fa Giorgos Verigakis
@util.api_method('POST')
458 d8e50a39 Giorgos Verigakis
def update_metadata(request, server_id):
459 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 201
460 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
461 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
462 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
463 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
464 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
465 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
466 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
467 ce55f211 Kostas Papadimitriou
468 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
469 9e98ba3c Giorgos Verigakis
    log.debug('update_server_metadata %s %s', server_id, req)
470 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
471 d8e50a39 Giorgos Verigakis
    try:
472 d8e50a39 Giorgos Verigakis
        metadata = req['metadata']
473 d8e50a39 Giorgos Verigakis
        assert isinstance(metadata, dict)
474 d8e50a39 Giorgos Verigakis
    except (KeyError, AssertionError):
475 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
476 ce55f211 Kostas Papadimitriou
477 7cc3c7d9 Giorgos Verigakis
    for key, val in metadata.items():
478 7cc3c7d9 Giorgos Verigakis
        meta, created = vm.metadata.get_or_create(meta_key=key)
479 7cc3c7d9 Giorgos Verigakis
        meta.meta_value = val
480 7cc3c7d9 Giorgos Verigakis
        meta.save()
481 ce55f211 Kostas Papadimitriou
482 7cc3c7d9 Giorgos Verigakis
    vm.save()
483 7cc3c7d9 Giorgos Verigakis
    vm_meta = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
484 7cc3c7d9 Giorgos Verigakis
    return util.render_metadata(request, vm_meta, status=201)
485 d8e50a39 Giorgos Verigakis
486 55cd40be Vangelis Koukis
487 b36f78fa Giorgos Verigakis
@util.api_method('GET')
488 d8e50a39 Giorgos Verigakis
def get_metadata_item(request, server_id, key):
489 d8e50a39 Giorgos Verigakis
    # Normal Response Codes: 200, 203
490 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
491 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
492 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
493 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
494 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
495 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
496 ce55f211 Kostas Papadimitriou
497 9e98ba3c Giorgos Verigakis
    log.debug('get_server_metadata_item %s %s', server_id, key)
498 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
499 b36f78fa Giorgos Verigakis
    meta = util.get_vm_meta(vm, key)
500 62b894c0 Giorgos Verigakis
    d = {meta.meta_key: meta.meta_value}
501 62b894c0 Giorgos Verigakis
    return util.render_meta(request, d, status=200)
502 d8e50a39 Giorgos Verigakis
503 55cd40be Vangelis Koukis
504 b36f78fa Giorgos Verigakis
@util.api_method('PUT')
505 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
506 d8e50a39 Giorgos Verigakis
def create_metadata_item(request, server_id, key):
507 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 201
508 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
509 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
510 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
511 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
512 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
513 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
514 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
515 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
516 ce55f211 Kostas Papadimitriou
517 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
518 9e98ba3c Giorgos Verigakis
    log.debug('create_server_metadata_item %s %s %s', server_id, key, req)
519 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
520 d8e50a39 Giorgos Verigakis
    try:
521 d8e50a39 Giorgos Verigakis
        metadict = req['meta']
522 d8e50a39 Giorgos Verigakis
        assert isinstance(metadict, dict)
523 d8e50a39 Giorgos Verigakis
        assert len(metadict) == 1
524 d8e50a39 Giorgos Verigakis
        assert key in metadict
525 d8e50a39 Giorgos Verigakis
    except (KeyError, AssertionError):
526 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
527 ce55f211 Kostas Papadimitriou
528 b36f78fa Giorgos Verigakis
    meta, created = VirtualMachineMetadata.objects.get_or_create(
529 b36f78fa Giorgos Verigakis
        meta_key=key,
530 b36f78fa Giorgos Verigakis
        vm=vm)
531 ce55f211 Kostas Papadimitriou
532 d8e50a39 Giorgos Verigakis
    meta.meta_value = metadict[key]
533 d8e50a39 Giorgos Verigakis
    meta.save()
534 5509b599 Giorgos Verigakis
    vm.save()
535 62b894c0 Giorgos Verigakis
    d = {meta.meta_key: meta.meta_value}
536 62b894c0 Giorgos Verigakis
    return util.render_meta(request, d, status=201)
537 d8e50a39 Giorgos Verigakis
538 55cd40be Vangelis Koukis
539 b36f78fa Giorgos Verigakis
@util.api_method('DELETE')
540 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
541 d8e50a39 Giorgos Verigakis
def delete_metadata_item(request, server_id, key):
542 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 204
543 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
544 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
545 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
546 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
547 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
548 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
549 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
550 d8e50a39 Giorgos Verigakis
    #                       overLimit (413),
551 ce55f211 Kostas Papadimitriou
552 9e98ba3c Giorgos Verigakis
    log.debug('delete_server_metadata_item %s %s', server_id, key)
553 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
554 b36f78fa Giorgos Verigakis
    meta = util.get_vm_meta(vm, key)
555 d8e50a39 Giorgos Verigakis
    meta.delete()
556 5509b599 Giorgos Verigakis
    vm.save()
557 d8e50a39 Giorgos Verigakis
    return HttpResponse(status=204)
558 c738c935 Giorgos Verigakis
559 55cd40be Vangelis Koukis
560 c738c935 Giorgos Verigakis
@util.api_method('GET')
561 c738c935 Giorgos Verigakis
def server_stats(request, server_id):
562 c738c935 Giorgos Verigakis
    # Normal Response Codes: 200
563 c738c935 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
564 c738c935 Giorgos Verigakis
    #                       serviceUnavailable (503),
565 c738c935 Giorgos Verigakis
    #                       unauthorized (401),
566 c738c935 Giorgos Verigakis
    #                       badRequest (400),
567 c738c935 Giorgos Verigakis
    #                       itemNotFound (404),
568 c738c935 Giorgos Verigakis
    #                       overLimit (413)
569 ce55f211 Kostas Papadimitriou
570 9e98ba3c Giorgos Verigakis
    log.debug('server_stats %s', server_id)
571 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
572 924d8085 Christos Stavrakakis
    #secret = util.encrypt(vm.backend_vm_id)
573 924d8085 Christos Stavrakakis
    secret = vm.backend_vm_id      # XXX disable backend id encryption
574 ce55f211 Kostas Papadimitriou
575 c738c935 Giorgos Verigakis
    stats = {
576 c738c935 Giorgos Verigakis
        'serverRef': vm.id,
577 c738c935 Giorgos Verigakis
        'refresh': settings.STATS_REFRESH_PERIOD,
578 5391d6b5 Giorgos Verigakis
        'cpuBar': settings.CPU_BAR_GRAPH_URL % secret,
579 5391d6b5 Giorgos Verigakis
        'cpuTimeSeries': settings.CPU_TIMESERIES_GRAPH_URL % secret,
580 5391d6b5 Giorgos Verigakis
        'netBar': settings.NET_BAR_GRAPH_URL % secret,
581 5391d6b5 Giorgos Verigakis
        'netTimeSeries': settings.NET_TIMESERIES_GRAPH_URL % secret}
582 ce55f211 Kostas Papadimitriou
583 c738c935 Giorgos Verigakis
    if request.serialization == 'xml':
584 c738c935 Giorgos Verigakis
        data = render_to_string('server_stats.xml', stats)
585 c738c935 Giorgos Verigakis
    else:
586 c738c935 Giorgos Verigakis
        data = json.dumps({'stats': stats})
587 c738c935 Giorgos Verigakis
588 c738c935 Giorgos Verigakis
    return HttpResponse(data, status=200)