Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (39.8 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 910d960d Christos Stavrakakis
from synnefo.logic import servers, utils as logic_utils, server_attachments
48 910d960d Christos Stavrakakis
from synnefo.volume.util import get_volume
49 9e98ba3c Giorgos Verigakis
50 bf5c82dc Christos Stavrakakis
from logging import getLogger
51 b3fd98ae Christos Stavrakakis
log = getLogger(__name__)
52 7e2f9d4b Giorgos Verigakis
53 e440e835 Christos Stavrakakis
urlpatterns = patterns(
54 e440e835 Christos Stavrakakis
    'synnefo.api.servers',
55 7e2f9d4b Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
56 7e2f9d4b Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_servers', {'detail': True}),
57 7e2f9d4b Giorgos Verigakis
    (r'^/(\d+)(?:.json|.xml)?$', 'server_demux'),
58 41a7fae7 Christos Stavrakakis
    (r'^/(\d+)/action(?:.json|.xml)?$', 'demux_server_action'),
59 b016b476 Giorgos Verigakis
    (r'^/(\d+)/ips(?:.json|.xml)?$', 'list_addresses'),
60 b016b476 Giorgos Verigakis
    (r'^/(\d+)/ips/(.+?)(?:.json|.xml)?$', 'list_addresses_by_network'),
61 8a992938 Christos Stavrakakis
    (r'^/(\d+)/metadata(?:.json|.xml)?$', 'metadata_demux'),
62 8a992938 Christos Stavrakakis
    (r'^/(\d+)/metadata/(.+?)(?:.json|.xml)?$', 'metadata_item_demux'),
63 c738c935 Giorgos Verigakis
    (r'^/(\d+)/stats(?:.json|.xml)?$', 'server_stats'),
64 85a634e6 Kostas Papadimitriou
    (r'^/(\d+)/diagnostics(?:.json)?$', 'get_server_diagnostics'),
65 910d960d Christos Stavrakakis
    (r'^/(\d+)/os-volume_attachments(?:.json)?$', 'demux_volumes'),
66 910d960d Christos Stavrakakis
    (r'^/(\d+)/os-volume_attachments/(\d+)(?:.json)?$', 'demux_volumes_item'),
67 7e2f9d4b Giorgos Verigakis
)
68 7e2f9d4b Giorgos Verigakis
69 5d805533 Christos Stavrakakis
VOLUME_SOURCE_TYPES = [
70 5d805533 Christos Stavrakakis
    "snapshot",
71 5d805533 Christos Stavrakakis
    "image",
72 5d805533 Christos Stavrakakis
    "volume",
73 5d805533 Christos Stavrakakis
    "blank"
74 5d805533 Christos Stavrakakis
]
75 5d805533 Christos Stavrakakis
76 7e2f9d4b Giorgos Verigakis
77 7e2f9d4b Giorgos Verigakis
def demux(request):
78 7e2f9d4b Giorgos Verigakis
    if request.method == 'GET':
79 7e2f9d4b Giorgos Verigakis
        return list_servers(request)
80 7e2f9d4b Giorgos Verigakis
    elif request.method == 'POST':
81 7e2f9d4b Giorgos Verigakis
        return create_server(request)
82 7e2f9d4b Giorgos Verigakis
    else:
83 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
84 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET', 'POST'])
85 7e2f9d4b Giorgos Verigakis
86 55cd40be Vangelis Koukis
87 7e2f9d4b Giorgos Verigakis
def server_demux(request, server_id):
88 7e2f9d4b Giorgos Verigakis
    if request.method == 'GET':
89 7e2f9d4b Giorgos Verigakis
        return get_server_details(request, server_id)
90 7e2f9d4b Giorgos Verigakis
    elif request.method == 'PUT':
91 7e2f9d4b Giorgos Verigakis
        return update_server_name(request, server_id)
92 7e2f9d4b Giorgos Verigakis
    elif request.method == 'DELETE':
93 7e2f9d4b Giorgos Verigakis
        return delete_server(request, server_id)
94 7e2f9d4b Giorgos Verigakis
    else:
95 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
96 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET',
97 2aba7764 Sofia Papagiannaki
                                                           'PUT',
98 2aba7764 Sofia Papagiannaki
                                                           'DELETE'])
99 d8e50a39 Giorgos Verigakis
100 55cd40be Vangelis Koukis
101 d8e50a39 Giorgos Verigakis
def metadata_demux(request, server_id):
102 d8e50a39 Giorgos Verigakis
    if request.method == 'GET':
103 d8e50a39 Giorgos Verigakis
        return list_metadata(request, server_id)
104 d8e50a39 Giorgos Verigakis
    elif request.method == 'POST':
105 d8e50a39 Giorgos Verigakis
        return update_metadata(request, server_id)
106 d8e50a39 Giorgos Verigakis
    else:
107 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
108 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET', 'POST'])
109 d8e50a39 Giorgos Verigakis
110 55cd40be Vangelis Koukis
111 d8e50a39 Giorgos Verigakis
def metadata_item_demux(request, server_id, key):
112 d8e50a39 Giorgos Verigakis
    if request.method == 'GET':
113 d8e50a39 Giorgos Verigakis
        return get_metadata_item(request, server_id, key)
114 d8e50a39 Giorgos Verigakis
    elif request.method == 'PUT':
115 d8e50a39 Giorgos Verigakis
        return create_metadata_item(request, server_id, key)
116 d8e50a39 Giorgos Verigakis
    elif request.method == 'DELETE':
117 d8e50a39 Giorgos Verigakis
        return delete_metadata_item(request, server_id, key)
118 d8e50a39 Giorgos Verigakis
    else:
119 2aba7764 Sofia Papagiannaki
        return api.api_method_not_allowed(request,
120 2aba7764 Sofia Papagiannaki
                                          allowed_methods=['GET',
121 2aba7764 Sofia Papagiannaki
                                                           'PUT',
122 2aba7764 Sofia Papagiannaki
                                                           'DELETE'])
123 7e2f9d4b Giorgos Verigakis
124 08b079e2 Stavros Sachtouris
125 910d960d Christos Stavrakakis
def demux_volumes(request, server_id):
126 910d960d Christos Stavrakakis
    if request.method == 'GET':
127 910d960d Christos Stavrakakis
        return get_volumes(request, server_id)
128 910d960d Christos Stavrakakis
    elif request.method == 'POST':
129 910d960d Christos Stavrakakis
        return attach_volume(request, server_id)
130 910d960d Christos Stavrakakis
    else:
131 910d960d Christos Stavrakakis
        return api.api_method_not_allowed(request,
132 910d960d Christos Stavrakakis
                                          allowed_methods=['GET', 'POST'])
133 910d960d Christos Stavrakakis
134 910d960d Christos Stavrakakis
135 910d960d Christos Stavrakakis
def demux_volumes_item(request, server_id, volume_id):
136 910d960d Christos Stavrakakis
    if request.method == 'GET':
137 910d960d Christos Stavrakakis
        return get_volume_info(request, server_id, volume_id)
138 910d960d Christos Stavrakakis
    elif request.method == 'DELETE':
139 910d960d Christos Stavrakakis
        return detach_volume(request, server_id, volume_id)
140 910d960d Christos Stavrakakis
    else:
141 910d960d Christos Stavrakakis
        return api.api_method_not_allowed(request,
142 910d960d Christos Stavrakakis
                                          allowed_methods=['GET', 'DELETE'])
143 910d960d Christos Stavrakakis
144 910d960d Christos Stavrakakis
145 b3a43976 Christos Stavrakakis
def nic_to_attachments(nic):
146 b3a43976 Christos Stavrakakis
    """Convert a NIC object to 'attachments attribute.
147 b3a43976 Christos Stavrakakis

148 b3a43976 Christos Stavrakakis
    Convert a NIC object to match the format of 'attachments' attribute of the
149 b3a43976 Christos Stavrakakis
    response to the /servers API call.
150 b3a43976 Christos Stavrakakis

151 b3a43976 Christos Stavrakakis
    NOTE: The 'ips' of the NIC object have been prefetched in order to avoid DB
152 b3a43976 Christos Stavrakakis
    queries. No subsequent queries for 'ips' (like filtering) should be
153 b3a43976 Christos Stavrakakis
    performed because this will return in a new DB query.
154 b3a43976 Christos Stavrakakis

155 b3a43976 Christos Stavrakakis
    """
156 c988fcca Christos Stavrakakis
    d = {'id': nic.id,
157 b3a43976 Christos Stavrakakis
         'network_id': str(nic.network_id),
158 7fede91e Christos Stavrakakis
         'mac_address': nic.mac,
159 92d2d1ce Christos Stavrakakis
         'ipv4': '',
160 92d2d1ce Christos Stavrakakis
         'ipv6': ''}
161 47c66641 Dimitris Aragiorgis
162 207b70d5 Giorgos Verigakis
    if nic.firewall_profile:
163 207b70d5 Giorgos Verigakis
        d['firewallProfile'] = nic.firewall_profile
164 b3a43976 Christos Stavrakakis
165 b3a43976 Christos Stavrakakis
    for ip in nic.ips.all():
166 b3a43976 Christos Stavrakakis
        if not ip.deleted:
167 b3a43976 Christos Stavrakakis
            ip_type = "floating" if ip.floating_ip else "fixed"
168 b3a43976 Christos Stavrakakis
            if ip.ipversion == 4:
169 b3a43976 Christos Stavrakakis
                d["ipv4"] = ip.address
170 b3a43976 Christos Stavrakakis
                d["OS-EXT-IPS:type"] = ip_type
171 b3a43976 Christos Stavrakakis
            else:
172 b3a43976 Christos Stavrakakis
                d["ipv6"] = ip.address
173 b3a43976 Christos Stavrakakis
                d["OS-EXT-IPS:type"] = ip_type
174 d44c236b Giorgos Verigakis
    return d
175 b016b476 Giorgos Verigakis
176 55cd40be Vangelis Koukis
177 78c165cf Christos Stavrakakis
def attachments_to_addresses(attachments):
178 b3a43976 Christos Stavrakakis
    """Convert 'attachments' attribute to 'addresses'.
179 b3a43976 Christos Stavrakakis

180 b3a43976 Christos Stavrakakis
    Convert a a list of 'attachments' attribute to a list of 'addresses'
181 b3a43976 Christos Stavrakakis
    attribute, as expected in the response to /servers API call.
182 b3a43976 Christos Stavrakakis

183 b3a43976 Christos Stavrakakis
    """
184 1f638a85 Christos Stavrakakis
    addresses = {}
185 78c165cf Christos Stavrakakis
    for nic in attachments:
186 b3a43976 Christos Stavrakakis
        net_addrs = []
187 92d2d1ce Christos Stavrakakis
        if nic["ipv4"]:
188 b3a43976 Christos Stavrakakis
            net_addrs.append({"version": 4,
189 b3a43976 Christos Stavrakakis
                              "addr": nic["ipv4"],
190 b3a43976 Christos Stavrakakis
                              "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
191 78c165cf Christos Stavrakakis
        if nic["ipv6"]:
192 b3a43976 Christos Stavrakakis
            net_addrs.append({"version": 6,
193 b3a43976 Christos Stavrakakis
                              "addr": nic["ipv6"],
194 b3a43976 Christos Stavrakakis
                              "OS-EXT-IPS:type": nic["OS-EXT-IPS:type"]})
195 b3a43976 Christos Stavrakakis
        addresses[nic["network_id"]] = net_addrs
196 1f638a85 Christos Stavrakakis
    return addresses
197 1f638a85 Christos Stavrakakis
198 1f638a85 Christos Stavrakakis
199 d8e50a39 Giorgos Verigakis
def vm_to_dict(vm, detail=False):
200 d8e50a39 Giorgos Verigakis
    d = dict(id=vm.id, name=vm.name)
201 1b696c26 Christos Stavrakakis
    d['links'] = util.vm_to_links(vm.id)
202 7e2f9d4b Giorgos Verigakis
    if detail:
203 936e0be3 Christos Stavrakakis
        d['user_id'] = vm.userid
204 936e0be3 Christos Stavrakakis
        d['tenant_id'] = vm.userid
205 41a7fae7 Christos Stavrakakis
        d['status'] = logic_utils.get_rsapi_state(vm)
206 41a7fae7 Christos Stavrakakis
        d['SNF:task_state'] = logic_utils.get_task_state(vm)
207 41a7fae7 Christos Stavrakakis
        d['progress'] = 100 if d['status'] == 'ACTIVE' else vm.buildpercentage
208 d8e50a39 Giorgos Verigakis
        d['hostId'] = vm.hostid
209 b3fd98ae Christos Stavrakakis
        d['updated'] = utils.isoformat(vm.updated)
210 b3fd98ae Christos Stavrakakis
        d['created'] = utils.isoformat(vm.created)
211 1b696c26 Christos Stavrakakis
        d['flavor'] = {"id": vm.flavor.id,
212 1b696c26 Christos Stavrakakis
                       "links": util.flavor_to_links(vm.flavor.id)}
213 1b696c26 Christos Stavrakakis
        d['image'] = {"id": vm.imageid,
214 1b696c26 Christos Stavrakakis
                      "links": util.image_to_links(vm.imageid)}
215 e221ade2 Christos Stavrakakis
        d['suspended'] = vm.suspended
216 ce55f211 Kostas Papadimitriou
217 7cc3c7d9 Giorgos Verigakis
        metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
218 1b696c26 Christos Stavrakakis
        d['metadata'] = metadata
219 aa197ee4 Vangelis Koukis
220 b3a43976 Christos Stavrakakis
        nics = vm.nics.all()
221 b3a43976 Christos Stavrakakis
        active_nics = filter(lambda nic: nic.state == "ACTIVE", nics)
222 b3a43976 Christos Stavrakakis
        active_nics.sort(key=lambda nic: nic.id)
223 b3a43976 Christos Stavrakakis
        attachments = map(nic_to_attachments, active_nics)
224 5029ff36 Christos Stavrakakis
        d['attachments'] = attachments
225 78c165cf Christos Stavrakakis
        d['addresses'] = attachments_to_addresses(attachments)
226 85a634e6 Kostas Papadimitriou
227 e7953d63 Christos Stavrakakis
        d['volumes'] = [v.id for v in vm.volumes.order_by('id')]
228 e7953d63 Christos Stavrakakis
229 85a634e6 Kostas Papadimitriou
        # include the latest vm diagnostic, if set
230 85a634e6 Kostas Papadimitriou
        diagnostic = vm.get_last_diagnostic()
231 85a634e6 Kostas Papadimitriou
        if diagnostic:
232 85a634e6 Kostas Papadimitriou
            d['diagnostics'] = diagnostics_to_dict([diagnostic])
233 1b696c26 Christos Stavrakakis
        else:
234 1b696c26 Christos Stavrakakis
            d['diagnostics'] = []
235 f8352d29 Christos Stavrakakis
        # Fixed
236 f8352d29 Christos Stavrakakis
        d["security_groups"] = [{"name": "default"}]
237 f8352d29 Christos Stavrakakis
        d["key_name"] = None
238 f8352d29 Christos Stavrakakis
        d["config_drive"] = ""
239 f8352d29 Christos Stavrakakis
        d["accessIPv4"] = ""
240 f8352d29 Christos Stavrakakis
        d["accessIPv6"] = ""
241 b3a43976 Christos Stavrakakis
        fqdn = get_server_fqdn(vm, active_nics)
242 2522e489 Christos Stavrakakis
        d["SNF:fqdn"] = fqdn
243 b3a43976 Christos Stavrakakis
        d["SNF:port_forwarding"] = get_server_port_forwarding(vm, active_nics,
244 b3a43976 Christos Stavrakakis
                                                              fqdn)
245 7219c7a3 Christos Stavrakakis
        d['deleted'] = vm.deleted
246 7e2f9d4b Giorgos Verigakis
    return d
247 7e2f9d4b Giorgos Verigakis
248 d8e50a39 Giorgos Verigakis
249 b3a43976 Christos Stavrakakis
def get_server_public_ip(vm_nics, version=4):
250 b3a43976 Christos Stavrakakis
    """Get the first public IP address of a server.
251 b3a43976 Christos Stavrakakis

252 b3a43976 Christos Stavrakakis
    NOTE: 'vm_nics' objects have prefetched the ips
253 b3a43976 Christos Stavrakakis
    """
254 734acd05 Christos Stavrakakis
    for nic in vm_nics:
255 734acd05 Christos Stavrakakis
        for ip in nic.ips.all():
256 734acd05 Christos Stavrakakis
            if ip.ipversion == version and ip.public:
257 734acd05 Christos Stavrakakis
                return ip
258 b3a43976 Christos Stavrakakis
    return None
259 b3a43976 Christos Stavrakakis
260 b3a43976 Christos Stavrakakis
261 b3a43976 Christos Stavrakakis
def get_server_fqdn(vm, vm_nics):
262 d328a525 Christos Stavrakakis
    fqdn_setting = settings.CYCLADES_SERVERS_FQDN
263 d328a525 Christos Stavrakakis
    if fqdn_setting is None:
264 9cec0c17 Christos Stavrakakis
        return None
265 d328a525 Christos Stavrakakis
    elif isinstance(fqdn_setting, basestring):
266 d328a525 Christos Stavrakakis
        return fqdn_setting % {"id": vm.id}
267 d328a525 Christos Stavrakakis
    else:
268 d328a525 Christos Stavrakakis
        msg = ("Invalid setting: CYCLADES_SERVERS_FQDN."
269 d328a525 Christos Stavrakakis
               " Value must be a string.")
270 d328a525 Christos Stavrakakis
        raise faults.InternalServerError(msg)
271 d328a525 Christos Stavrakakis
272 d328a525 Christos Stavrakakis
273 b3a43976 Christos Stavrakakis
def get_server_port_forwarding(vm, vm_nics, fqdn):
274 2522e489 Christos Stavrakakis
    """Create API 'port_forwarding' attribute from corresponding setting.
275 2522e489 Christos Stavrakakis

276 2522e489 Christos Stavrakakis
    Create the 'port_forwarding' API vm attribute based on the corresponding
277 2522e489 Christos Stavrakakis
    setting (CYCLADES_PORT_FORWARDING), which can be either a tuple
278 2522e489 Christos Stavrakakis
    of the form (host, port) or a callable object returning such tuple. In
279 2522e489 Christos Stavrakakis
    case of callable object, must be called with the following arguments:
280 2522e489 Christos Stavrakakis
    * ip_address
281 2522e489 Christos Stavrakakis
    * server_id
282 2522e489 Christos Stavrakakis
    * fqdn
283 2522e489 Christos Stavrakakis
    * owner UUID
284 2522e489 Christos Stavrakakis

285 b3a43976 Christos Stavrakakis
    NOTE: 'vm_nics' objects have prefetched the ips
286 2522e489 Christos Stavrakakis
    """
287 2522e489 Christos Stavrakakis
    port_forwarding = {}
288 b3a43976 Christos Stavrakakis
    public_ip = get_server_public_ip(vm_nics)
289 b3a43976 Christos Stavrakakis
    if public_ip is None:
290 b3a43976 Christos Stavrakakis
        return port_forwarding
291 2522e489 Christos Stavrakakis
    for dport, to_dest in settings.CYCLADES_PORT_FORWARDING.items():
292 2522e489 Christos Stavrakakis
        if hasattr(to_dest, "__call__"):
293 b3a43976 Christos Stavrakakis
            to_dest = to_dest(public_ip.address, vm.id, fqdn, vm.userid)
294 2522e489 Christos Stavrakakis
        msg = ("Invalid setting: CYCLADES_PORT_FOWARDING."
295 2522e489 Christos Stavrakakis
               " Value must be a tuple of two elements (host, port).")
296 2522e489 Christos Stavrakakis
        if not isinstance(to_dest, tuple) or len(to_dest) != 2:
297 2522e489 Christos Stavrakakis
                raise faults.InternalServerError(msg)
298 2522e489 Christos Stavrakakis
        else:
299 2522e489 Christos Stavrakakis
            try:
300 2522e489 Christos Stavrakakis
                host, port = to_dest
301 2522e489 Christos Stavrakakis
            except (TypeError, ValueError):
302 2522e489 Christos Stavrakakis
                raise faults.InternalServerError(msg)
303 2522e489 Christos Stavrakakis
304 2522e489 Christos Stavrakakis
        port_forwarding[dport] = {"host": host, "port": str(port)}
305 2522e489 Christos Stavrakakis
    return port_forwarding
306 2522e489 Christos Stavrakakis
307 2522e489 Christos Stavrakakis
308 85a634e6 Kostas Papadimitriou
def diagnostics_to_dict(diagnostics):
309 85a634e6 Kostas Papadimitriou
    """
310 85a634e6 Kostas Papadimitriou
    Extract api data from diagnostics QuerySet.
311 85a634e6 Kostas Papadimitriou
    """
312 85a634e6 Kostas Papadimitriou
    entries = list()
313 85a634e6 Kostas Papadimitriou
314 85a634e6 Kostas Papadimitriou
    for diagnostic in diagnostics:
315 85a634e6 Kostas Papadimitriou
        # format source date if set
316 85a634e6 Kostas Papadimitriou
        formatted_source_date = None
317 85a634e6 Kostas Papadimitriou
        if diagnostic.source_date:
318 b3fd98ae Christos Stavrakakis
            formatted_source_date = utils.isoformat(diagnostic.source_date)
319 85a634e6 Kostas Papadimitriou
320 85a634e6 Kostas Papadimitriou
        entry = {
321 85a634e6 Kostas Papadimitriou
            'source': diagnostic.source,
322 b3fd98ae Christos Stavrakakis
            'created': utils.isoformat(diagnostic.created),
323 85a634e6 Kostas Papadimitriou
            'message': diagnostic.message,
324 85a634e6 Kostas Papadimitriou
            'details': diagnostic.details,
325 85a634e6 Kostas Papadimitriou
            'level': diagnostic.level,
326 85a634e6 Kostas Papadimitriou
        }
327 85a634e6 Kostas Papadimitriou
328 85a634e6 Kostas Papadimitriou
        if formatted_source_date:
329 85a634e6 Kostas Papadimitriou
            entry['source_date'] = formatted_source_date
330 85a634e6 Kostas Papadimitriou
331 85a634e6 Kostas Papadimitriou
        entries.append(entry)
332 85a634e6 Kostas Papadimitriou
333 85a634e6 Kostas Papadimitriou
    return entries
334 85a634e6 Kostas Papadimitriou
335 85a634e6 Kostas Papadimitriou
336 d8e50a39 Giorgos Verigakis
def render_server(request, server, status=200):
337 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
338 b36f78fa Giorgos Verigakis
        data = render_to_string('server.xml', {
339 b36f78fa Giorgos Verigakis
            'server': server,
340 b36f78fa Giorgos Verigakis
            'is_root': True})
341 7e2f9d4b Giorgos Verigakis
    else:
342 d8e50a39 Giorgos Verigakis
        data = json.dumps({'server': server})
343 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=status)
344 aa197ee4 Vangelis Koukis
345 7e2f9d4b Giorgos Verigakis
346 85a634e6 Kostas Papadimitriou
def render_diagnostics(request, diagnostics_dict, status=200):
347 85a634e6 Kostas Papadimitriou
    """
348 85a634e6 Kostas Papadimitriou
    Render diagnostics dictionary to json response.
349 85a634e6 Kostas Papadimitriou
    """
350 85a634e6 Kostas Papadimitriou
    return HttpResponse(json.dumps(diagnostics_dict), status=status)
351 85a634e6 Kostas Papadimitriou
352 85a634e6 Kostas Papadimitriou
353 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
354 85a634e6 Kostas Papadimitriou
def get_server_diagnostics(request, server_id):
355 85a634e6 Kostas Papadimitriou
    """
356 85a634e6 Kostas Papadimitriou
    Virtual machine diagnostics api view.
357 85a634e6 Kostas Papadimitriou
    """
358 85a634e6 Kostas Papadimitriou
    log.debug('server_diagnostics %s', server_id)
359 85a634e6 Kostas Papadimitriou
    vm = util.get_vm(server_id, request.user_uniq)
360 85a634e6 Kostas Papadimitriou
    diagnostics = diagnostics_to_dict(vm.diagnostics.all())
361 85a634e6 Kostas Papadimitriou
    return render_diagnostics(request, diagnostics)
362 85a634e6 Kostas Papadimitriou
363 85a634e6 Kostas Papadimitriou
364 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
365 7e2f9d4b Giorgos Verigakis
def list_servers(request, detail=False):
366 7e2f9d4b Giorgos Verigakis
    # Normal Response Codes: 200, 203
367 7e2f9d4b Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
368 7e2f9d4b Giorgos Verigakis
    #                       serviceUnavailable (503),
369 7e2f9d4b Giorgos Verigakis
    #                       unauthorized (401),
370 7e2f9d4b Giorgos Verigakis
    #                       badRequest (400),
371 7e2f9d4b Giorgos Verigakis
    #                       overLimit (413)
372 ce55f211 Kostas Papadimitriou
373 0c37a721 Christos Stavrakakis
    log.debug('list_servers detail=%s', detail)
374 e524ed5f Kostas Papadimitriou
    user_vms = VirtualMachine.objects.filter(userid=request.user_uniq)
375 b3a43976 Christos Stavrakakis
    if detail:
376 b3a43976 Christos Stavrakakis
        user_vms = user_vms.prefetch_related("nics__ips")
377 ce55f211 Kostas Papadimitriou
378 b6bc4afa Christos Stavrakakis
    user_vms = utils.filter_modified_since(request, objects=user_vms)
379 ce55f211 Kostas Papadimitriou
380 41a7fae7 Christos Stavrakakis
    servers_dict = [vm_to_dict(server, detail)
381 41a7fae7 Christos Stavrakakis
                    for server in user_vms.order_by('id')]
382 aa197ee4 Vangelis Koukis
383 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
384 b36f78fa Giorgos Verigakis
        data = render_to_string('list_servers.xml', {
385 41a7fae7 Christos Stavrakakis
            'servers': servers_dict,
386 b36f78fa Giorgos Verigakis
            'detail': detail})
387 7e2f9d4b Giorgos Verigakis
    else:
388 41a7fae7 Christos Stavrakakis
        data = json.dumps({'servers': servers_dict})
389 aa197ee4 Vangelis Koukis
390 c36934a7 Giorgos Verigakis
    return HttpResponse(data, status=200)
391 7e2f9d4b Giorgos Verigakis
392 55cd40be Vangelis Koukis
393 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
394 2509ce17 Christos Stavrakakis
def create_server(request):
395 7e2f9d4b Giorgos Verigakis
    # Normal Response Code: 202
396 7e2f9d4b Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
397 7e2f9d4b Giorgos Verigakis
    #                       serviceUnavailable (503),
398 7e2f9d4b Giorgos Verigakis
    #                       unauthorized (401),
399 7e2f9d4b Giorgos Verigakis
    #                       badMediaType(415),
400 7e2f9d4b Giorgos Verigakis
    #                       itemNotFound (404),
401 7e2f9d4b Giorgos Verigakis
    #                       badRequest (400),
402 7e2f9d4b Giorgos Verigakis
    #                       serverCapacityUnavailable (503),
403 7e2f9d4b Giorgos Verigakis
    #                       overLimit (413)
404 bcd80cd9 Christos Stavrakakis
    req = utils.get_request_dict(request)
405 bcd80cd9 Christos Stavrakakis
    log.info('create_server %s', req)
406 bcd80cd9 Christos Stavrakakis
    user_id = request.user_uniq
407 bcd80cd9 Christos Stavrakakis
408 7e2f9d4b Giorgos Verigakis
    try:
409 bcd80cd9 Christos Stavrakakis
        server = req['server']
410 bcd80cd9 Christos Stavrakakis
        name = server['name']
411 bcd80cd9 Christos Stavrakakis
        metadata = server.get('metadata', {})
412 bcd80cd9 Christos Stavrakakis
        assert isinstance(metadata, dict)
413 bcd80cd9 Christos Stavrakakis
        image_id = server['imageRef']
414 bcd80cd9 Christos Stavrakakis
        flavor_id = server['flavorRef']
415 bcd80cd9 Christos Stavrakakis
        personality = server.get('personality', [])
416 bcd80cd9 Christos Stavrakakis
        assert isinstance(personality, list)
417 3aecadc8 Christos Stavrakakis
        networks = server.get("networks")
418 3aecadc8 Christos Stavrakakis
        if networks is not None:
419 3aecadc8 Christos Stavrakakis
            assert isinstance(networks, list)
420 bcd80cd9 Christos Stavrakakis
    except (KeyError, AssertionError):
421 bcd80cd9 Christos Stavrakakis
        raise faults.BadRequest("Malformed request")
422 bcd80cd9 Christos Stavrakakis
423 5d805533 Christos Stavrakakis
    volumes = None
424 5d805533 Christos Stavrakakis
    dev_map = server.get("block_device_mapping_v2")
425 5d805533 Christos Stavrakakis
    if dev_map is not None:
426 5d805533 Christos Stavrakakis
        volumes = parse_block_device_mapping(dev_map)
427 5d805533 Christos Stavrakakis
428 bcd80cd9 Christos Stavrakakis
    # Verify that personalities are well-formed
429 bcd80cd9 Christos Stavrakakis
    util.verify_personality(personality)
430 bcd80cd9 Christos Stavrakakis
    # Get flavor (ensure it is active)
431 bcd80cd9 Christos Stavrakakis
    flavor = util.get_flavor(flavor_id, include_deleted=False)
432 62b76f38 Christos Stavrakakis
    if not flavor.allow_create:
433 62b76f38 Christos Stavrakakis
        msg = ("It is not allowed to create a server from flavor with id '%d',"
434 62b76f38 Christos Stavrakakis
               " see 'allow_create' flavor attribute")
435 62b76f38 Christos Stavrakakis
        raise faults.Forbidden(msg % flavor.id)
436 bcd80cd9 Christos Stavrakakis
    # Generate password
437 bcd80cd9 Christos Stavrakakis
    password = util.random_password()
438 bcd80cd9 Christos Stavrakakis
439 5d805533 Christos Stavrakakis
    vm = servers.create(user_id, name, password, flavor, image_id,
440 cb66110b Christos Stavrakakis
                        metadata=metadata, personality=personality,
441 5d805533 Christos Stavrakakis
                        networks=networks, volumes=volumes)
442 bcd80cd9 Christos Stavrakakis
443 bcd80cd9 Christos Stavrakakis
    server = vm_to_dict(vm, detail=True)
444 bcd80cd9 Christos Stavrakakis
    server['status'] = 'BUILD'
445 bcd80cd9 Christos Stavrakakis
    server['adminPass'] = password
446 bcd80cd9 Christos Stavrakakis
447 bcd80cd9 Christos Stavrakakis
    response = render_server(request, server, status=202)
448 4b8e03e5 Christos Stavrakakis
449 bcd80cd9 Christos Stavrakakis
    return response
450 bcd80cd9 Christos Stavrakakis
451 bcd80cd9 Christos Stavrakakis
452 5d805533 Christos Stavrakakis
def parse_block_device_mapping(dev_map):
453 5d805533 Christos Stavrakakis
    """Parse 'block_device_mapping_v2' attribute"""
454 5d805533 Christos Stavrakakis
    if not isinstance(dev_map, list):
455 5d805533 Christos Stavrakakis
        raise faults.BadRequest("Block Device Mapping is Invalid")
456 5d805533 Christos Stavrakakis
    return [_parse_block_device(device) for device in dev_map]
457 5d805533 Christos Stavrakakis
458 5d805533 Christos Stavrakakis
459 5d805533 Christos Stavrakakis
def _parse_block_device(device):
460 5d805533 Christos Stavrakakis
    """Parse and validate a block device mapping"""
461 5d805533 Christos Stavrakakis
    if not isinstance(device, dict):
462 5d805533 Christos Stavrakakis
        raise faults.BadRequest("Block Device Mapping is Invalid")
463 5d805533 Christos Stavrakakis
464 5d805533 Christos Stavrakakis
    # Validate source type
465 5d805533 Christos Stavrakakis
    source_type = device.get("source_type")
466 5d805533 Christos Stavrakakis
    if source_type is None:
467 5d805533 Christos Stavrakakis
        raise faults.BadRequest("Block Device Mapping is Invalid: Invalid"
468 5d805533 Christos Stavrakakis
                                " source_type field")
469 5d805533 Christos Stavrakakis
    elif source_type not in VOLUME_SOURCE_TYPES:
470 5d805533 Christos Stavrakakis
        raise faults.BadRequest("Block Device Mapping is Invalid: source_type"
471 5d805533 Christos Stavrakakis
                                " must be on of %s"
472 5d805533 Christos Stavrakakis
                                % ", ".join(VOLUME_SOURCE_TYPES))
473 5d805533 Christos Stavrakakis
474 5d805533 Christos Stavrakakis
    # Validate source UUID
475 5d805533 Christos Stavrakakis
    uuid = device.get("uuid")
476 5d805533 Christos Stavrakakis
    if uuid is None and source_type != "blank":
477 5d805533 Christos Stavrakakis
        raise faults.BadRequest("Block Device Mapping is Invalid: uuid of"
478 5d805533 Christos Stavrakakis
                                " %s is missing" % source_type)
479 5d805533 Christos Stavrakakis
480 5d805533 Christos Stavrakakis
    # Validate volume size
481 5d805533 Christos Stavrakakis
    size = device.get("volume_size")
482 5d805533 Christos Stavrakakis
    if size is not None:
483 5d805533 Christos Stavrakakis
        try:
484 5d805533 Christos Stavrakakis
            size = int(size)
485 5d805533 Christos Stavrakakis
        except (TypeError, ValueError):
486 5d805533 Christos Stavrakakis
            raise faults.BadRequest("Block Device Mapping is Invalid: Invalid"
487 5d805533 Christos Stavrakakis
                                    " size field")
488 5d805533 Christos Stavrakakis
489 5d805533 Christos Stavrakakis
    # Validate 'delete_on_termination'
490 5d805533 Christos Stavrakakis
    delete_on_termination = device.get("delete_on_termination")
491 5d805533 Christos Stavrakakis
    if delete_on_termination is not None:
492 5d805533 Christos Stavrakakis
        if not isinstance(delete_on_termination, bool):
493 5d805533 Christos Stavrakakis
            raise faults.BadRequest("Block Device Mapping is Invalid: Invalid"
494 5d805533 Christos Stavrakakis
                                    " delete_on_termination field")
495 5d805533 Christos Stavrakakis
    else:
496 5d805533 Christos Stavrakakis
        if source_type == "volume":
497 5d805533 Christos Stavrakakis
            delete_on_termination = False
498 5d805533 Christos Stavrakakis
        else:
499 5d805533 Christos Stavrakakis
            delete_on_termination = True
500 5d805533 Christos Stavrakakis
501 5d805533 Christos Stavrakakis
    # Unused API Attributes
502 5d805533 Christos Stavrakakis
    # boot_index = device.get("boot_index")
503 5d805533 Christos Stavrakakis
    # destination_type = device.get("destination_type")
504 5d805533 Christos Stavrakakis
505 5d805533 Christos Stavrakakis
    return {"source_type": source_type,
506 5d805533 Christos Stavrakakis
            "source_uuid": uuid,
507 5d805533 Christos Stavrakakis
            "size": size,
508 5d805533 Christos Stavrakakis
            "delete_on_termination": delete_on_termination}
509 5d805533 Christos Stavrakakis
510 5d805533 Christos Stavrakakis
511 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
512 7e2f9d4b Giorgos Verigakis
def get_server_details(request, server_id):
513 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 200, 203
514 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
515 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
516 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
517 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
518 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
519 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
520 ce55f211 Kostas Papadimitriou
521 0c37a721 Christos Stavrakakis
    log.debug('get_server_details %s', server_id)
522 b3a43976 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq,
523 b3a43976 Christos Stavrakakis
                     prefetch_related="nics__ips")
524 d8e50a39 Giorgos Verigakis
    server = vm_to_dict(vm, detail=True)
525 d8e50a39 Giorgos Verigakis
    return render_server(request, server)
526 7e2f9d4b Giorgos Verigakis
527 55cd40be Vangelis Koukis
528 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='PUT', user_required=True, logger=log)
529 874383a4 Christos Stavrakakis
@transaction.commit_on_success
530 c36934a7 Giorgos Verigakis
def update_server_name(request, server_id):
531 c36934a7 Giorgos Verigakis
    # Normal Response Code: 204
532 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
533 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
534 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
535 c36934a7 Giorgos Verigakis
    #                       badRequest (400),
536 c36934a7 Giorgos Verigakis
    #                       badMediaType(415),
537 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
538 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
539 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
540 aa197ee4 Vangelis Koukis
541 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
542 bf5c82dc Christos Stavrakakis
    log.info('update_server_name %s %s', server_id, req)
543 ce55f211 Kostas Papadimitriou
544 14402edc Christos Stavrakakis
    req = utils.get_attribute(req, "server", attr_type=dict, required=True)
545 14402edc Christos Stavrakakis
    name = utils.get_attribute(req, "name", attr_type=basestring,
546 14402edc Christos Stavrakakis
                               required=True)
547 aa197ee4 Vangelis Koukis
548 7f2dbcad Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=True,
549 7f2dbcad Christos Stavrakakis
                     non_suspended=True)
550 a52cc1b4 Christos Stavrakakis
551 a52cc1b4 Christos Stavrakakis
    servers.rename(vm, new_name=name)
552 aa197ee4 Vangelis Koukis
553 7e2f9d4b Giorgos Verigakis
    return HttpResponse(status=204)
554 7e2f9d4b Giorgos Verigakis
555 55cd40be Vangelis Koukis
556 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log)
557 7e2f9d4b Giorgos Verigakis
def delete_server(request, server_id):
558 c36934a7 Giorgos Verigakis
    # Normal Response Codes: 204
559 c36934a7 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
560 c36934a7 Giorgos Verigakis
    #                       serviceUnavailable (503),
561 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
562 c36934a7 Giorgos Verigakis
    #                       itemNotFound (404),
563 c36934a7 Giorgos Verigakis
    #                       unauthorized (401),
564 c36934a7 Giorgos Verigakis
    #                       buildInProgress (409),
565 c36934a7 Giorgos Verigakis
    #                       overLimit (413)
566 ce55f211 Kostas Papadimitriou
567 bf5c82dc Christos Stavrakakis
    log.info('delete_server %s', server_id)
568 7f2dbcad Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=True,
569 7f2dbcad Christos Stavrakakis
                     non_suspended=True)
570 41a7fae7 Christos Stavrakakis
    vm = servers.destroy(vm)
571 7e2f9d4b Giorgos Verigakis
    return HttpResponse(status=204)
572 b016b476 Giorgos Verigakis
573 55cd40be Vangelis Koukis
574 04a95cf3 Kostas Papadimitriou
# additional server actions
575 04a95cf3 Kostas Papadimitriou
ARBITRARY_ACTIONS = ['console', 'firewallProfile']
576 04a95cf3 Kostas Papadimitriou
577 e440e835 Christos Stavrakakis
578 7f2dbcad Christos Stavrakakis
def key_to_action(key):
579 7f2dbcad Christos Stavrakakis
    """Map HTTP request key to a VM Action"""
580 7f2dbcad Christos Stavrakakis
    if key == "shutdown":
581 7f2dbcad Christos Stavrakakis
        return "STOP"
582 7f2dbcad Christos Stavrakakis
    if key == "delete":
583 7f2dbcad Christos Stavrakakis
        return "DESTROY"
584 b7f21824 Kostas Papadimitriou
    if key in ARBITRARY_ACTIONS:
585 7f2dbcad Christos Stavrakakis
        return None
586 7f2dbcad Christos Stavrakakis
    else:
587 7f2dbcad Christos Stavrakakis
        return key.upper()
588 7f2dbcad Christos Stavrakakis
589 7f2dbcad Christos Stavrakakis
590 41a7fae7 Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
591 41a7fae7 Christos Stavrakakis
@transaction.commit_on_success
592 41a7fae7 Christos Stavrakakis
def demux_server_action(request, server_id):
593 41a7fae7 Christos Stavrakakis
    req = utils.get_request_dict(request)
594 41a7fae7 Christos Stavrakakis
    log.debug('server_action %s %s', server_id, req)
595 7f2dbcad Christos Stavrakakis
596 14402edc Christos Stavrakakis
    if not isinstance(req, dict) and len(req) != 1:
597 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Malformed request")
598 41a7fae7 Christos Stavrakakis
599 41a7fae7 Christos Stavrakakis
    # Do not allow any action on deleted or suspended VMs
600 41a7fae7 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=True,
601 41a7fae7 Christos Stavrakakis
                     non_deleted=True, non_suspended=True)
602 7f2dbcad Christos Stavrakakis
603 ece5581b Christos Stavrakakis
    action = req.keys()[0]
604 14402edc Christos Stavrakakis
    if not isinstance(action, basestring):
605 14402edc Christos Stavrakakis
        raise faults.BadRequest("Malformed Request. Invalid action.")
606 7f2dbcad Christos Stavrakakis
607 41a7fae7 Christos Stavrakakis
    if key_to_action(action) not in [x[0] for x in VirtualMachine.ACTIONS]:
608 41a7fae7 Christos Stavrakakis
        if action not in ARBITRARY_ACTIONS:
609 41a7fae7 Christos Stavrakakis
            raise faults.BadRequest("Action %s not supported" % action)
610 14402edc Christos Stavrakakis
    action_args = utils.get_attribute(req, action, required=True,
611 14402edc Christos Stavrakakis
                                      attr_type=dict)
612 7f2dbcad Christos Stavrakakis
613 41a7fae7 Christos Stavrakakis
    return server_actions[action](request, vm, action_args)
614 7f2dbcad Christos Stavrakakis
615 7f2dbcad Christos Stavrakakis
616 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
617 b016b476 Giorgos Verigakis
def list_addresses(request, server_id):
618 b016b476 Giorgos Verigakis
    # Normal Response Codes: 200, 203
619 b016b476 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
620 b016b476 Giorgos Verigakis
    #                       serviceUnavailable (503),
621 b016b476 Giorgos Verigakis
    #                       unauthorized (401),
622 b016b476 Giorgos Verigakis
    #                       badRequest (400),
623 b016b476 Giorgos Verigakis
    #                       overLimit (413)
624 ce55f211 Kostas Papadimitriou
625 0c37a721 Christos Stavrakakis
    log.debug('list_addresses %s', server_id)
626 14402edc Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq,
627 14402edc Christos Stavrakakis
                     prefetch_related="nics__ips")
628 b3a43976 Christos Stavrakakis
    attachments = [nic_to_attachments(nic)
629 b3a43976 Christos Stavrakakis
                   for nic in vm.nics.filter(state="ACTIVE")]
630 78c165cf Christos Stavrakakis
    addresses = attachments_to_addresses(attachments)
631 ce55f211 Kostas Papadimitriou
632 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
633 b016b476 Giorgos Verigakis
        data = render_to_string('list_addresses.xml', {'addresses': addresses})
634 b016b476 Giorgos Verigakis
    else:
635 1f638a85 Christos Stavrakakis
        data = json.dumps({'addresses': addresses, 'attachments': attachments})
636 aa197ee4 Vangelis Koukis
637 b016b476 Giorgos Verigakis
    return HttpResponse(data, status=200)
638 b016b476 Giorgos Verigakis
639 55cd40be Vangelis Koukis
640 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
641 b016b476 Giorgos Verigakis
def list_addresses_by_network(request, server_id, network_id):
642 b016b476 Giorgos Verigakis
    # Normal Response Codes: 200, 203
643 b016b476 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
644 b016b476 Giorgos Verigakis
    #                       serviceUnavailable (503),
645 b016b476 Giorgos Verigakis
    #                       unauthorized (401),
646 b016b476 Giorgos Verigakis
    #                       badRequest (400),
647 b016b476 Giorgos Verigakis
    #                       itemNotFound (404),
648 b016b476 Giorgos Verigakis
    #                       overLimit (413)
649 ce55f211 Kostas Papadimitriou
650 0c37a721 Christos Stavrakakis
    log.debug('list_addresses_by_network %s %s', server_id, network_id)
651 4b3b8688 Giorgos Verigakis
    machine = util.get_vm(server_id, request.user_uniq)
652 4b3b8688 Giorgos Verigakis
    network = util.get_network(network_id, request.user_uniq)
653 7c714455 Christos Stavrakakis
    nics = machine.nics.filter(network=network, state="ACTIVE")
654 b3a43976 Christos Stavrakakis
    addresses = attachments_to_addresses(map(nic_to_attachments, nics))
655 ce55f211 Kostas Papadimitriou
656 d8e50a39 Giorgos Verigakis
    if request.serialization == 'xml':
657 1f638a85 Christos Stavrakakis
        data = render_to_string('address.xml', {'addresses': addresses})
658 b016b476 Giorgos Verigakis
    else:
659 1f638a85 Christos Stavrakakis
        data = json.dumps({'network': addresses})
660 aa197ee4 Vangelis Koukis
661 b016b476 Giorgos Verigakis
    return HttpResponse(data, status=200)
662 d8e50a39 Giorgos Verigakis
663 55cd40be Vangelis Koukis
664 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
665 d8e50a39 Giorgos Verigakis
def list_metadata(request, server_id):
666 d8e50a39 Giorgos Verigakis
    # Normal Response Codes: 200, 203
667 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
668 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
669 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
670 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
671 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
672 ce55f211 Kostas Papadimitriou
673 0c37a721 Christos Stavrakakis
    log.debug('list_server_metadata %s', server_id)
674 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
675 7cc3c7d9 Giorgos Verigakis
    metadata = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
676 5029ff36 Christos Stavrakakis
    return util.render_metadata(request, metadata, use_values=False,
677 5029ff36 Christos Stavrakakis
                                status=200)
678 d8e50a39 Giorgos Verigakis
679 55cd40be Vangelis Koukis
680 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
681 874383a4 Christos Stavrakakis
@transaction.commit_on_success
682 d8e50a39 Giorgos Verigakis
def update_metadata(request, server_id):
683 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 201
684 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
685 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
686 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
687 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
688 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
689 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
690 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
691 ce55f211 Kostas Papadimitriou
692 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
693 bf5c82dc Christos Stavrakakis
    log.info('update_server_metadata %s %s', server_id, req)
694 e221ade2 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
695 14402edc Christos Stavrakakis
    metadata = utils.get_attribute(req, "metadata", required=True,
696 14402edc Christos Stavrakakis
                                   attr_type=dict)
697 ce55f211 Kostas Papadimitriou
698 7cc3c7d9 Giorgos Verigakis
    for key, val in metadata.items():
699 14402edc Christos Stavrakakis
        if not isinstance(key, (basestring, int)) or\
700 14402edc Christos Stavrakakis
           not isinstance(val, (basestring, int)):
701 14402edc Christos Stavrakakis
            raise faults.BadRequest("Malformed Request. Invalid metadata.")
702 7cc3c7d9 Giorgos Verigakis
        meta, created = vm.metadata.get_or_create(meta_key=key)
703 7cc3c7d9 Giorgos Verigakis
        meta.meta_value = val
704 7cc3c7d9 Giorgos Verigakis
        meta.save()
705 ce55f211 Kostas Papadimitriou
706 7cc3c7d9 Giorgos Verigakis
    vm.save()
707 7cc3c7d9 Giorgos Verigakis
    vm_meta = dict((m.meta_key, m.meta_value) for m in vm.metadata.all())
708 7cc3c7d9 Giorgos Verigakis
    return util.render_metadata(request, vm_meta, status=201)
709 d8e50a39 Giorgos Verigakis
710 55cd40be Vangelis Koukis
711 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
712 d8e50a39 Giorgos Verigakis
def get_metadata_item(request, server_id, key):
713 d8e50a39 Giorgos Verigakis
    # Normal Response Codes: 200, 203
714 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
715 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
716 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
717 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
718 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
719 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
720 ce55f211 Kostas Papadimitriou
721 0c37a721 Christos Stavrakakis
    log.debug('get_server_metadata_item %s %s', server_id, key)
722 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
723 b36f78fa Giorgos Verigakis
    meta = util.get_vm_meta(vm, key)
724 62b894c0 Giorgos Verigakis
    d = {meta.meta_key: meta.meta_value}
725 62b894c0 Giorgos Verigakis
    return util.render_meta(request, d, status=200)
726 d8e50a39 Giorgos Verigakis
727 55cd40be Vangelis Koukis
728 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='PUT', user_required=True, logger=log)
729 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
730 d8e50a39 Giorgos Verigakis
def create_metadata_item(request, server_id, key):
731 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 201
732 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
733 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
734 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
735 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
736 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
737 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
738 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
739 d8e50a39 Giorgos Verigakis
    #                       overLimit (413)
740 ce55f211 Kostas Papadimitriou
741 b3fd98ae Christos Stavrakakis
    req = utils.get_request_dict(request)
742 bf5c82dc Christos Stavrakakis
    log.info('create_server_metadata_item %s %s %s', server_id, key, req)
743 e221ade2 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
744 d8e50a39 Giorgos Verigakis
    try:
745 d8e50a39 Giorgos Verigakis
        metadict = req['meta']
746 d8e50a39 Giorgos Verigakis
        assert isinstance(metadict, dict)
747 d8e50a39 Giorgos Verigakis
        assert len(metadict) == 1
748 d8e50a39 Giorgos Verigakis
        assert key in metadict
749 d8e50a39 Giorgos Verigakis
    except (KeyError, AssertionError):
750 ccd0d474 Giorgos Verigakis
        raise faults.BadRequest("Malformed request")
751 ce55f211 Kostas Papadimitriou
752 b36f78fa Giorgos Verigakis
    meta, created = VirtualMachineMetadata.objects.get_or_create(
753 b36f78fa Giorgos Verigakis
        meta_key=key,
754 b36f78fa Giorgos Verigakis
        vm=vm)
755 ce55f211 Kostas Papadimitriou
756 d8e50a39 Giorgos Verigakis
    meta.meta_value = metadict[key]
757 d8e50a39 Giorgos Verigakis
    meta.save()
758 5509b599 Giorgos Verigakis
    vm.save()
759 62b894c0 Giorgos Verigakis
    d = {meta.meta_key: meta.meta_value}
760 62b894c0 Giorgos Verigakis
    return util.render_meta(request, d, status=201)
761 d8e50a39 Giorgos Verigakis
762 55cd40be Vangelis Koukis
763 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log)
764 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
765 d8e50a39 Giorgos Verigakis
def delete_metadata_item(request, server_id, key):
766 d8e50a39 Giorgos Verigakis
    # Normal Response Code: 204
767 d8e50a39 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
768 d8e50a39 Giorgos Verigakis
    #                       serviceUnavailable (503),
769 d8e50a39 Giorgos Verigakis
    #                       unauthorized (401),
770 d8e50a39 Giorgos Verigakis
    #                       itemNotFound (404),
771 d8e50a39 Giorgos Verigakis
    #                       badRequest (400),
772 d8e50a39 Giorgos Verigakis
    #                       buildInProgress (409),
773 d8e50a39 Giorgos Verigakis
    #                       badMediaType(415),
774 d8e50a39 Giorgos Verigakis
    #                       overLimit (413),
775 ce55f211 Kostas Papadimitriou
776 bf5c82dc Christos Stavrakakis
    log.info('delete_server_metadata_item %s %s', server_id, key)
777 e221ade2 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
778 b36f78fa Giorgos Verigakis
    meta = util.get_vm_meta(vm, key)
779 d8e50a39 Giorgos Verigakis
    meta.delete()
780 5509b599 Giorgos Verigakis
    vm.save()
781 d8e50a39 Giorgos Verigakis
    return HttpResponse(status=204)
782 c738c935 Giorgos Verigakis
783 55cd40be Vangelis Koukis
784 b3fd98ae Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
785 c738c935 Giorgos Verigakis
def server_stats(request, server_id):
786 c738c935 Giorgos Verigakis
    # Normal Response Codes: 200
787 c738c935 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
788 c738c935 Giorgos Verigakis
    #                       serviceUnavailable (503),
789 c738c935 Giorgos Verigakis
    #                       unauthorized (401),
790 c738c935 Giorgos Verigakis
    #                       badRequest (400),
791 c738c935 Giorgos Verigakis
    #                       itemNotFound (404),
792 c738c935 Giorgos Verigakis
    #                       overLimit (413)
793 ce55f211 Kostas Papadimitriou
794 0c37a721 Christos Stavrakakis
    log.debug('server_stats %s', server_id)
795 4b3b8688 Giorgos Verigakis
    vm = util.get_vm(server_id, request.user_uniq)
796 bd16bf3e Stratos Psomadakis
    secret = util.stats_encrypt(vm.backend_vm_id)
797 ce55f211 Kostas Papadimitriou
798 c738c935 Giorgos Verigakis
    stats = {
799 c738c935 Giorgos Verigakis
        'serverRef': vm.id,
800 c738c935 Giorgos Verigakis
        'refresh': settings.STATS_REFRESH_PERIOD,
801 5391d6b5 Giorgos Verigakis
        'cpuBar': settings.CPU_BAR_GRAPH_URL % secret,
802 5391d6b5 Giorgos Verigakis
        'cpuTimeSeries': settings.CPU_TIMESERIES_GRAPH_URL % secret,
803 5391d6b5 Giorgos Verigakis
        'netBar': settings.NET_BAR_GRAPH_URL % secret,
804 5391d6b5 Giorgos Verigakis
        'netTimeSeries': settings.NET_TIMESERIES_GRAPH_URL % secret}
805 ce55f211 Kostas Papadimitriou
806 c738c935 Giorgos Verigakis
    if request.serialization == 'xml':
807 c738c935 Giorgos Verigakis
        data = render_to_string('server_stats.xml', stats)
808 c738c935 Giorgos Verigakis
    else:
809 c738c935 Giorgos Verigakis
        data = json.dumps({'stats': stats})
810 c738c935 Giorgos Verigakis
811 c738c935 Giorgos Verigakis
    return HttpResponse(data, status=200)
812 41a7fae7 Christos Stavrakakis
813 41a7fae7 Christos Stavrakakis
814 41a7fae7 Christos Stavrakakis
# ACTIONS
815 41a7fae7 Christos Stavrakakis
816 41a7fae7 Christos Stavrakakis
817 41a7fae7 Christos Stavrakakis
server_actions = {}
818 41a7fae7 Christos Stavrakakis
network_actions = {}
819 41a7fae7 Christos Stavrakakis
820 41a7fae7 Christos Stavrakakis
821 41a7fae7 Christos Stavrakakis
def server_action(name):
822 41a7fae7 Christos Stavrakakis
    '''Decorator for functions implementing server actions.
823 41a7fae7 Christos Stavrakakis
    `name` is the key in the dict passed by the client.
824 41a7fae7 Christos Stavrakakis
    '''
825 41a7fae7 Christos Stavrakakis
826 41a7fae7 Christos Stavrakakis
    def decorator(func):
827 41a7fae7 Christos Stavrakakis
        server_actions[name] = func
828 41a7fae7 Christos Stavrakakis
        return func
829 41a7fae7 Christos Stavrakakis
    return decorator
830 41a7fae7 Christos Stavrakakis
831 41a7fae7 Christos Stavrakakis
832 41a7fae7 Christos Stavrakakis
def network_action(name):
833 41a7fae7 Christos Stavrakakis
    '''Decorator for functions implementing network actions.
834 41a7fae7 Christos Stavrakakis
    `name` is the key in the dict passed by the client.
835 41a7fae7 Christos Stavrakakis
    '''
836 41a7fae7 Christos Stavrakakis
837 41a7fae7 Christos Stavrakakis
    def decorator(func):
838 41a7fae7 Christos Stavrakakis
        network_actions[name] = func
839 41a7fae7 Christos Stavrakakis
        return func
840 41a7fae7 Christos Stavrakakis
    return decorator
841 41a7fae7 Christos Stavrakakis
842 41a7fae7 Christos Stavrakakis
843 41a7fae7 Christos Stavrakakis
@server_action('start')
844 41a7fae7 Christos Stavrakakis
def start(request, vm, args):
845 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
846 41a7fae7 Christos Stavrakakis
    # Error Response Codes: serviceUnavailable (503),
847 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404)
848 41a7fae7 Christos Stavrakakis
    vm = servers.start(vm)
849 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
850 41a7fae7 Christos Stavrakakis
851 41a7fae7 Christos Stavrakakis
852 41a7fae7 Christos Stavrakakis
@server_action('shutdown')
853 41a7fae7 Christos Stavrakakis
def shutdown(request, vm, args):
854 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
855 41a7fae7 Christos Stavrakakis
    # Error Response Codes: serviceUnavailable (503),
856 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404)
857 41a7fae7 Christos Stavrakakis
    vm = servers.stop(vm)
858 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
859 41a7fae7 Christos Stavrakakis
860 41a7fae7 Christos Stavrakakis
861 41a7fae7 Christos Stavrakakis
@server_action('reboot')
862 41a7fae7 Christos Stavrakakis
def reboot(request, vm, args):
863 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
864 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
865 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
866 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
867 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
868 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
869 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
870 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
871 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
872 41a7fae7 Christos Stavrakakis
873 bbae3e45 Christos Stavrakakis
    reboot_type = args.get("type", "SOFT")
874 bbae3e45 Christos Stavrakakis
    if reboot_type not in ["SOFT", "HARD"]:
875 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Invalid 'type' attribute.")
876 41a7fae7 Christos Stavrakakis
    vm = servers.reboot(vm, reboot_type=reboot_type)
877 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
878 41a7fae7 Christos Stavrakakis
879 41a7fae7 Christos Stavrakakis
880 41a7fae7 Christos Stavrakakis
@server_action('firewallProfile')
881 41a7fae7 Christos Stavrakakis
def set_firewall_profile(request, vm, args):
882 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 200
883 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
884 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
885 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
886 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
887 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
888 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
889 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
890 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
891 41a7fae7 Christos Stavrakakis
    profile = args.get("profile")
892 41a7fae7 Christos Stavrakakis
    if profile is None:
893 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Missing 'profile' attribute")
894 d0545590 Christos Stavrakakis
    nic_id = args.get("nic")
895 d0545590 Christos Stavrakakis
    if nic_id is None:
896 d0545590 Christos Stavrakakis
        raise faults.BadRequest("Missing 'nic' attribute")
897 c988fcca Christos Stavrakakis
    nic = util.get_vm_nic(vm, nic_id)
898 d0545590 Christos Stavrakakis
    servers.set_firewall_profile(vm, profile=profile, nic=nic)
899 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
900 41a7fae7 Christos Stavrakakis
901 41a7fae7 Christos Stavrakakis
902 41a7fae7 Christos Stavrakakis
@server_action('resize')
903 41a7fae7 Christos Stavrakakis
def resize(request, vm, args):
904 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
905 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
906 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
907 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
908 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
909 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
910 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
911 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
912 41a7fae7 Christos Stavrakakis
    #                       serverCapacityUnavailable (503),
913 41a7fae7 Christos Stavrakakis
    #                       overLimit (413),
914 41a7fae7 Christos Stavrakakis
    #                       resizeNotAllowed (403)
915 41a7fae7 Christos Stavrakakis
    flavorRef = args.get("flavorRef")
916 41a7fae7 Christos Stavrakakis
    if flavorRef is None:
917 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Missing 'flavorRef' attribute.")
918 41a7fae7 Christos Stavrakakis
    flavor = util.get_flavor(flavor_id=flavorRef, include_deleted=False)
919 41a7fae7 Christos Stavrakakis
    servers.resize(vm, flavor=flavor)
920 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
921 41a7fae7 Christos Stavrakakis
922 41a7fae7 Christos Stavrakakis
923 41a7fae7 Christos Stavrakakis
@server_action('console')
924 41a7fae7 Christos Stavrakakis
def get_console(request, vm, args):
925 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 200
926 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
927 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
928 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
929 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
930 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
931 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
932 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
933 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
934 41a7fae7 Christos Stavrakakis
935 41a7fae7 Christos Stavrakakis
    log.info("Get console  VM %s: %s", vm, args)
936 41a7fae7 Christos Stavrakakis
937 41a7fae7 Christos Stavrakakis
    console_type = args.get("type")
938 41a7fae7 Christos Stavrakakis
    if console_type is None:
939 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("No console 'type' specified.")
940 41a7fae7 Christos Stavrakakis
    elif console_type != "vnc":
941 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Console 'type' can only be 'vnc'.")
942 41a7fae7 Christos Stavrakakis
    console_info = servers.console(vm, console_type)
943 41a7fae7 Christos Stavrakakis
944 41a7fae7 Christos Stavrakakis
    if request.serialization == 'xml':
945 41a7fae7 Christos Stavrakakis
        mimetype = 'application/xml'
946 41a7fae7 Christos Stavrakakis
        data = render_to_string('console.xml', {'console': console_info})
947 41a7fae7 Christos Stavrakakis
    else:
948 41a7fae7 Christos Stavrakakis
        mimetype = 'application/json'
949 41a7fae7 Christos Stavrakakis
        data = json.dumps({'console': console_info})
950 41a7fae7 Christos Stavrakakis
951 41a7fae7 Christos Stavrakakis
    return HttpResponse(data, mimetype=mimetype, status=200)
952 41a7fae7 Christos Stavrakakis
953 41a7fae7 Christos Stavrakakis
954 41a7fae7 Christos Stavrakakis
@server_action('changePassword')
955 41a7fae7 Christos Stavrakakis
def change_password(request, vm, args):
956 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Changing password is not supported.')
957 41a7fae7 Christos Stavrakakis
958 41a7fae7 Christos Stavrakakis
959 41a7fae7 Christos Stavrakakis
@server_action('rebuild')
960 41a7fae7 Christos Stavrakakis
def rebuild(request, vm, args):
961 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Rebuild not supported.')
962 41a7fae7 Christos Stavrakakis
963 41a7fae7 Christos Stavrakakis
964 41a7fae7 Christos Stavrakakis
@server_action('confirmResize')
965 41a7fae7 Christos Stavrakakis
def confirm_resize(request, vm, args):
966 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Resize not supported.')
967 41a7fae7 Christos Stavrakakis
968 41a7fae7 Christos Stavrakakis
969 41a7fae7 Christos Stavrakakis
@server_action('revertResize')
970 41a7fae7 Christos Stavrakakis
def revert_resize(request, vm, args):
971 41a7fae7 Christos Stavrakakis
    raise faults.NotImplemented('Resize not supported.')
972 41a7fae7 Christos Stavrakakis
973 41a7fae7 Christos Stavrakakis
974 41a7fae7 Christos Stavrakakis
@network_action('add')
975 41a7fae7 Christos Stavrakakis
@transaction.commit_on_success
976 41a7fae7 Christos Stavrakakis
def add(request, net, args):
977 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
978 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
979 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
980 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
981 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
982 41a7fae7 Christos Stavrakakis
    #                       buildInProgress (409),
983 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
984 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
985 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
986 41a7fae7 Christos Stavrakakis
    server_id = args.get('serverRef', None)
987 41a7fae7 Christos Stavrakakis
    if not server_id:
988 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest('Malformed Request.')
989 41a7fae7 Christos Stavrakakis
990 41a7fae7 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
991 41a7fae7 Christos Stavrakakis
    servers.connect(vm, network=net)
992 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
993 41a7fae7 Christos Stavrakakis
994 41a7fae7 Christos Stavrakakis
995 41a7fae7 Christos Stavrakakis
@network_action('remove')
996 41a7fae7 Christos Stavrakakis
@transaction.commit_on_success
997 41a7fae7 Christos Stavrakakis
def remove(request, net, args):
998 41a7fae7 Christos Stavrakakis
    # Normal Response Code: 202
999 41a7fae7 Christos Stavrakakis
    # Error Response Codes: computeFault (400, 500),
1000 41a7fae7 Christos Stavrakakis
    #                       serviceUnavailable (503),
1001 41a7fae7 Christos Stavrakakis
    #                       unauthorized (401),
1002 41a7fae7 Christos Stavrakakis
    #                       badRequest (400),
1003 41a7fae7 Christos Stavrakakis
    #                       badMediaType(415),
1004 41a7fae7 Christos Stavrakakis
    #                       itemNotFound (404),
1005 41a7fae7 Christos Stavrakakis
    #                       overLimit (413)
1006 41a7fae7 Christos Stavrakakis
1007 41a7fae7 Christos Stavrakakis
    attachment = args.get("attachment")
1008 41a7fae7 Christos Stavrakakis
    if attachment is None:
1009 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Missing 'attachment' attribute.")
1010 41a7fae7 Christos Stavrakakis
    try:
1011 c988fcca Christos Stavrakakis
        nic_id = int(attachment)
1012 41a7fae7 Christos Stavrakakis
    except (ValueError, TypeError):
1013 41a7fae7 Christos Stavrakakis
        raise faults.BadRequest("Invalid 'attachment' attribute.")
1014 41a7fae7 Christos Stavrakakis
1015 c988fcca Christos Stavrakakis
    nic = util.get_nic(nic_id=nic_id)
1016 c988fcca Christos Stavrakakis
    server_id = nic.machine_id
1017 41a7fae7 Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, non_suspended=True)
1018 c988fcca Christos Stavrakakis
1019 7c714455 Christos Stavrakakis
    servers.disconnect(vm, nic)
1020 41a7fae7 Christos Stavrakakis
1021 41a7fae7 Christos Stavrakakis
    return HttpResponse(status=202)
1022 9ba6bb95 Christos Stavrakakis
1023 9ba6bb95 Christos Stavrakakis
1024 f8675683 Christos Stavrakakis
@server_action("addFloatingIp")
1025 9ba6bb95 Christos Stavrakakis
def add_floating_ip(request, vm, args):
1026 9ba6bb95 Christos Stavrakakis
    address = args.get("address")
1027 9ba6bb95 Christos Stavrakakis
    if address is None:
1028 9ba6bb95 Christos Stavrakakis
        raise faults.BadRequest("Missing 'address' attribute")
1029 9ba6bb95 Christos Stavrakakis
1030 fae6e5f0 Christos Stavrakakis
    userid = vm.userid
1031 fae6e5f0 Christos Stavrakakis
    floating_ip = util.get_floating_ip_by_address(userid, address,
1032 fae6e5f0 Christos Stavrakakis
                                                  for_update=True)
1033 fae6e5f0 Christos Stavrakakis
    servers.create_port(userid, floating_ip.network, machine=vm,
1034 fae6e5f0 Christos Stavrakakis
                        user_ipaddress=floating_ip)
1035 9ba6bb95 Christos Stavrakakis
    return HttpResponse(status=202)
1036 9ba6bb95 Christos Stavrakakis
1037 9ba6bb95 Christos Stavrakakis
1038 f8675683 Christos Stavrakakis
@server_action("removeFloatingIp")
1039 9ba6bb95 Christos Stavrakakis
def remove_floating_ip(request, vm, args):
1040 9ba6bb95 Christos Stavrakakis
    address = args.get("address")
1041 9ba6bb95 Christos Stavrakakis
    if address is None:
1042 9ba6bb95 Christos Stavrakakis
        raise faults.BadRequest("Missing 'address' attribute")
1043 fae6e5f0 Christos Stavrakakis
    floating_ip = util.get_floating_ip_by_address(vm.userid, address,
1044 fae6e5f0 Christos Stavrakakis
                                                  for_update=True)
1045 fae6e5f0 Christos Stavrakakis
    if floating_ip.nic is None:
1046 fae6e5f0 Christos Stavrakakis
        raise faults.BadRequest("Floating IP %s not attached to instance"
1047 fae6e5f0 Christos Stavrakakis
                                % address)
1048 fae6e5f0 Christos Stavrakakis
    servers.delete_port(floating_ip.nic)
1049 9ba6bb95 Christos Stavrakakis
    return HttpResponse(status=202)
1050 910d960d Christos Stavrakakis
1051 910d960d Christos Stavrakakis
1052 910d960d Christos Stavrakakis
def volume_to_attachment(volume):
1053 910d960d Christos Stavrakakis
    return {"id": volume.id,
1054 910d960d Christos Stavrakakis
            "volumeId": volume.id,
1055 910d960d Christos Stavrakakis
            "serverId": volume.machine_id,
1056 5d805533 Christos Stavrakakis
            "device": ""}  # TODO: What device to return?
1057 910d960d Christos Stavrakakis
1058 910d960d Christos Stavrakakis
1059 910d960d Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
1060 910d960d Christos Stavrakakis
def get_volumes(request, server_id):
1061 910d960d Christos Stavrakakis
    log.debug("get_volumes server_id %s", server_id)
1062 910d960d Christos Stavrakakis
    vm = util.get_vm(server_id, request.user_uniq, for_update=False)
1063 910d960d Christos Stavrakakis
1064 910d960d Christos Stavrakakis
    # TODO: Filter attachments!!
1065 910d960d Christos Stavrakakis
    volumes = vm.volumes.filter(deleted=False).order_by("id")
1066 910d960d Christos Stavrakakis
    attachments = [volume_to_attachment(v) for v in volumes]
1067 910d960d Christos Stavrakakis
1068 910d960d Christos Stavrakakis
    data = json.dumps({'volumeAttachments': attachments})
1069 910d960d Christos Stavrakakis
    return HttpResponse(data, status=200)
1070 910d960d Christos Stavrakakis
    pass
1071 910d960d Christos Stavrakakis
1072 910d960d Christos Stavrakakis
1073 910d960d Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log)
1074 910d960d Christos Stavrakakis
def get_volume_info(request, server_id, volume_id):
1075 910d960d Christos Stavrakakis
    log.debug("get_volume_info server_id %s volume_id", server_id, volume_id)
1076 910d960d Christos Stavrakakis
    user_id = request.user_uniq
1077 910d960d Christos Stavrakakis
    vm = util.get_vm(server_id, user_id)
1078 910d960d Christos Stavrakakis
    volume = get_volume(user_id, volume_id, for_update=False,
1079 910d960d Christos Stavrakakis
                        exception=faults.BadRequest)
1080 910d960d Christos Stavrakakis
    servers._check_attachment(vm, volume)
1081 910d960d Christos Stavrakakis
    attachment = volume_to_attachment(volume)
1082 910d960d Christos Stavrakakis
    data = json.dumps({'volumeAttachment': attachment})
1083 910d960d Christos Stavrakakis
    return HttpResponse(data, status=200)
1084 910d960d Christos Stavrakakis
1085 910d960d Christos Stavrakakis
1086 910d960d Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log)
1087 910d960d Christos Stavrakakis
def attach_volume(request, server_id):
1088 910d960d Christos Stavrakakis
    req = utils.get_request_dict(request)
1089 910d960d Christos Stavrakakis
    log.debug("attach_volume server_id %s request", server_id, req)
1090 910d960d Christos Stavrakakis
    user_id = request.user_uniq
1091 910d960d Christos Stavrakakis
    vm = util.get_vm(server_id, user_id, for_update=True)
1092 910d960d Christos Stavrakakis
1093 910d960d Christos Stavrakakis
    attachment_dict = api.utils.get_attribute(req, "volumeAttachment",
1094 910d960d Christos Stavrakakis
                                              required=True)
1095 910d960d Christos Stavrakakis
    # Get volume
1096 910d960d Christos Stavrakakis
    volume_id = api.utils.get_attribute(attachment_dict, "volumeId")
1097 910d960d Christos Stavrakakis
    volume = get_volume(user_id, volume_id, for_update=True,
1098 910d960d Christos Stavrakakis
                        exception=faults.BadRequest)
1099 910d960d Christos Stavrakakis
    vm = server_attachments.attach_volume(vm, volume)
1100 910d960d Christos Stavrakakis
    attachment = volume_to_attachment(volume)
1101 910d960d Christos Stavrakakis
    data = json.dumps({'volumeAttachment': attachment})
1102 910d960d Christos Stavrakakis
1103 910d960d Christos Stavrakakis
    return HttpResponse(data, status=202)
1104 910d960d Christos Stavrakakis
1105 910d960d Christos Stavrakakis
1106 910d960d Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log)
1107 910d960d Christos Stavrakakis
def detach_volume(request, server_id, volume_id):
1108 910d960d Christos Stavrakakis
    log.debug("detach_volume server_id %s volume_id", server_id, volume_id)
1109 910d960d Christos Stavrakakis
    user_id = request.user_uniq
1110 910d960d Christos Stavrakakis
    vm = util.get_vm(server_id, user_id)
1111 910d960d Christos Stavrakakis
    volume = get_volume(user_id, volume_id, for_update=True,
1112 910d960d Christos Stavrakakis
                        exception=faults.BadRequest)
1113 910d960d Christos Stavrakakis
    vm = server_attachments.detach_volume(vm, volume)
1114 910d960d Christos Stavrakakis
    # TODO: Check volume state, send job to detach volume
1115 910d960d Christos Stavrakakis
    return HttpResponse(status=202)