compute.py 8.25 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# Copyright (C) 2016 GRNET S.A.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
from base64 import b64encode
16
import webob.exc
17 18
from soi.utils import empty_list_200
from os.path import join
Kostas Vogias's avatar
Kostas Vogias committed
19
from soi.utils import check_activation
20

Kostas Vogias's avatar
Kostas Vogias committed
21
@check_activation
22 23 24 25 26 27 28 29
def snf_index(cls, req):
    """Synnefo: list servers"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'servers_get'
    response = req.get_response(cls.app)
    return cls.get_from_response(response, "servers", [])


Kostas Vogias's avatar
Kostas Vogias committed
30
@check_activation
31 32 33 34 35 36 37 38 39
def snf_get_flavors(cls, req):
    """Synnefo: list flavors"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'flavors_get'
    req.environ['kwargs'] = {'detail': True}
    response = req.get_response(cls.app)
    return cls.get_from_response(response, 'flavors', [])


Kostas Vogias's avatar
Kostas Vogias committed
40
@check_activation
41 42 43 44 45 46 47 48 49
def snf_get_images(cls, req):
    """Synnefo: list images"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'images_get'
    req.environ['kwargs'] = {'detail': True}
    response = req.get_response(cls.app)
    return cls.get_from_response(response, 'images', [])


50 51 52 53 54 55 56 57 58 59 60 61
def _openstackify_addresses(addresses, attachments):
    """Adjust server-nics to os-interface format
    This will affect addresses, not attachments, but attachments are used as
    input information
    """
    for att in attachments:
        net_id, mac_addr = att['network_id'], att['mac_address']
        for addr in addresses[net_id]:
            addr.setdefault('net_id', net_id)
            addr.setdefault('OS-EXT-IPS-MAC:mac_addr', mac_addr)


Kostas Vogias's avatar
Kostas Vogias committed
62
@check_activation
63 64 65 66 67 68 69 70 71 72 73
def snf_get_server(cls, req, server_id):
    """Synnefo: server info <server_id>"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'servers_get'
    req.environ['kwargs'] = {'server_id': server_id}
    response = req.get_response(cls.app)
    r = cls.get_from_response(response, "server", {})
    _openstackify_addresses(r['addresses'], r['attachments'])
    return r


Kostas Vogias's avatar
Kostas Vogias committed
74
@check_activation
75 76 77 78 79 80 81 82 83
def snf_get_flavor(cls, req, flavor_id):
    """Synnefo: flavor info <flavor_id>"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'flavors_get'
    req.environ['kwargs'] = {'flavor_id': flavor_id}
    response = req.get_response(cls.app)
    return cls.get_from_response(response, "flavor", {})


Kostas Vogias's avatar
Kostas Vogias committed
84
@check_activation
85 86 87 88 89 90 91 92 93
def snf_get_image(cls, req, image_id):
    """Synnefo: flavor info <image_id>"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'images_get'
    req.environ['kwargs'] = {'image_id': image_id}
    response = req.get_response(cls.app)
    return cls.get_from_response(response, "image", {})


Kostas Vogias's avatar
Kostas Vogias committed
94
@check_activation
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
def snf_get_server_volumes_link(cls, req, server_id):
    """Synnefo: server attachments <server_id>"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'volume_attachment_get'
    req.environ['kwargs'] = {'server_id': server_id}
    response = req.get_response(cls.app)
    return cls.get_from_response(response, "volumeAttachments", [])


def _openstackify_net_attachments(attachments):
    """OpenStack uses different keys for the same fields"""
    for a in attachments:
        a['net_id'] = a['network_id']
        a['mac_addr'] = a['mac_address']
        a['port_id'] = a['id']
        if a.get('OS-EXT-IPS:type', '') in ('fixed', ):
            fixed_ips, ipv4, ipv6 = {}, a.get('ipv4'), a.get('ipv6')
            if ipv4 or ipv6:
                fixed_ips['ip_address'] = ipv6 or ipv4
            a['fixed_ips'] = fixed_ips


Kostas Vogias's avatar
Kostas Vogias committed
117
@check_activation
118 119 120 121 122 123 124 125 126 127 128
def snf_get_server_net_attachments(cls, req, compute_id):
    """Adjust server-nics to os-interface format"""
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'servers_ips_get'
    req.environ['kwargs'] = {'server_id': compute_id}
    response = req.get_response(cls.app)
    r = cls.get_from_response(response, 'attachments', [])
    _openstackify_net_attachments(r)
    return r


129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
def _get_personality(image, public_key):
    """Resolve superuser from VM, prepare public_key injection
    :returns: {contents: public_key, path: , owner: , group: , mode: }
    """
    pkey = b64encode(public_key)
    personality = [{
        'contents': pkey,
        'path': '/var/lib/cloud/seed/nocloud-net/meta-data'
    }]
    try:
        users = image['metadata']['users']
    except KeyError:
        return personality

    for user in users.split():
        prefix = '/' if user == 'root' else '/home'
        path = join(prefix, user, '.ssh', 'authorized_keys')
        personality.append(dict(
            contents=pkey, path=path, owner=user, group=user, mode=0600))
    return personality


Kostas Vogias's avatar
Kostas Vogias committed
151
@check_activation
152 153
def snf_create_server(cls, req, name, image, flavor, **kwargs):
    """Synnefo: create a new VM"""
154 155
    project = req.environ.get('HTTP_X_PROJECT_ID', None)
    body = dict(name=name, imageRef=image, flavorRef=flavor, project=project)
156

Stavros Sachtouris's avatar
Stavros Sachtouris committed
157 158 159 160 161 162 163 164 165 166 167
    personality, user_data = [], kwargs.pop('user_data', None)
    if user_data:
        personality.append({
            'contents': b64encode(user_data),
            'path': '/var/lib/cloud/seed/nocloud-net/user-data'})

    key_name = kwargs.pop('key_name', None)
    if key_name:
        public_key = req.environ.get('soi:public_keys', {}).get(key_name)
        if public_key:
            image_info = snf_get_image(cls, req, image)
168
            personality += _get_personality(image_info, public_key)
Stavros Sachtouris's avatar
Stavros Sachtouris committed
169 170

    if personality:
171 172
        body['personality'] = personality

173
    body.update(kwargs)
174 175 176 177 178 179

    if not body.get('networks', None):
        body.pop('networks', None)
    if not body.get('block_device_mapping_v2', None):
        body.pop('block_device_mapping_v2', None)

180
    req.environ['kwargs'] = dict(json_data=dict(server=body))
181 182
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'servers_post'
183 184 185 186

    response = req.get_response(cls.app)
    r = cls.get_from_response(response, 'server', {})
    _openstackify_addresses(r['addresses'], r['attachments'])
187

188 189 190
    return r


Kostas Vogias's avatar
Kostas Vogias committed
191
@check_activation
192
def snf_delete_server(cls, req, server_id):
193
    """Synnefo: delete server"""
194 195 196 197 198
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'servers_delete'
    req.environ['kwargs'] = {'server_id': server_id}
    req.get_response(cls.app)

199

Kostas Vogias's avatar
Kostas Vogias committed
200
@check_activation
201
def snf_run_action(cls, req, action, server_id):
202
    """Synnefo: server actions"""
203
    try:
204 205 206 207 208
        json_data = {
            'start': {'start': {}},
            'stop': {'shutdown': {}},
            'restart': {'reboot': {'type': 'SOFT'}}
        }[action]
209 210 211 212
    except KeyError:
        raise webob.exc.HTTPNotImplemented(
            explanation='Action {0} not supported'.format(action))

213 214 215 216 217
    req.environ['service_type'] = 'compute'
    req.environ['method_name'] = 'servers_action_post'
    req.environ['kwargs'] = {'server_id': server_id, 'json_data': json_data}
    req.get_response(cls.app)

218

Kostas Vogias's avatar
Kostas Vogias committed
219
@check_activation
220 221 222 223 224 225 226
def keypair_register(cls, req, name, public_key):
    """Put public key in req.environ['public_keys'], with name as key"""
    public_keys = req.environ.get('soi:public_keys', {})
    public_keys[name] = public_key
    req.environ['soi:public_keys'] = public_keys


227 228
function_map = {
    'index': snf_index,
229
    'get_server': snf_get_server,
230
    'get_flavors': snf_get_flavors,
231
    'get_flavor': snf_get_flavor,
232
    'get_images': snf_get_images,
233 234 235
    'get_image': snf_get_image,
    'get_server_volumes_link': snf_get_server_volumes_link,
    '_get_ports': snf_get_server_net_attachments,
236 237
    'delete': snf_delete_server,
    'create_server': snf_create_server,
238
    'run_action': snf_run_action,
239 240 241 242
    'keypair_create': keypair_register,
    # TODO: Implement these when you do networks
    'get_floating_ips': empty_list_200,  # Helps in server delete
    'get_floating_ip_pools': empty_list_200,
243
}