Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (32 kB)

1 b3fd98ae Christos Stavrakakis
# Copyright 2011-2013 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 ccd0d474 Giorgos Verigakis
from django.conf import settings
35 6b256427 Christos Stavrakakis
from django.conf.urls import patterns
36 a6b17d33 Christos Stavrakakis
37 4dba0480 Christos Stavrakakis
from django.db import transaction
38 7e2f9d4b Giorgos Verigakis
from django.http import HttpResponse
39 7e2f9d4b Giorgos Verigakis
from django.template.loader import render_to_string
40 29a59bc1 Giorgos Verigakis
from django.utils import simplejson as json
41 7e2f9d4b Giorgos Verigakis
42 b3fd98ae Christos Stavrakakis
from snf_django.lib import api
43 b3fd98ae Christos Stavrakakis
from snf_django.lib.api import faults, utils
44 816d7588 Christos Stavrakakis
45 bd40abfa Christos Stavrakakis
from synnefo.api import util
46 41a7fae7 Christos Stavrakakis
from synnefo.db.models import (VirtualMachine, VirtualMachineMetadata)
47 41a7fae7 Christos Stavrakakis
from synnefo.logic import servers, utils as logic_utils
48 9e98ba3c Giorgos Verigakis
49 bf5c82dc Christos Stavrakakis
from logging import getLogger
50 b3fd98ae Christos Stavrakakis
log = getLogger(__name__)
51 7e2f9d4b Giorgos Verigakis
52 e440e835 Christos Stavrakakis
urlpatterns = patterns(
53 e440e835 Christos Stavrakakis
    'synnefo.api.servers',
54 7e2f9d4b Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
55 7e2f9d4b Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_servers', {'detail': True}),
56 7e2f9d4b Giorgos Verigakis
    (r'^/(\d+)(?:.json|.xml)?$', 'server_demux'),
57 41a7fae7 Christos Stavrakakis
    (r'^/(\d+)/action(?:.json|.xml)?$', 'demux_server_action'),
58 b016b476 Giorgos Verigakis
    (r'^/(\d+)/ips(?:.json|.xml)?$', 'list_addresses'),
59 b016b476 Giorgos Verigakis
    (r'^/(\d+)/ips/(.+?)(?:.json|.xml)?$', 'list_addresses_by_network'),
60 8a992938 Christos Stavrakakis
    (r'^/(\d+)/metadata(?:.json|.xml)?$', 'metadata_demux'),
61 8a992938 Christos Stavrakakis
    (r'^/(\d+)/metadata/(.+?)(?:.json|.xml)?$', 'metadata_item_demux'),
62 c738c935 Giorgos Verigakis
    (r'^/(\d+)/stats(?:.json|.xml)?$', 'server_stats'),
63 85a634e6 Kostas Papadimitriou
    (r'^/(\d+)/diagnostics(?:.json)?$', 'get_server_diagnostics'),
64 7e2f9d4b Giorgos Verigakis
)
65 7e2f9d4b Giorgos Verigakis
66 7e2f9d4b Giorgos Verigakis
67 7e2f9d4b Giorgos Verigakis
def demux(request):
68 7e2f9d4b Giorgos Verigakis
    if request.method == 'GET':
69 7e2f9d4b Giorgos Verigakis
        return list_servers(request)
70 7e2f9d4b Giorgos Verigakis
    elif request.method == 'POST':
71 7e2f9d4b Giorgos Verigakis
        return create_server(request)
72 7e2f9d4b Giorgos Verigakis
    else:
73 381a548c Sofia Papagiannaki
        return api.api_method_not_allowed(request,
74 381a548c Sofia Papagiannaki
                                          allowed_methods=['GET', 'POST'])
75 7e2f9d4b Giorgos Verigakis
76 55cd40be Vangelis Koukis
77 7e2f9d4b Giorgos Verigakis
def server_demux(request, server_id):
78 7e2f9d4b Giorgos Verigakis
    if request.method == 'GET':
79 7e2f9d4b Giorgos Verigakis
        return get_server_details(request, server_id)
80 7e2f9d4b Giorgos Verigakis
    elif request.method == 'PUT':
81 7e2f9d4b Giorgos Verigakis
        return update_server_name(request, server_id)
82 7e2f9d4b Giorgos Verigakis
    elif request.method == 'DELETE':
83 7e2f9d4b Giorgos Verigakis
        return delete_server(request, server_id)
84 7e2f9d4b Giorgos Verigakis
    else:
85 381a548c Sofia Papagiannaki
        return api.api_method_not_allowed(request,
86 381a548c Sofia Papagiannaki
                                          allowed_methods=['GET',
87 381a548c Sofia Papagiannaki
                                                           'PUT',
88 381a548c Sofia Papagiannaki
                                                           'DELETE'])
89 d8e50a39 Giorgos Verigakis
90 55cd40be Vangelis Koukis
91 d8e50a39 Giorgos Verigakis
def metadata_demux(request, server_id):
92 d8e50a39 Giorgos Verigakis
    if request.method == 'GET':
93 d8e50a39 Giorgos Verigakis
        return list_metadata(request, server_id)
94 d8e50a39 Giorgos Verigakis
    elif request.method == 'POST':
95 d8e50a39 Giorgos Verigakis
        return update_metadata(request, server_id)
96 d8e50a39 Giorgos Verigakis
    else:
97 381a548c Sofia Papagiannaki
        return api.api_method_not_allowed(request,
98 381a548c Sofia Papagiannaki
                                          allowed_methods=['GET', 'POST'])
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 381a548c Sofia Papagiannaki
        return api.api_method_not_allowed(request,
110 381a548c Sofia Papagiannaki
                                          allowed_methods=['GET',
111 381a548c Sofia Papagiannaki
                                                           'PUT',
112 381a548c Sofia Papagiannaki
                                                           'DELETE'])
113 7e2f9d4b Giorgos Verigakis
114 08b079e2 Stavros Sachtouris
115 7fede91e Christos Stavrakakis
def nic_to_dict(nic):
116 78c165cf Christos Stavrakakis
    ip_type = "floating" if nic.is_floating_ip else "fixed"
117 7fede91e Christos Stavrakakis
    d = {'id': util.construct_nic_id(nic),
118 7fede91e Christos Stavrakakis
         'network_id': str(nic.network.id),
119 7fede91e Christos Stavrakakis
         'mac_address': nic.mac,
120 8d325d4b Christos Stavrakakis
         'ipv4': nic.ipv4,
121 8d325d4b Christos Stavrakakis
         'ipv6': nic.ipv6,
122 78c165cf Christos Stavrakakis
         'OS-EXT-IPS:type': ip_type}
123 47c66641 Dimitris Aragiorgis
124 207b70d5 Giorgos Verigakis
    if nic.firewall_profile:
125 207b70d5 Giorgos Verigakis
        d['firewallProfile'] = nic.firewall_profile
126 d44c236b Giorgos Verigakis
    return d
127 b016b476 Giorgos Verigakis
128 55cd40be Vangelis Koukis
129 78c165cf Christos Stavrakakis
def attachments_to_addresses(attachments):
130 1f638a85 Christos Stavrakakis
    addresses = {}
131 78c165cf Christos Stavrakakis
    for nic in attachments:
132 1f638a85 Christos Stavrakakis
        net_nics = []
133 f8352d29 Christos Stavrakakis
        net_nics.append({"version": 4,
134 78c165cf Christos Stavrakakis
                         "addr": nic["ipv4"],
135 78c165cf Christos Stavrakakis
                         "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
136 78c165cf Christos Stavrakakis
        if nic["ipv6"]:
137 f8352d29 Christos Stavrakakis
            net_nics.append({"version": 6,
138 78c165cf Christos Stavrakakis
                             "addr": nic["ipv6"],
139 78c165cf Christos Stavrakakis
                             "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
140 78c165cf Christos Stavrakakis
        addresses[nic["network_id"]] = net_nics
141 1f638a85 Christos Stavrakakis
    return addresses
142 1f638a85 Christos Stavrakakis
143 1f638a85 Christos Stavrakakis
144 d8e50a39 Giorgos Verigakis
def vm_to_dict(vm, detail=False):
145 d8e50a39 Giorgos Verigakis
    d = dict(id=vm.id, name=vm.name)
146 1b696c26 Christos Stavrakakis
    d['links'] = util.vm_to_links(vm.id)
147 7e2f9d4b Giorgos Verigakis
    if detail:
148 936e0be3 Christos Stavrakakis
        d['user_id'] = vm.userid
149 936e0be3 Christos Stavrakakis
        d['tenant_id'] = vm.userid
150 41a7fae7 Christos Stavrakakis
        d['status'] = logic_utils.get_rsapi_state(vm)
151 41a7fae7 Christos Stavrakakis
        d['SNF:task_state'] = logic_utils.get_task_state(vm)
152 41a7fae7 Christos Stavrakakis
        d['progress'] = 100 if d['status'] == 'ACTIVE' else vm.buildpercentage
153 d8e50a39 Giorgos Verigakis
        d['hostId'] = vm.hostid
154 b3fd98ae Christos Stavrakakis
        d['updated'] = utils.isoformat(vm.updated)
155 b3fd98ae Christos Stavrakakis
        d['created'] = utils.isoformat(vm.created)
156 1b696c26 Christos Stavrakakis
        d['flavor'] = {"id": vm.flavor.id,
157 1b696c26 Christos Stavrakakis
                       "links": util.flavor_to_links(vm.flavor.id)}
158 1b696c26 Christos Stavrakakis
        d['image'] = {"id": vm.imageid,
159 1b696c26 Christos Stavrakakis
                      "links": util.image_to_links(vm.imageid)}
160 e221ade2 Christos Stavrakakis
        d['suspended'] = vm.suspended
161 ce55f211 Kostas Papadimitriou
162 7cc3c7d9 Giorgos Verigakis
        metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
163 1b696c26 Christos Stavrakakis
        d['metadata'] = metadata
164 aa197ee4 Vangelis Koukis
165 939d71dd Christos Stavrakakis
        vm_nics = vm.nics.filter(state="ACTIVE").order_by("index")
166 939d71dd Christos Stavrakakis
        attachments = map(nic_to_dict, vm_nics)
167 5029ff36 Christos Stavrakakis
        d['attachments'] = attachments
168 78c165cf Christos Stavrakakis
        d['addresses'] = attachments_to_addresses(attachments)
169 85a634e6 Kostas Papadimitriou
170 85a634e6 Kostas Papadimitriou
        # include the latest vm diagnostic, if set
171 85a634e6 Kostas Papadimitriou
        diagnostic = vm.get_last_diagnostic()
172 85a634e6 Kostas Papadimitriou
        if diagnostic:
173 85a634e6 Kostas Papadimitriou
            d['diagnostics'] = diagnostics_to_dict([diagnostic])
174 1b696c26 Christos Stavrakakis
        else:
175 1b696c26 Christos Stavrakakis
            d['diagnostics'] = []
176 f8352d29 Christos Stavrakakis
        # Fixed
177 f8352d29 Christos Stavrakakis
        d["security_groups"] = [{"name": "default"}]
178 f8352d29 Christos Stavrakakis
        d["key_name"] = None
179 f8352d29 Christos Stavrakakis
        d["config_drive"] = ""
180 f8352d29 Christos Stavrakakis
        d["accessIPv4"] = ""
181 f8352d29 Christos Stavrakakis
        d["accessIPv6"] = ""
182 2522e489 Christos Stavrakakis
        fqdn = get_server_fqdn(vm)
183 2522e489 Christos Stavrakakis
        d["SNF:fqdn"] = fqdn
184 2522e489 Christos Stavrakakis
        d["SNF:port_forwarding"] = get_server_port_forwarding(vm, fqdn)
185 85a634e6 Kostas Papadimitriou
186 7e2f9d4b Giorgos Verigakis
    return d
187 7e2f9d4b Giorgos Verigakis
188 d8e50a39 Giorgos Verigakis
189 d328a525 Christos Stavrakakis
def get_server_fqdn(vm):
190 d328a525 Christos Stavrakakis
    fqdn_setting = settings.CYCLADES_SERVERS_FQDN
191 d328a525 Christos Stavrakakis
    if fqdn_setting is None:
192 d328a525 Christos Stavrakakis
        public_nics = vm.nics.filter(network__public=True, state="ACTIVE")
193 d328a525 Christos Stavrakakis
        # Return the first public IPv4 address if exists
194 d328a525 Christos Stavrakakis
        ipv4_nics = public_nics.exclude(ipv4=None)
195 d328a525 Christos Stavrakakis
        if ipv4_nics:
196 d328a525 Christos Stavrakakis
            return ipv4_nics[0].ipv4
197 d328a525 Christos Stavrakakis
        # Else return the first public IPv6 address if exists
198 d328a525 Christos Stavrakakis
        ipv6_nics = public_nics.exclude(ipv6=None)
199 d328a525 Christos Stavrakakis
        if ipv6_nics:
200 d328a525 Christos Stavrakakis
            return ipv6_nics[0].ipv6
201 d328a525 Christos Stavrakakis
        return ""
202 d328a525 Christos Stavrakakis
    elif isinstance(fqdn_setting, basestring):
203 d328a525 Christos Stavrakakis
        return fqdn_setting % {"id": vm.id}
204 d328a525 Christos Stavrakakis
    else:
205 d328a525 Christos Stavrakakis
        msg = ("Invalid setting: CYCLADES_SERVERS_FQDN."
206 d328a525 Christos Stavrakakis
               " Value must be a string.")
207 d328a525 Christos Stavrakakis
        raise faults.InternalServerError(msg)
208 d328a525 Christos Stavrakakis
209 d328a525 Christos Stavrakakis
210 2522e489 Christos Stavrakakis
def get_server_port_forwarding(vm, fqdn):
211 2522e489 Christos Stavrakakis
    """Create API 'port_forwarding' attribute from corresponding setting.
212 2522e489 Christos Stavrakakis

213 2522e489 Christos Stavrakakis
    Create the 'port_forwarding' API vm attribute based on the corresponding
214 2522e489 Christos Stavrakakis
    setting (CYCLADES_PORT_FORWARDING), which can be either a tuple
215 2522e489 Christos Stavrakakis
    of the form (host, port) or a callable object returning such tuple. In
216 2522e489 Christos Stavrakakis
    case of callable object, must be called with the following arguments:
217 2522e489 Christos Stavrakakis
    * ip_address
218 2522e489 Christos Stavrakakis
    * server_id
219 2522e489 Christos Stavrakakis
    * fqdn
220 2522e489 Christos Stavrakakis
    * owner UUID
221 2522e489 Christos Stavrakakis

222 2522e489 Christos Stavrakakis
    """
223 2522e489 Christos Stavrakakis
    port_forwarding = {}
224 2522e489 Christos Stavrakakis
    for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
225 2522e489 Christos Stavrakakis
        if hasattr(to_dest, "__call__"):
226 2522e489 Christos Stavrakakis
            public_nics = vm.nics.filter(network__public=True, state="ACTIVE")\
227 2522e489 Christos Stavrakakis
                                 .exclude(ipv4=None).order_by('index')
228 2522e489 Christos Stavrakakis
            if public_nics:
229 2522e489 Christos Stavrakakis
                vm_ipv4 = public_nics[0].ipv4
230 2522e489 Christos Stavrakakis
            else:
231 2522e489 Christos Stavrakakis
                vm_ipv4 = None
232 2522e489 Christos Stavrakakis
            to_dest = to_dest(vm_ipv4, vm.id, fqdn, vm.userid)
233 2522e489 Christos Stavrakakis
        msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
234 2522e489 Christos Stavrakakis
               " Value must be a tuple of two elements (host, port).")
235 2522e489 Christos Stavrakakis
        if to_dest is None:
236 2522e489 Christos Stavrakakis
            continue
237 2522e489 Christos Stavrakakis
        if not isinstance(to_dest, tuple) or len(to_dest) != 2:
238 2522e489 Christos Stavrakakis
                raise faults.InternalServerError(msg)
239 2522e489 Christos Stavrakakis
        else:
240 2522e489 Christos Stavrakakis
            try:
241 2522e489 Christos Stavrakakis
                host, port = to_dest
242 2522e489 Christos Stavrakakis
            except (TypeError, ValueError):
243 2522e489 Christos Stavrakakis
                raise faults.InternalServerError(msg)
244 2522e489 Christos Stavrakakis
245 2522e489 Christos Stavrakakis
        port_forwarding[dport] = {"host": host, "port": str(port)}
246 2522e489 Christos Stavrakakis
    return port_forwarding
247 2522e489 Christos Stavrakakis
248 2522e489 Christos Stavrakakis
249 85a634e6 Kostas Papadimitriou
def diagnostics_to_dict(diagnostics):
250 85a634e6 Kostas Papadimitriou
    """
251 85a634e6 Kostas Papadimitriou
    Extract api data from diagnostics QuerySet.
252 85a634e6 Kostas Papadimitriou
    """
253 85a634e6 Kostas Papadimitriou
    entries = list()
254 85a634e6 Kostas Papadimitriou
255 85a634e6 Kostas Papadimitriou
    for diagnostic in diagnostics:
256 85a634e6 Kostas Papadimitriou
        # format source date if set
257 85a634e6 Kostas Papadimitriou
        formatted_source_date = None
258 85a634e6 Kostas Papadimitriou
        if diagnostic.source_date:
259 b3fd98ae Christos Stavrakakis
            formatted_source_date = utils.isoformat(diagnostic.source_date)
260 85a634e6 Kostas Papadimitriou
261 85a634e6 Kostas Papadimitriou
        entry = {
262 85a634e6 Kostas Papadimitriou
            'source': diagnostic.source,
263 b3fd98ae Christos Stavrakakis
            'created': utils.isoformat(diagnostic.created),
264 85a634e6 Kostas Papadimitriou
            'message': diagnostic.message,
265 85a634e6 Kostas Papadimitriou
            'details': diagnostic.details,
266 85a634e6 Kostas Papadimitriou
            'level': diagnostic.level,
267 85a634e6 Kostas Papadimitriou
        }
268 85a634e6 Kostas Papadimitriou
269 85a634e6 Kostas Papadimitriou
        if formatted_source_date:
270 85a634e6 Kostas Papadimitriou
            entry['source_date'] = formatted_source_date
271 85a634e6 Kostas Papadimitriou
272 85a634e6 Kostas Papadimitriou
        entries.append(entry)
273 85a634e6 Kostas Papadimitriou
274 85a634e6 Kostas Papadimitriou
    return entries
275 85a634e6 Kostas Papadimitriou
276 85a634e6 Kostas Papadimitriou
277 d8e50a39 Giorgos Verigakis
def render_server(request, server, status=200):
278 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
279 b36f78fa Giorgos Verigakis
        data = render_to_string('server.xml', {
280 b36f78fa Giorgos Verigakis
            'server': server,
281 b36f78fa Giorgos Verigakis
            'is_root': True})
282 7e2f9d4b Giorgos Verigakis
    else:
283 d8e50a39 Giorgos Verigakis
        data = json.dumps({'server': server})
284 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=status)
285 aa197ee4 Vangelis Koukis
286 7e2f9d4b Giorgos Verigakis
287 85a634e6 Kostas Papadimitriou
def render_diagnostics(request, diagnostics_dict, status=200):
288 85a634e6 Kostas Papadimitriou
    """
289 85a634e6 Kostas Papadimitriou
    Render diagnostics dictionary to json response.
290 85a634e6 Kostas Papadimitriou
    """
291 85a634e6 Kostas Papadimitriou
    return HttpResponse(json.dumps(diagnostics_dict), status=status)
292 85a634e6 Kostas Papadimitriou
293 85a634e6 Kostas Papadimitriou
294 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
295 85a634e6 Kostas Papadimitriou
def get_server_diagnostics(request, server_id):
296 85a634e6 Kostas Papadimitriou
    """
297 85a634e6 Kostas Papadimitriou
    Virtual machine diagnostics api view.
298 85a634e6 Kostas Papadimitriou
    """
299 85a634e6 Kostas Papadimitriou
    log.debug('server_diagnostics %s', server_id)
300 85a634e6 Kostas Papadimitriou
    vm = util.get_vm(server_id, request.user_uniq)
301 85a634e6 Kostas Papadimitriou
    diagnostics = diagnostics_to_dict(vm.diagnostics.all())
302 85a634e6 Kostas Papadimitriou
    return render_diagnostics(request, diagnostics)
303 85a634e6 Kostas Papadimitriou
304 85a634e6 Kostas Papadimitriou
305 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
306 7e2f9d4b Giorgos Verigakis
def list_servers(request, detail=False):
307 7e2f9d4b Giorgos Verigakis
    # Normal Response Codes: 200, 203
308 7e2f9d4b Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
309 7e2f9d4b Giorgos Verigakis
    #                       serviceUnavailable (503),
310 7e2f9d4b Giorgos Verigakis
    #                       unauthorized (401),
311 7e2f9d4b Giorgos Verigakis
    #                       badRequest (400),
312 7e2f9d4b Giorgos Verigakis
    #                       overLimit (413)
313 ce55f211 Kostas Papadimitriou
314 0c37a721 Christos Stavrakakis
    log.debug('list_servers detail=%s', detail)
315 e524ed5f Kostas Papadimitriou
    user_vms = VirtualMachine.objects.filter(userid=request.user_uniq)
316 ce55f211 Kostas Papadimitriou
317 b6bc4afa Christos Stavrakakis
    user_vms = utils.filter_modified_since(request, objects=user_vms)
318 ce55f211 Kostas Papadimitriou
319 41a7fae7 Christos Stavrakakis
    servers_dict = [vm_to_dict(server, detail)
320 41a7fae7 Christos Stavrakakis
                    for server in user_vms.order_by('id')]
321 aa197ee4 Vangelis Koukis
322 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
323 b36f78fa Giorgos Verigakis
        data = render_to_string('list_servers.xml', {
324 41a7fae7 Christos Stavrakakis
            'servers': servers_dict,
325 b36f78fa Giorgos Verigakis
            'detail': detail})
326 7e2f9d4b Giorgos Verigakis
    else:
327 41a7fae7 Christos Stavrakakis
        data = json.dumps({'servers': servers_dict})
328 aa197ee4 Vangelis Koukis
329 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=200)
330 7e2f9d4b Giorgos Verigakis
331 55cd40be Vangelis Koukis
332 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
333 2509ce17 Christos Stavrakakis
def create_server(request):
334 7e2f9d4b Giorgos Verigakis
    # Normal Response Code: 202
335 7e2f9d4b Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
336 7e2f9d4b Giorgos Verigakis
    #                       serviceUnavailable (503),
337 7e2f9d4b Giorgos Verigakis
    #                       unauthorized (401),
338 7e2f9d4b Giorgos Verigakis
    #                       badMediaType(415),
339 7e2f9d4b Giorgos Verigakis
    #                       itemNotFound (404),
340 7e2f9d4b Giorgos Verigakis
    #                       badRequest (400),
341 7e2f9d4b Giorgos Verigakis
    #                       serverCapacityUnavailable (503),
342 7e2f9d4b Giorgos Verigakis
    #                       overLimit (413)
343 bcd80cd9 Christos Stavrakakis
    req = utils.get_request_dict(request)
344 bcd80cd9 Christos Stavrakakis
    log.info('create_server %s', req)
345 bcd80cd9 Christos Stavrakakis
    user_id = request.user_uniq
346 bcd80cd9 Christos Stavrakakis
347 7e2f9d4b Giorgos Verigakis
    try:
348 bcd80cd9 Christos Stavrakakis
        server = req['server']
349 bcd80cd9 Christos Stavrakakis
        name = server['name']
350 bcd80cd9 Christos Stavrakakis
        metadata = server.get('metadata', {})
351 bcd80cd9 Christos Stavrakakis
        assert isinstance(metadata, dict)
352 bcd80cd9 Christos Stavrakakis
        image_id = server['imageRef']
353 bcd80cd9 Christos Stavrakakis
        flavor_id = server['flavorRef']
354 bcd80cd9 Christos Stavrakakis
        personality = server.get('personality', [])
355 bcd80cd9 Christos Stavrakakis
        assert isinstance(personality, list)
356 cb66110b Christos Stavrakakis
        private_networks = server.get("networks", [])
357 cb66110b Christos Stavrakakis
        assert isinstance(private_networks, list)
358 816d7588 Christos Stavrakakis
        floating_ips = server.get("floating_ips", [])
359 816d7588 Christos Stavrakakis
        assert isinstance(floating_ips, list)
360 bcd80cd9 Christos Stavrakakis
    except (KeyError, AssertionError):
361 bcd80cd9 Christos Stavrakakis
        raise faults.BadRequest("Malformed request")
362 bcd80cd9 Christos Stavrakakis
363 bcd80cd9 Christos Stavrakakis
    # Verify that personalities are well-formed
364 bcd80cd9 Christos Stavrakakis
    util.verify_personality(personality)
365 bcd80cd9 Christos Stavrakakis
    # Get image information
366 bcd80cd9 Christos Stavrakakis
    image = util.get_image_dict(image_id, user_id)
367 bcd80cd9 Christos Stavrakakis
    # Get flavor (ensure it is active)
368 bcd80cd9 Christos Stavrakakis
    flavor = util.get_flavor(flavor_id, include_deleted=False)
369 bcd80cd9 Christos Stavrakakis
    # Generate password
370 bcd80cd9 Christos Stavrakakis
    password = util.random_password()
371 bcd80cd9 Christos Stavrakakis
372 41a7fae7 Christos Stavrakakis
    vm = servers.create(user_id, name, password, flavor, image,
373 cb66110b Christos Stavrakakis
                        metadata=metadata, personality=personality,
374 816d7588 Christos Stavrakakis
                        private_networks=private_networks,
375 816d7588 Christos Stavrakakis
                        floating_ips=floating_ips)
376 bcd80cd9 Christos Stavrakakis
377 bcd80cd9 Christos Stavrakakis
    server = vm_to_dict(vm, detail=True)
378 bcd80cd9 Christos Stavrakakis
    server['status'] = 'BUILD'
379 bcd80cd9 Christos Stavrakakis
    server['adminPass'] = password
380 bcd80cd9 Christos Stavrakakis
381 bcd80cd9 Christos Stavrakakis
    response = render_server(request, server, status=202)
382 4b8e03e5 Christos Stavrakakis
383 bcd80cd9 Christos Stavrakakis
    return response
384 bcd80cd9 Christos Stavrakakis
385 bcd80cd9 Christos Stavrakakis
386 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
387 7e2f9d4b Giorgos Verigakis
def get_server_details(request, server_id):
388 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
389 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
390 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
391 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
392 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
393 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
394 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
395 ce55f211 Kostas Papadimitriou
396 0c37a721 Christos Stavrakakis
    log.debug('get_server_details %s', server_id)
397 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
398 d8e50a39 Giorgos Verigakis
    server = vm_to_dict(vm, detail=True)
399 d8e50a39 Giorgos Verigakis
    return render_server(request, server)
400 7e2f9d4b Giorgos Verigakis
401 55cd40be Vangelis Koukis
402 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='PUT', user_required=True, logger=log)
403 874383a4 Christos Stavrakakis
@transaction.commit_on_success
404 c36934a7 Giorgos Verigakis
def update_server_name(request, server_id):
405 c36934a7 Giorgos Verigakis
    # Normal Response Code: 204
406 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
407 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
408 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
409 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
410 c36934a7 Giorgos Verigakis
    #                       badMediaType(415),
411 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
412 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
413 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
414 aa197ee4 Vangelis Koukis
415 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
416 bf5c82dc Christos Stavrakakis
    log.info('update_server_name %s %s', server_id, req)
417 ce55f211 Kostas Papadimitriou
418 7e2f9d4b Giorgos Verigakis
    try:
419 7e2f9d4b Giorgos Verigakis
        name = req['server']['name']
420 529178b1 Giorgos Verigakis
    except (TypeError, KeyError):
421 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
422 aa197ee4 Vangelis Koukis
423 7f2dbcad Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=True,
424 7f2dbcad Christos Stavrakakis
                     non_suspended=True)
425 a52cc1b4 Christos Stavrakakis
426 a52cc1b4 Christos Stavrakakis
    servers.rename(vm, new_name=name)
427 aa197ee4 Vangelis Koukis
428 7e2f9d4b Giorgos Verigakis
    return HttpResponse(status=204)
429 7e2f9d4b Giorgos Verigakis
430 55cd40be Vangelis Koukis
431 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log)
432 7e2f9d4b Giorgos Verigakis
def delete_server(request, server_id):
433 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 204
434 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
435 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
436 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
437 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
438 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
439 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
440 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
441 ce55f211 Kostas Papadimitriou
442 bf5c82dc Christos Stavrakakis
    log.info('delete_server %s', server_id)
443 7f2dbcad Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=True,
444 7f2dbcad Christos Stavrakakis
                     non_suspended=True)
445 41a7fae7 Christos Stavrakakis
    vm = servers.destroy(vm)
446 7e2f9d4b Giorgos Verigakis
    return HttpResponse(status=204)
447 b016b476 Giorgos Verigakis
448 55cd40be Vangelis Koukis
449 04a95cf3 Kostas Papadimitriou
# additional server actions
450 04a95cf3 Kostas Papadimitriou
ARBITRARY_ACTIONS = ['console', 'firewallProfile']
451 04a95cf3 Kostas Papadimitriou
452 e440e835 Christos Stavrakakis
453 7f2dbcad Christos Stavrakakis
def key_to_action(key):
454 7f2dbcad Christos Stavrakakis
    """Map HTTP request key to a VM Action"""
455 7f2dbcad Christos Stavrakakis
    if key == "shutdown":
456 7f2dbcad Christos Stavrakakis
        return "STOP"
457 7f2dbcad Christos Stavrakakis
    if key == "delete":
458 7f2dbcad Christos Stavrakakis
        return "DESTROY"
459 b7f21824 Kostas Papadimitriou
    if key in ARBITRARY_ACTIONS:
460 7f2dbcad Christos Stavrakakis
        return None
461 7f2dbcad Christos Stavrakakis
    else:
462 7f2dbcad Christos Stavrakakis
        return key.upper()
463 7f2dbcad Christos Stavrakakis
464 7f2dbcad Christos Stavrakakis
465 41a7fae7 Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
466 41a7fae7 Christos Stavrakakis
@transaction.commit_on_success
467 41a7fae7 Christos Stavrakakis
def demux_server_action(request, server_id):
468 41a7fae7 Christos Stavrakakis
    req = utils.get_request_dict(request)
469 41a7fae7 Christos Stavrakakis
    log.debug('server_action %s %s', server_id, req)
470 7f2dbcad Christos Stavrakakis
471 41a7fae7 Christos Stavrakakis
    if len(req) != 1:
472 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Malformed request")
473 41a7fae7 Christos Stavrakakis
474 41a7fae7 Christos Stavrakakis
    # Do not allow any action on deleted or suspended VMs
475 41a7fae7 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=True,
476 41a7fae7 Christos Stavrakakis
                     non_deleted=True, non_suspended=True)
477 7f2dbcad Christos Stavrakakis
478 ece5581b Christos Stavrakakis
    action = req.keys()[0]
479 7f2dbcad Christos Stavrakakis
480 41a7fae7 Christos Stavrakakis
    if key_to_action(action) not in [x[0] for x in VirtualMachine.ACTIONS]:
481 41a7fae7 Christos Stavrakakis
        if action not in ARBITRARY_ACTIONS:
482 41a7fae7 Christos Stavrakakis
            raise faults.BadRequest("Action %s not supported" % action)
483 41a7fae7 Christos Stavrakakis
    action_args = req[action]
484 7f2dbcad Christos Stavrakakis
485 41a7fae7 Christos Stavrakakis
    if not isinstance(action_args, dict):
486 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Invalid argument")
487 7f2dbcad Christos Stavrakakis
488 41a7fae7 Christos Stavrakakis
    return server_actions[action](request, vm, action_args)
489 7f2dbcad Christos Stavrakakis
490 7f2dbcad Christos Stavrakakis
491 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
492 b016b476 Giorgos Verigakis
def list_addresses(request, server_id):
493 b016b476 Giorgos Verigakis
    # Normal Response Codes: 200, 203
494 b016b476 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
495 b016b476 Giorgos Verigakis
    #                       serviceUnavailable (503),
496 b016b476 Giorgos Verigakis
    #                       unauthorized (401),
497 b016b476 Giorgos Verigakis
    #                       badRequest (400),
498 b016b476 Giorgos Verigakis
    #                       overLimit (413)
499 ce55f211 Kostas Papadimitriou
500 0c37a721 Christos Stavrakakis
    log.debug('list_addresses %s', server_id)
501 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
502 051e47f8 Christos Stavrakakis
    attachments = [nic_to_dict(nic) for nic in vm.nics.filter(state="ACTIVE")]
503 78c165cf Christos Stavrakakis
    addresses = attachments_to_addresses(attachments)
504 ce55f211 Kostas Papadimitriou
505 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
506 b016b476 Giorgos Verigakis
        data = render_to_string('list_addresses.xml', {'addresses': addresses})
507 b016b476 Giorgos Verigakis
    else:
508 1f638a85 Christos Stavrakakis
        data = json.dumps({'addresses': addresses, 'attachments': attachments})
509 aa197ee4 Vangelis Koukis
510 b016b476 Giorgos Verigakis
    return HttpResponse(data, status=200)
511 b016b476 Giorgos Verigakis
512 55cd40be Vangelis Koukis
513 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
514 b016b476 Giorgos Verigakis
def list_addresses_by_network(request, server_id, network_id):
515 b016b476 Giorgos Verigakis
    # Normal Response Codes: 200, 203
516 b016b476 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
517 b016b476 Giorgos Verigakis
    #                       serviceUnavailable (503),
518 b016b476 Giorgos Verigakis
    #                       unauthorized (401),
519 b016b476 Giorgos Verigakis
    #                       badRequest (400),
520 b016b476 Giorgos Verigakis
    #                       itemNotFound (404),
521 b016b476 Giorgos Verigakis
    #                       overLimit (413)
522 ce55f211 Kostas Papadimitriou
523 0c37a721 Christos Stavrakakis
    log.debug('list_addresses_by_network %s %s', server_id, network_id)
524 4b3b8688 Giorgos Verigakis
    machine = util.get_vm(server_id, request.user_uniq)
525 4b3b8688 Giorgos Verigakis
    network = util.get_network(network_id, request.user_uniq)
526 051e47f8 Christos Stavrakakis
    nics = machine.nics.filter(network=network, state="ACTIVE").all()
527 78c165cf Christos Stavrakakis
    addresses = attachments_to_addresses(map(nic_to_dict, nics))
528 ce55f211 Kostas Papadimitriou
529 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
530 1f638a85 Christos Stavrakakis
        data = render_to_string('address.xml', {'addresses': addresses})
531 b016b476 Giorgos Verigakis
    else:
532 1f638a85 Christos Stavrakakis
        data = json.dumps({'network': addresses})
533 aa197ee4 Vangelis Koukis
534 b016b476 Giorgos Verigakis
    return HttpResponse(data, status=200)
535 d8e50a39 Giorgos Verigakis
536 55cd40be Vangelis Koukis
537 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
538 d8e50a39 Giorgos Verigakis
def list_metadata(request, server_id):
539 d8e50a39 Giorgos Verigakis
    # Normal Response Codes: 200, 203
540 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
541 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
542 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
543 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
544 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
545 ce55f211 Kostas Papadimitriou
546 0c37a721 Christos Stavrakakis
    log.debug('list_server_metadata %s', server_id)
547 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
548 7cc3c7d9 Giorgos Verigakis
    metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
549 5029ff36 Christos Stavrakakis
    return util.render_metadata(request, metadata, use_values=False,
550 5029ff36 Christos Stavrakakis
                                status=200)
551 d8e50a39 Giorgos Verigakis
552 55cd40be Vangelis Koukis
553 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
554 874383a4 Christos Stavrakakis
@transaction.commit_on_success
555 d8e50a39 Giorgos Verigakis
def update_metadata(request, server_id):
556 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 201
557 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
558 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
559 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
560 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
561 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
562 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
563 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
564 ce55f211 Kostas Papadimitriou
565 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
566 bf5c82dc Christos Stavrakakis
    log.info('update_server_metadata %s %s', server_id, req)
567 e221ade2 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
568 d8e50a39 Giorgos Verigakis
    try:
569 d8e50a39 Giorgos Verigakis
        metadata = req['metadata']
570 d8e50a39 Giorgos Verigakis
        assert isinstance(metadata, dict)
571 d8e50a39 Giorgos Verigakis
    except (KeyError, AssertionError):
572 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
573 ce55f211 Kostas Papadimitriou
574 7cc3c7d9 Giorgos Verigakis
    for key, val in metadata.items():
575 7cc3c7d9 Giorgos Verigakis
        meta, created = vm.metadata.get_or_create(meta_key=key)
576 7cc3c7d9 Giorgos Verigakis
        meta.meta_value = val
577 7cc3c7d9 Giorgos Verigakis
        meta.save()
578 ce55f211 Kostas Papadimitriou
579 7cc3c7d9 Giorgos Verigakis
    vm.save()
580 7cc3c7d9 Giorgos Verigakis
    vm_meta = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
581 7cc3c7d9 Giorgos Verigakis
    return util.render_metadata(request, vm_meta, status=201)
582 d8e50a39 Giorgos Verigakis
583 55cd40be Vangelis Koukis
584 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
585 d8e50a39 Giorgos Verigakis
def get_metadata_item(request, server_id, key):
586 d8e50a39 Giorgos Verigakis
    # Normal Response Codes: 200, 203
587 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
588 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
589 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
590 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
591 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
592 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
593 ce55f211 Kostas Papadimitriou
594 0c37a721 Christos Stavrakakis
    log.debug('get_server_metadata_item %s %s', server_id, key)
595 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
596 b36f78fa Giorgos Verigakis
    meta = util.get_vm_meta(vm, key)
597 62b894c0 Giorgos Verigakis
    d = {meta.meta_key: meta.meta_value}
598 62b894c0 Giorgos Verigakis
    return util.render_meta(request, d, status=200)
599 d8e50a39 Giorgos Verigakis
600 55cd40be Vangelis Koukis
601 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='PUT', user_required=True, logger=log)
602 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
603 d8e50a39 Giorgos Verigakis
def create_metadata_item(request, server_id, key):
604 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 201
605 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
606 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
607 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
608 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
609 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
610 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
611 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
612 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
613 ce55f211 Kostas Papadimitriou
614 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
615 bf5c82dc Christos Stavrakakis
    log.info('create_server_metadata_item %s %s %s', server_id, key, req)
616 e221ade2 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
617 d8e50a39 Giorgos Verigakis
    try:
618 d8e50a39 Giorgos Verigakis
        metadict = req['meta']
619 d8e50a39 Giorgos Verigakis
        assert isinstance(metadict, dict)
620 d8e50a39 Giorgos Verigakis
        assert len(metadict) == 1
621 d8e50a39 Giorgos Verigakis
        assert key in metadict
622 d8e50a39 Giorgos Verigakis
    except (KeyError, AssertionError):
623 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
624 ce55f211 Kostas Papadimitriou
625 b36f78fa Giorgos Verigakis
    meta, created = VirtualMachineMetadata.objects.get_or_create(
626 b36f78fa Giorgos Verigakis
        meta_key=key,
627 b36f78fa Giorgos Verigakis
        vm=vm)
628 ce55f211 Kostas Papadimitriou
629 d8e50a39 Giorgos Verigakis
    meta.meta_value = metadict[key]
630 d8e50a39 Giorgos Verigakis
    meta.save()
631 5509b599 Giorgos Verigakis
    vm.save()
632 62b894c0 Giorgos Verigakis
    d = {meta.meta_key: meta.meta_value}
633 62b894c0 Giorgos Verigakis
    return util.render_meta(request, d, status=201)
634 d8e50a39 Giorgos Verigakis
635 55cd40be Vangelis Koukis
636 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log)
637 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
638 d8e50a39 Giorgos Verigakis
def delete_metadata_item(request, server_id, key):
639 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 204
640 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
641 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
642 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
643 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
644 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
645 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
646 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
647 d8e50a39 Giorgos Verigakis
    #                       overLimit (413),
648 ce55f211 Kostas Papadimitriou
649 bf5c82dc Christos Stavrakakis
    log.info('delete_server_metadata_item %s %s', server_id, key)
650 e221ade2 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
651 b36f78fa Giorgos Verigakis
    meta = util.get_vm_meta(vm, key)
652 d8e50a39 Giorgos Verigakis
    meta.delete()
653 5509b599 Giorgos Verigakis
    vm.save()
654 d8e50a39 Giorgos Verigakis
    return HttpResponse(status=204)
655 c738c935 Giorgos Verigakis
656 55cd40be Vangelis Koukis
657 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
658 c738c935 Giorgos Verigakis
def server_stats(request, server_id):
659 c738c935 Giorgos Verigakis
    # Normal Response Codes: 200
660 c738c935 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
661 c738c935 Giorgos Verigakis
    #                       serviceUnavailable (503),
662 c738c935 Giorgos Verigakis
    #                       unauthorized (401),
663 c738c935 Giorgos Verigakis
    #                       badRequest (400),
664 c738c935 Giorgos Verigakis
    #                       itemNotFound (404),
665 c738c935 Giorgos Verigakis
    #                       overLimit (413)
666 ce55f211 Kostas Papadimitriou
667 0c37a721 Christos Stavrakakis
    log.debug('server_stats %s', server_id)
668 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
669 924d8085 Christos Stavrakakis
    #secret = util.encrypt(vm.backend_vm_id)
670 924d8085 Christos Stavrakakis
    secret = vm.backend_vm_id      # XXX disable backend id encryption
671 ce55f211 Kostas Papadimitriou
672 c738c935 Giorgos Verigakis
    stats = {
673 c738c935 Giorgos Verigakis
        'serverRef': vm.id,
674 c738c935 Giorgos Verigakis
        'refresh': settings.STATS_REFRESH_PERIOD,
675 5391d6b5 Giorgos Verigakis
        'cpuBar': settings.CPU_BAR_GRAPH_URL % secret,
676 5391d6b5 Giorgos Verigakis
        'cpuTimeSeries': settings.CPU_TIMESERIES_GRAPH_URL % secret,
677 5391d6b5 Giorgos Verigakis
        'netBar': settings.NET_BAR_GRAPH_URL % secret,
678 5391d6b5 Giorgos Verigakis
        'netTimeSeries': settings.NET_TIMESERIES_GRAPH_URL % secret}
679 ce55f211 Kostas Papadimitriou
680 c738c935 Giorgos Verigakis
    if request.serialization == 'xml':
681 c738c935 Giorgos Verigakis
        data = render_to_string('server_stats.xml', stats)
682 c738c935 Giorgos Verigakis
    else:
683 c738c935 Giorgos Verigakis
        data = json.dumps({'stats': stats})
684 c738c935 Giorgos Verigakis
685 c738c935 Giorgos Verigakis
    return HttpResponse(data, status=200)
686 41a7fae7 Christos Stavrakakis
687 41a7fae7 Christos Stavrakakis
688 41a7fae7 Christos Stavrakakis
# ACTIONS
689 41a7fae7 Christos Stavrakakis
690 41a7fae7 Christos Stavrakakis
691 41a7fae7 Christos Stavrakakis
server_actions = {}
692 41a7fae7 Christos Stavrakakis
network_actions = {}
693 41a7fae7 Christos Stavrakakis
694 41a7fae7 Christos Stavrakakis
695 41a7fae7 Christos Stavrakakis
def server_action(name):
696 41a7fae7 Christos Stavrakakis
    '''Decorator for functions implementing server actions.
697 41a7fae7 Christos Stavrakakis
    `name` is the key in the dict passed by the client.
698 41a7fae7 Christos Stavrakakis
    '''
699 41a7fae7 Christos Stavrakakis
700 41a7fae7 Christos Stavrakakis
    def decorator(func):
701 41a7fae7 Christos Stavrakakis
        server_actions[name] = func
702 41a7fae7 Christos Stavrakakis
        return func
703 41a7fae7 Christos Stavrakakis
    return decorator
704 41a7fae7 Christos Stavrakakis
705 41a7fae7 Christos Stavrakakis
706 41a7fae7 Christos Stavrakakis
def network_action(name):
707 41a7fae7 Christos Stavrakakis
    '''Decorator for functions implementing network actions.
708 41a7fae7 Christos Stavrakakis
    `name` is the key in the dict passed by the client.
709 41a7fae7 Christos Stavrakakis
    '''
710 41a7fae7 Christos Stavrakakis
711 41a7fae7 Christos Stavrakakis
    def decorator(func):
712 41a7fae7 Christos Stavrakakis
        network_actions[name] = func
713 41a7fae7 Christos Stavrakakis
        return func
714 41a7fae7 Christos Stavrakakis
    return decorator
715 41a7fae7 Christos Stavrakakis
716 41a7fae7 Christos Stavrakakis
717 41a7fae7 Christos Stavrakakis
@server_action('start')
718 41a7fae7 Christos Stavrakakis
def start(request, vm, args):
719 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
720 41a7fae7 Christos Stavrakakis
    # Error Response Codes: serviceUnavailable (503),
721 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404)
722 41a7fae7 Christos Stavrakakis
    vm = servers.start(vm)
723 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
724 41a7fae7 Christos Stavrakakis
725 41a7fae7 Christos Stavrakakis
726 41a7fae7 Christos Stavrakakis
@server_action('shutdown')
727 41a7fae7 Christos Stavrakakis
def shutdown(request, vm, args):
728 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
729 41a7fae7 Christos Stavrakakis
    # Error Response Codes: serviceUnavailable (503),
730 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404)
731 41a7fae7 Christos Stavrakakis
    vm = servers.stop(vm)
732 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
733 41a7fae7 Christos Stavrakakis
734 41a7fae7 Christos Stavrakakis
735 41a7fae7 Christos Stavrakakis
@server_action('reboot')
736 41a7fae7 Christos Stavrakakis
def reboot(request, vm, args):
737 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
738 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
739 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
740 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
741 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
742 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
743 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
744 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
745 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
746 41a7fae7 Christos Stavrakakis
747 bbae3e45 Christos Stavrakakis
    reboot_type = args.get("type", "SOFT")
748 bbae3e45 Christos Stavrakakis
    if reboot_type not in ["SOFT", "HARD"]:
749 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Invalid 'type' attribute.")
750 41a7fae7 Christos Stavrakakis
    vm = servers.reboot(vm, reboot_type=reboot_type)
751 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
752 41a7fae7 Christos Stavrakakis
753 41a7fae7 Christos Stavrakakis
754 41a7fae7 Christos Stavrakakis
@server_action('firewallProfile')
755 41a7fae7 Christos Stavrakakis
def set_firewall_profile(request, vm, args):
756 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 200
757 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
758 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
759 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
760 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
761 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
762 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
763 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
764 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
765 41a7fae7 Christos Stavrakakis
    profile = args.get("profile")
766 41a7fae7 Christos Stavrakakis
    if profile is None:
767 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Missing 'profile' attribute")
768 b2791a77 Christos Stavrakakis
    index = args.get("index", 0)
769 b2791a77 Christos Stavrakakis
    servers.set_firewall_profile(vm, profile=profile, index=index)
770 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
771 41a7fae7 Christos Stavrakakis
772 41a7fae7 Christos Stavrakakis
773 41a7fae7 Christos Stavrakakis
@server_action('resize')
774 41a7fae7 Christos Stavrakakis
def resize(request, vm, args):
775 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
776 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
777 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
778 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
779 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
780 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
781 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
782 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
783 41a7fae7 Christos Stavrakakis
    #                       serverCapacityUnavailable (503),
784 41a7fae7 Christos Stavrakakis
    #                       overLimit (413),
785 41a7fae7 Christos Stavrakakis
    #                       resizeNotAllowed (403)
786 41a7fae7 Christos Stavrakakis
    flavorRef = args.get("flavorRef")
787 41a7fae7 Christos Stavrakakis
    if flavorRef is None:
788 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Missing 'flavorRef' attribute.")
789 41a7fae7 Christos Stavrakakis
    flavor = util.get_flavor(flavor_id=flavorRef, include_deleted=False)
790 41a7fae7 Christos Stavrakakis
    servers.resize(vm, flavor=flavor)
791 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
792 41a7fae7 Christos Stavrakakis
793 41a7fae7 Christos Stavrakakis
794 41a7fae7 Christos Stavrakakis
@server_action('console')
795 41a7fae7 Christos Stavrakakis
def get_console(request, vm, args):
796 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 200
797 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
798 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
799 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
800 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
801 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
802 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
803 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
804 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
805 41a7fae7 Christos Stavrakakis
806 41a7fae7 Christos Stavrakakis
    log.info("Get console  VM %s: %s", vm, args)
807 41a7fae7 Christos Stavrakakis
808 41a7fae7 Christos Stavrakakis
    console_type = args.get("type")
809 41a7fae7 Christos Stavrakakis
    if console_type is None:
810 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("No console 'type' specified.")
811 41a7fae7 Christos Stavrakakis
    elif console_type != "vnc":
812 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Console 'type' can only be 'vnc'.")
813 41a7fae7 Christos Stavrakakis
    console_info = servers.console(vm, console_type)
814 41a7fae7 Christos Stavrakakis
815 41a7fae7 Christos Stavrakakis
    if request.serialization == 'xml':
816 41a7fae7 Christos Stavrakakis
        mimetype = 'application/xml'
817 41a7fae7 Christos Stavrakakis
        data = render_to_string('console.xml', {'console': console_info})
818 41a7fae7 Christos Stavrakakis
    else:
819 41a7fae7 Christos Stavrakakis
        mimetype = 'application/json'
820 41a7fae7 Christos Stavrakakis
        data = json.dumps({'console': console_info})
821 41a7fae7 Christos Stavrakakis
822 41a7fae7 Christos Stavrakakis
    return HttpResponse(data, mimetype=mimetype, status=200)
823 41a7fae7 Christos Stavrakakis
824 41a7fae7 Christos Stavrakakis
825 41a7fae7 Christos Stavrakakis
@server_action('changePassword')
826 41a7fae7 Christos Stavrakakis
def change_password(request, vm, args):
827 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Changing password is not supported.')
828 41a7fae7 Christos Stavrakakis
829 41a7fae7 Christos Stavrakakis
830 41a7fae7 Christos Stavrakakis
@server_action('rebuild')
831 41a7fae7 Christos Stavrakakis
def rebuild(request, vm, args):
832 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Rebuild not supported.')
833 41a7fae7 Christos Stavrakakis
834 41a7fae7 Christos Stavrakakis
835 41a7fae7 Christos Stavrakakis
@server_action('confirmResize')
836 41a7fae7 Christos Stavrakakis
def confirm_resize(request, vm, args):
837 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Resize not supported.')
838 41a7fae7 Christos Stavrakakis
839 41a7fae7 Christos Stavrakakis
840 41a7fae7 Christos Stavrakakis
@server_action('revertResize')
841 41a7fae7 Christos Stavrakakis
def revert_resize(request, vm, args):
842 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Resize not supported.')
843 41a7fae7 Christos Stavrakakis
844 41a7fae7 Christos Stavrakakis
845 41a7fae7 Christos Stavrakakis
@network_action('add')
846 41a7fae7 Christos Stavrakakis
@transaction.commit_on_success
847 41a7fae7 Christos Stavrakakis
def add(request, net, args):
848 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
849 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
850 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
851 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
852 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
853 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
854 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
855 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
856 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
857 41a7fae7 Christos Stavrakakis
    server_id = args.get('serverRef', None)
858 41a7fae7 Christos Stavrakakis
    if not server_id:
859 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest('Malformed Request.')
860 41a7fae7 Christos Stavrakakis
861 41a7fae7 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
862 41a7fae7 Christos Stavrakakis
    servers.connect(vm, network=net)
863 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
864 41a7fae7 Christos Stavrakakis
865 41a7fae7 Christos Stavrakakis
866 41a7fae7 Christos Stavrakakis
@network_action('remove')
867 41a7fae7 Christos Stavrakakis
@transaction.commit_on_success
868 41a7fae7 Christos Stavrakakis
def remove(request, net, args):
869 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
870 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
871 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
872 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
873 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
874 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
875 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
876 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
877 41a7fae7 Christos Stavrakakis
878 41a7fae7 Christos Stavrakakis
    attachment = args.get("attachment")
879 41a7fae7 Christos Stavrakakis
    if attachment is None:
880 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Missing 'attachment' attribute.")
881 41a7fae7 Christos Stavrakakis
    try:
882 41a7fae7 Christos Stavrakakis
        # attachment string: nic-<vm-id>-<nic-index>
883 41a7fae7 Christos Stavrakakis
        _, server_id, nic_index = attachment.split("-", 2)
884 41a7fae7 Christos Stavrakakis
        server_id = int(server_id)
885 41a7fae7 Christos Stavrakakis
        nic_index = int(nic_index)
886 41a7fae7 Christos Stavrakakis
    except (ValueError, TypeError):
887 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Invalid 'attachment' attribute.")
888 41a7fae7 Christos Stavrakakis
889 41a7fae7 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
890 41a7fae7 Christos Stavrakakis
    servers.disconnect(vm, nic_index=nic_index)
891 41a7fae7 Christos Stavrakakis
892 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
893 9ba6bb95 Christos Stavrakakis
894 9ba6bb95 Christos Stavrakakis
895 f8675683 Christos Stavrakakis
@server_action("addFloatingIp")
896 9ba6bb95 Christos Stavrakakis
def add_floating_ip(request, vm, args):
897 9ba6bb95 Christos Stavrakakis
    address = args.get("address")
898 9ba6bb95 Christos Stavrakakis
    if address is None:
899 9ba6bb95 Christos Stavrakakis
        raise faults.BadRequest("Missing 'address' attribute")
900 9ba6bb95 Christos Stavrakakis
901 9ba6bb95 Christos Stavrakakis
    servers.add_floating_ip(vm, address)
902 9ba6bb95 Christos Stavrakakis
    return HttpResponse(status=202)
903 9ba6bb95 Christos Stavrakakis
904 9ba6bb95 Christos Stavrakakis
905 f8675683 Christos Stavrakakis
@server_action("removeFloatingIp")
906 9ba6bb95 Christos Stavrakakis
def remove_floating_ip(request, vm, args):
907 9ba6bb95 Christos Stavrakakis
    address = args.get("address")
908 9ba6bb95 Christos Stavrakakis
    if address is None:
909 9ba6bb95 Christos Stavrakakis
        raise faults.BadRequest("Missing 'address' attribute")
910 9ba6bb95 Christos Stavrakakis
911 9ba6bb95 Christos Stavrakakis
    servers.remove_floating_ip(vm, address)
912 9ba6bb95 Christos Stavrakakis
    return HttpResponse(status=202)