Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / cyclades / __init__.py @ 89a1c636

History | View | Annotate | Download (9.7 kB)

1 24851aa5 Stavros Sachtouris
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 a1c50326 Giorgos Verigakis
#
3 a1c50326 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 a1c50326 Giorgos Verigakis
# without modification, are permitted provided that the following
5 a1c50326 Giorgos Verigakis
# conditions are met:
6 a1c50326 Giorgos Verigakis
#
7 a1c50326 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 a1c50326 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 a1c50326 Giorgos Verigakis
#      disclaimer.
10 a1c50326 Giorgos Verigakis
#
11 a1c50326 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 a1c50326 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 a1c50326 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 a1c50326 Giorgos Verigakis
#      provided with the distribution.
15 a1c50326 Giorgos Verigakis
#
16 a1c50326 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a1c50326 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a1c50326 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a1c50326 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a1c50326 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a1c50326 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a1c50326 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a1c50326 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a1c50326 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a1c50326 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a1c50326 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a1c50326 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 a1c50326 Giorgos Verigakis
#
29 a1c50326 Giorgos Verigakis
# The views and conclusions contained in the software and
30 a1c50326 Giorgos Verigakis
# documentation are those of the authors and should not be
31 a1c50326 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 a1c50326 Giorgos Verigakis
# or implied, of GRNET S.A.
33 a1c50326 Giorgos Verigakis
34 55faa0bc Stavros Sachtouris
from kamaki.clients.cyclades.rest_api import CycladesRestClient
35 e864cd9e Stavros Sachtouris
from kamaki.clients.network import NetworkClient
36 0e27687b Stavros Sachtouris
from kamaki.clients.utils import path4url
37 6f2b87c1 Stavros Sachtouris
from kamaki.clients import ClientError, Waiter
38 89a1c636 Giorgos Korfiatis
import json
39 3dabe5d2 Stavros Sachtouris
40 6f2b87c1 Stavros Sachtouris
class CycladesClient(CycladesRestClient, Waiter):
41 76e7661e Stavros Sachtouris
    """Synnefo Cyclades Compute API client"""
42 2f749e6e Stavros Sachtouris
43 dbcbf446 Stavros Sachtouris
    def create_server(
44 dbcbf446 Stavros Sachtouris
            self, name, flavor_id, image_id,
45 89a1c636 Giorgos Korfiatis
            metadata=None, personality=None, networks=None, project=None):
46 dbcbf446 Stavros Sachtouris
        """Submit request to create a new server
47 dbcbf446 Stavros Sachtouris

48 dbcbf446 Stavros Sachtouris
        :param name: (str)
49 dbcbf446 Stavros Sachtouris

50 dbcbf446 Stavros Sachtouris
        :param flavor_id: integer id denoting a preset hardware configuration
51 dbcbf446 Stavros Sachtouris

52 2bd23362 Stavros Sachtouris
        :param image_id: (str) id denoting the OS image to run on virt. server
53 dbcbf446 Stavros Sachtouris

54 dbcbf446 Stavros Sachtouris
        :param metadata: (dict) vm metadata updated by os/users image metadata
55 dbcbf446 Stavros Sachtouris

56 dbcbf446 Stavros Sachtouris
        :param personality: a list of (file path, file contents) tuples,
57 2bd23362 Stavros Sachtouris
            describing files to be injected into virtual server upon creation
58 dbcbf446 Stavros Sachtouris

59 264a13f7 Stavros Sachtouris
        :param networks: (list of dicts) Networks to connect to, list this:
60 264a13f7 Stavros Sachtouris
            "networks": [
61 de329b4c Stavros Sachtouris
            {"uuid": <network_uuid>},
62 de329b4c Stavros Sachtouris
            {"uuid": <network_uuid>, "fixed_ip": address},
63 de329b4c Stavros Sachtouris
            {"port": <port_id>}, ...]
64 eb647cfe Stavros Sachtouris
            ATTENTION: Empty list is different to None. None means ' do not
65 eb647cfe Stavros Sachtouris
            mention it', empty list means 'automatically get an ip'
66 264a13f7 Stavros Sachtouris

67 89a1c636 Giorgos Korfiatis
        :param project: the project where to assign the server
68 89a1c636 Giorgos Korfiatis

69 2bd23362 Stavros Sachtouris
        :returns: a dict with the new virtual server details
70 dbcbf446 Stavros Sachtouris

71 dbcbf446 Stavros Sachtouris
        :raises ClientError: wraps request errors
72 dbcbf446 Stavros Sachtouris
        """
73 dbcbf446 Stavros Sachtouris
        image = self.get_image_details(image_id)
74 dbcbf446 Stavros Sachtouris
        metadata = metadata or dict()
75 dbcbf446 Stavros Sachtouris
        for key in ('os', 'users'):
76 dbcbf446 Stavros Sachtouris
            try:
77 dbcbf446 Stavros Sachtouris
                metadata[key] = image['metadata'][key]
78 dbcbf446 Stavros Sachtouris
            except KeyError:
79 dbcbf446 Stavros Sachtouris
                pass
80 dbcbf446 Stavros Sachtouris
81 65a8b1da Stavros Sachtouris
        return super(CycladesClient, self).create_server(
82 65a8b1da Stavros Sachtouris
            name, flavor_id, image_id,
83 89a1c636 Giorgos Korfiatis
            metadata=metadata, personality=personality, networks=networks,
84 89a1c636 Giorgos Korfiatis
            project=project)
85 dbcbf446 Stavros Sachtouris
86 a1c50326 Giorgos Verigakis
    def start_server(self, server_id):
87 f5eac743 Stavros Sachtouris
        """Submit a startup request
88 f5eac743 Stavros Sachtouris

89 f5eac743 Stavros Sachtouris
        :param server_id: integer (str or int)
90 cd295a1d Stavros Sachtouris

91 cd295a1d Stavros Sachtouris
        :returns: (dict) response headers
92 f5eac743 Stavros Sachtouris
        """
93 6a0b1658 Giorgos Verigakis
        req = {'start': {}}
94 ef2e6c9f Stavros Sachtouris
        r = self.servers_action_post(server_id, json_data=req, success=202)
95 cd295a1d Stavros Sachtouris
        return r.headers
96 6f1ec797 Stavros Sachtouris
97 a1c50326 Giorgos Verigakis
    def shutdown_server(self, server_id):
98 f5eac743 Stavros Sachtouris
        """Submit a shutdown request
99 f5eac743 Stavros Sachtouris

100 f5eac743 Stavros Sachtouris
        :param server_id: integer (str or int)
101 cd295a1d Stavros Sachtouris

102 cd295a1d Stavros Sachtouris
        :returns: (dict) response headers
103 f5eac743 Stavros Sachtouris
        """
104 6a0b1658 Giorgos Verigakis
        req = {'shutdown': {}}
105 ef2e6c9f Stavros Sachtouris
        r = self.servers_action_post(server_id, json_data=req, success=202)
106 cd295a1d Stavros Sachtouris
        return r.headers
107 3dabe5d2 Stavros Sachtouris
108 a1c50326 Giorgos Verigakis
    def get_server_console(self, server_id):
109 f5eac743 Stavros Sachtouris
        """
110 f5eac743 Stavros Sachtouris
        :param server_id: integer (str or int)
111 f5eac743 Stavros Sachtouris

112 2bd23362 Stavros Sachtouris
        :returns: (dict) info to set a VNC connection to virtual server
113 f5eac743 Stavros Sachtouris
        """
114 6a0b1658 Giorgos Verigakis
        req = {'console': {'type': 'vnc'}}
115 ef2e6c9f Stavros Sachtouris
        r = self.servers_action_post(server_id, json_data=req, success=200)
116 6a0b1658 Giorgos Verigakis
        return r.json['console']
117 2d67ecbb Stavros Sachtouris
118 89a1c636 Giorgos Korfiatis
    def reassign_server(self, server_id, project):
119 89a1c636 Giorgos Korfiatis
        req = {'reassign': {'project': project}}
120 89a1c636 Giorgos Korfiatis
        r = self.servers_action_post(server_id, json_data=req, success=200)
121 89a1c636 Giorgos Korfiatis
        return r.headers
122 89a1c636 Giorgos Korfiatis
123 a1c50326 Giorgos Verigakis
    def get_server_stats(self, server_id):
124 f5eac743 Stavros Sachtouris
        """
125 f5eac743 Stavros Sachtouris
        :param server_id: integer (str or int)
126 f5eac743 Stavros Sachtouris

127 f5eac743 Stavros Sachtouris
        :returns: (dict) auto-generated graphs of statistics (urls)
128 f5eac743 Stavros Sachtouris
        """
129 e51c7d5b Stavros Sachtouris
        r = self.servers_stats_get(server_id)
130 6a0b1658 Giorgos Verigakis
        return r.json['stats']
131 3dabe5d2 Stavros Sachtouris
132 b45834eb Stavros Sachtouris
    def get_server_diagnostics(self, server_id):
133 b45834eb Stavros Sachtouris
        """
134 b45834eb Stavros Sachtouris
        :param server_id: integer (str or int)
135 b45834eb Stavros Sachtouris

136 b45834eb Stavros Sachtouris
        :returns: (list)
137 b45834eb Stavros Sachtouris
        """
138 b45834eb Stavros Sachtouris
        r = self.servers_diagnostics_get(server_id)
139 b45834eb Stavros Sachtouris
        return r.json
140 b45834eb Stavros Sachtouris
141 7b2e4bf1 Stavros Sachtouris
    def wait_server(
142 7b2e4bf1 Stavros Sachtouris
            self, server_id,
143 7b2e4bf1 Stavros Sachtouris
            current_status='BUILD',
144 7b2e4bf1 Stavros Sachtouris
            delay=1, max_wait=100, wait_cb=None):
145 7b2e4bf1 Stavros Sachtouris
        """Wait for server while its status is current_status
146 7b2e4bf1 Stavros Sachtouris

147 7b2e4bf1 Stavros Sachtouris
        :param server_id: integer (str or int)
148 7b2e4bf1 Stavros Sachtouris

149 7b2e4bf1 Stavros Sachtouris
        :param current_status: (str) BUILD|ACTIVE|STOPPED|DELETED|REBOOT
150 7b2e4bf1 Stavros Sachtouris

151 7b2e4bf1 Stavros Sachtouris
        :param delay: time interval between retries
152 7b2e4bf1 Stavros Sachtouris

153 c788a761 Stavros Sachtouris
        :max_wait: (int) timeout in secconds
154 c788a761 Stavros Sachtouris

155 7b2e4bf1 Stavros Sachtouris
        :param wait_cb: if set a progressbar is used to show progress
156 7b2e4bf1 Stavros Sachtouris

157 7b2e4bf1 Stavros Sachtouris
        :returns: (str) the new mode if succesfull, (bool) False if timed out
158 7b2e4bf1 Stavros Sachtouris
        """
159 7b2e4bf1 Stavros Sachtouris
160 7b2e4bf1 Stavros Sachtouris
        def get_status(self, server_id):
161 7b2e4bf1 Stavros Sachtouris
            r = self.get_server_details(server_id)
162 7b2e4bf1 Stavros Sachtouris
            return r['status'], (r.get('progress', None) if (
163 7b2e4bf1 Stavros Sachtouris
                            current_status in ('BUILD', )) else None)
164 7b2e4bf1 Stavros Sachtouris
165 7b2e4bf1 Stavros Sachtouris
        return self._wait(
166 7b2e4bf1 Stavros Sachtouris
            server_id, current_status, get_status, delay, max_wait, wait_cb)
167 7b2e4bf1 Stavros Sachtouris
168 e864cd9e Stavros Sachtouris
169 eb647cfe Stavros Sachtouris
class CycladesNetworkClient(NetworkClient):
170 e864cd9e Stavros Sachtouris
    """Cyclades Network API extentions"""
171 e864cd9e Stavros Sachtouris
172 e864cd9e Stavros Sachtouris
    network_types = (
173 e864cd9e Stavros Sachtouris
        'CUSTOM', 'MAC_FILTERED', 'IP_LESS_ROUTED', 'PHYSICAL_VLAN')
174 e864cd9e Stavros Sachtouris
175 0e27687b Stavros Sachtouris
    def list_networks(self, detail=None):
176 0e27687b Stavros Sachtouris
        path = path4url('networks', 'detail' if detail else '')
177 0e27687b Stavros Sachtouris
        r = self.get(path, success=200)
178 0e27687b Stavros Sachtouris
        return r.json['networks']
179 0e27687b Stavros Sachtouris
180 89a1c636 Giorgos Korfiatis
    def create_network(self, type, name=None, shared=None, project=None):
181 e864cd9e Stavros Sachtouris
        req = dict(network=dict(type=type, admin_state_up=True))
182 e864cd9e Stavros Sachtouris
        if name:
183 e864cd9e Stavros Sachtouris
            req['network']['name'] = name
184 e864cd9e Stavros Sachtouris
        if shared not in (None, ):
185 e864cd9e Stavros Sachtouris
            req['network']['shared'] = bool(shared)
186 89a1c636 Giorgos Korfiatis
        if project is not None:
187 89a1c636 Giorgos Korfiatis
            req['network']['project'] = project
188 e864cd9e Stavros Sachtouris
        r = self.networks_post(json_data=req, success=201)
189 e864cd9e Stavros Sachtouris
        return r.json['network']
190 ccdd1b82 Stavros Sachtouris
191 89a1c636 Giorgos Korfiatis
    def networks_action_post(
192 89a1c636 Giorgos Korfiatis
            self, network_id='', json_data=None, success=202, **kwargs):
193 89a1c636 Giorgos Korfiatis
        """POST base_url/networks/<network_id>/action
194 89a1c636 Giorgos Korfiatis

195 89a1c636 Giorgos Korfiatis
        :returns: request response
196 89a1c636 Giorgos Korfiatis
        """
197 89a1c636 Giorgos Korfiatis
        if json_data:
198 89a1c636 Giorgos Korfiatis
            json_data = json.dumps(json_data)
199 89a1c636 Giorgos Korfiatis
            self.set_header('Content-Type', 'application/json')
200 89a1c636 Giorgos Korfiatis
            self.set_header('Content-Length', len(json_data))
201 89a1c636 Giorgos Korfiatis
        path = path4url('networks', network_id, 'action')
202 89a1c636 Giorgos Korfiatis
        return self.post(path, data=json_data, success=success, **kwargs)
203 89a1c636 Giorgos Korfiatis
204 89a1c636 Giorgos Korfiatis
    def reassign_network(self, network_id, project):
205 89a1c636 Giorgos Korfiatis
        req = {'reassign': {'project': project}}
206 89a1c636 Giorgos Korfiatis
        r = self.networks_action_post(network_id, json_data=req, success=200)
207 89a1c636 Giorgos Korfiatis
        return r.headers
208 89a1c636 Giorgos Korfiatis
209 f3740b99 Stavros Sachtouris
    def list_ports(self, detail=None):
210 f3740b99 Stavros Sachtouris
        path = path4url('ports', 'detail' if detail else '')
211 f3740b99 Stavros Sachtouris
        r = self.get(path, success=200)
212 f3740b99 Stavros Sachtouris
        return r.json['ports']
213 f3740b99 Stavros Sachtouris
214 737995ed Stavros Sachtouris
    def create_port(
215 eb647cfe Stavros Sachtouris
            self, network_id,
216 eb647cfe Stavros Sachtouris
            device_id=None, security_groups=None, name=None, fixed_ips=None):
217 b82c93a5 Stavros Sachtouris
        """
218 b82c93a5 Stavros Sachtouris
        :param fixed_ips: (list of dicts) [{"ip_address": IPv4}, ...]
219 b82c93a5 Stavros Sachtouris
        """
220 eb647cfe Stavros Sachtouris
        port = dict(network_id=network_id)
221 eb647cfe Stavros Sachtouris
        if device_id:
222 eb647cfe Stavros Sachtouris
            port['device_id'] = device_id
223 ccdd1b82 Stavros Sachtouris
        if security_groups:
224 ccdd1b82 Stavros Sachtouris
            port['security_groups'] = security_groups
225 737995ed Stavros Sachtouris
        if name:
226 737995ed Stavros Sachtouris
            port['name'] = name
227 56d84a4e Stavros Sachtouris
        if fixed_ips:
228 b82c93a5 Stavros Sachtouris
            for fixed_ip in fixed_ips or []:
229 b82c93a5 Stavros Sachtouris
                if not 'ip_address' in fixed_ip:
230 7a3c66e1 Stavros Sachtouris
                    raise ValueError('Invalid fixed_ip [%s]' % fixed_ip)
231 b7d79306 Stavros Sachtouris
            port['fixed_ips'] = fixed_ips
232 e8ba3e9f Stavros Sachtouris
        r = self.ports_post(json_data=dict(port=port), success=201)
233 ccdd1b82 Stavros Sachtouris
        return r.json['port']
234 6f2b87c1 Stavros Sachtouris
235 89a1c636 Giorgos Korfiatis
    def create_floatingip(self, floating_network_id, floating_ip_address='',
236 89a1c636 Giorgos Korfiatis
                          project=None):
237 89a1c636 Giorgos Korfiatis
        args = {"project": project}
238 eb647cfe Stavros Sachtouris
        return super(CycladesNetworkClient, self).create_floatingip(
239 89a1c636 Giorgos Korfiatis
            floating_network_id, floating_ip_address=floating_ip_address,
240 89a1c636 Giorgos Korfiatis
            args=args)
241 6f2b87c1 Stavros Sachtouris
242 eb647cfe Stavros Sachtouris
    def update_floatingip(self, floating_network_id, floating_ip_address=''):
243 eb647cfe Stavros Sachtouris
        """To nullify something optional, use None"""
244 eb647cfe Stavros Sachtouris
        return super(CycladesNetworkClient, self).update_floatingip(
245 eb647cfe Stavros Sachtouris
            floating_network_id, floating_ip_address=floating_ip_address)
246 89a1c636 Giorgos Korfiatis
247 89a1c636 Giorgos Korfiatis
    def floating_ip_action_post(
248 89a1c636 Giorgos Korfiatis
            self, fip_id, json_data=None, success=202, **kwargs):
249 89a1c636 Giorgos Korfiatis
        """POST base_url/floatingips/<fip_id>/action
250 89a1c636 Giorgos Korfiatis

251 89a1c636 Giorgos Korfiatis
        :returns: request response
252 89a1c636 Giorgos Korfiatis
        """
253 89a1c636 Giorgos Korfiatis
        if json_data:
254 89a1c636 Giorgos Korfiatis
            json_data = json.dumps(json_data)
255 89a1c636 Giorgos Korfiatis
            self.set_header('Content-Type', 'application/json')
256 89a1c636 Giorgos Korfiatis
            self.set_header('Content-Length', len(json_data))
257 89a1c636 Giorgos Korfiatis
        path = path4url('floatingips', fip_id, 'action')
258 89a1c636 Giorgos Korfiatis
        return self.post(path, data=json_data, success=success, **kwargs)
259 89a1c636 Giorgos Korfiatis
260 89a1c636 Giorgos Korfiatis
    def reassign_floating_ip(self, floating_network_id, project):
261 89a1c636 Giorgos Korfiatis
        req = {'reassign': {'project': project}}
262 89a1c636 Giorgos Korfiatis
        r = self.floating_ip_action_post(floating_network_id, json_data=req)