compute.py 11.4 KB
Newer Older
1
# Copyright (C) 2012-2013 GRNET S.A.
2
#
3 4 5 6
# 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.
7
#
8 9 10 11
# 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.
12
#
13 14
# 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 16


17
from snfOCCI.config import SERVER_CONFIG
18
from snfOCCI.extensions import snf_addons
19

20
from kamaki.clients import ClientError
21

22 23 24 25 26 27
from occi.backend import ActionBackend, KindBackend, MixinBackend
from occi.core_model import Mixin
from occi.extensions import infrastructure
from occi.exceptions import HTTPError
from base64 import b64encode, b64decode
import json, yaml
28

29 30
# Compute Backend for snf-occi-server

31 32 33

class MyBackend(KindBackend, ActionBackend):

John Giannelos's avatar
John Giannelos committed
34
    # Updating and Replacing compute instances not supported by Cyclades
35

36
    def update(self, old, new, extras):
John Giannelos's avatar
John Giannelos committed
37
        raise HTTPError(501, "Update is currently no applicable")
38 39

    def replace(self, old, new, extras):
John Giannelos's avatar
John Giannelos committed
40
        raise HTTPError(501, "Replace is currently no applicable")
41 42


43
class SNFBackend(MixinBackend, ActionBackend):
44

45
    pass
46 47 48 49 50 51 52 53


class ComputeBackend(MyBackend):
    '''
    Backend for Cyclades/Openstack compute instances
    '''

    def create(self, entity, extras):
54
        # Creating new compute instance
55 56
        try:

57 58
            snf_network = extras['snf_network']
            snf = extras['client']
59 60

            for mixin in entity.mixins:
61 62 63 64 65 66 67
                print mixin
                if 'occi.core.id' in mixin.attributes:
                    if mixin.related[0].term == 'os_tpl':
                        image_id = mixin.attributes['occi.core.id']
                    elif mixin.related[0].term == 'resource_tpl':
                        flavor = mixin
                        flavor_id = mixin.attributes['occi.core.id']
68

69 70

            vm_name = entity.attributes['occi.core.title']
71

72 73 74 75
            user_data = None
            user_pub_key = None
            meta_json = None
            personality = []
76

77 78
            if entity.attributes.has_key('org.openstack.compute.user_data'):
                            user_data = b64decode(entity.attributes['org.openstack.compute.user_data'])
79 80 81
                            # user_data = entity.attributes['org.openstack.compute.user_data']


82 83
            if entity.attributes.has_key('org.openstack.credentials.publickey.data'):
                            user_pub_key = entity.attributes['org.openstack.credentials.publickey.data']
84

85 86 87 88 89 90 91
           # Implementation for the meta.json file to use the respective NoCloud cloudinit driver
           # if user_data and user_pub_key:
           #     meta_json = json.dumps({'dsmode':'net','public-keys':user_pub_key,'user-data': user_data}, sort_keys=True,indent=4, separators=(',', ': ') )
           # elif user_data:
            #    meta_json = json.dumps({'dsmode':'net','user-data': user_data}, sort_keys=True,indent=4, separators=(',', ': ') )
           # elif user_pub_key:
            #    meta_json = json.dumps({'dsmode':'net','public-keys':user_pub_key}, sort_keys=True,indent=4, separators=(',', ': ') )
92

93 94 95 96 97 98 99
          #  if meta_json:
           #     print "!!!!!!!!!!!!!!!!!!!!!!!!!!!! CONTEXTUALIZATION!!!!!!!!!!!!!!!!!!!!!!"
           #     personality.append({'contents':b64encode(meta_json),
            #                            'path':' /var/lib/cloud/seed/config_drive/meta.js'})
            #    info = snf.create_server(vm_name, flavor_id, image_id,personality=personality)
           # else:
            #    info = snf.create_server(vm_name, flavor_id, image_id)
100

101 102 103 104 105 106 107 108
            if user_data:
                #userDataDict = dict([('user-data', user_data)])
                #userData = yaml.dump(userDataDict)
                userData = user_data
            if user_pub_key:
                pub_keyDict = dict([('public-keys',user_pub_key)])
                pub_key = yaml.dump(pub_keyDict)

109
        # kwargs = dict(project_id='6d9ec935-fcd4-4ae1-a3a0-10e612c4f867')
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
            kwargs = dict(project_id=extras.get('snf_project', None))

            if user_data and user_pub_key:
                print "!!!!!!!!!!!!!!!!!!!!!!!!!!!! CONTEXTUALIZATION - USER DATA & PUBLIC KEY!!!!!!!!!!!!!!!!!!!!!!"
                personality.append({'contents':b64encode(userData),
                                        'path':'/var/lib/cloud/seed/nocloud-net/user-data'})
                personality.append({'contents':b64encode(pub_key),
                                        'path':'/var/lib/cloud/seed/nocloud-net/meta-data'})
                info = snf.create_server(vm_name, flavor_id, image_id,personality=personality, **kwargs)
            elif user_data:
                print "!!!!!!!!!!!!!!!!!!!!!!!!!!!! CONTEXTUALIZATION - USER DATA!!!!!!!!!!!!!!!!!!!!!!"
                personality.append({'contents':b64encode(userData),
                                        'path':'/var/lib/cloud/seed/nocloud-net/user-data'})
                info = snf.create_server(vm_name, flavor_id, image_id,personality=personality, **kwargs)
            elif user_pub_key:
                print "!!!!!!!!!!!!!!!!!!!!!!!!!!!! CONTEXTUALIZATION - PUBLIC KEY!!!!!!!!!!!!!!!!!!!!!!"
                personality.append({'contents':b64encode(pub_key),
                                        'path':'/var/lib/cloud/seed/nocloud-net/meta-data'})
                for retries in range(2):
                    try:
                        info = snf.create_server(vm_name, flavor_id, image_id,personality=personality, **kwargs)
                    except ClientError as ce:
                        if ce.status in (409, ):
                            print 'ce, create an IP and retry'.format(ce=ce)
                            snf_network.create_floatingip(**kwargs)
                        else:
                            raise ce
            else:
138
                print 'Create a server with some attributes...'
139 140 141 142 143 144 145 146 147 148
                for retries in range(2):
                    try:
                        info = snf.create_server(vm_name, flavor_id, image_id, **kwargs)
                        break
                    except ClientError as ce:
                        if ce.status in (409, ):
                            print '{ce}, create an IP and retry'.format(ce=ce)
                            snf_network.create_floatingip(**kwargs)
                        else:
                            raise ce
149

150 151 152 153 154
            entity.attributes['occi.compute.state'] = 'inactive'
            entity.attributes['occi.core.id'] = str(info['id'])
            entity.attributes['occi.compute.architecture'] = SERVER_CONFIG['compute_arch']
            entity.attributes['occi.compute.cores'] = flavor.attributes['occi.compute.cores']
            entity.attributes['occi.compute.memory'] = flavor.attributes['occi.compute.memory']
155 156 157 158 159

            entity.actions = [
                infrastructure.STOP,
                infrastructure.SUSPEND,
                infrastructure.RESTART]
160 161 162 163 164 165 166 167 168 169 170

            # entity.attributes['occi.compute.hostname'] = SERVER_CONFIG['hostname'] % {'id':info['id']}
            info['adminPass']= ""
            print info
            networkIDs = info['addresses'].keys()
                #resource.attributes['occi.compute.hostname'] = SERVER_CONFIG['hostname'] % {'id':int(key)}
            if len(networkIDs)>0:    
                entity.attributes['occi.compute.hostname'] =  str(info['addresses'][networkIDs[0]][0]['addr'])
            else:
                entity.attributes['occi.compute.hostname'] = ""
               
171 172
        except (UnboundLocalError, KeyError) as e:
            raise HTTPError(406, 'Missing details about compute instance')
173

174
            
175 176 177

    def retrieve(self, entity, extras):
        
John Giannelos's avatar
John Giannelos committed
178
        #Triggering cyclades to retrieve up to date information
179

180
        snf = extras['snf']
181 182 183 184 185 186 187

        vm_id = int(entity.attributes['occi.core.id'])
        vm_info = snf.get_server_details(vm_id)
        vm_state = vm_info['status']
        
        status_dict = {'ACTIVE' : 'active',
                       'STOPPED' : 'inactive',
188
                       'REBOOT' : 'inactive',
189 190 191
                       'ERROR' : 'inactive',
                       'BUILD' : 'inactive',
                       'DELETED' : 'inactive',
192
                       'UNKNOWN' : 'inactive'
193 194 195
                       }
        
        entity.attributes['occi.compute.state'] = status_dict[vm_state]
John Giannelos's avatar
John Giannelos committed
196 197 198
                
        if vm_state == 'ERROR':
            raise HTTPError(500, 'ERROR building the compute instance')
199

John Giannelos's avatar
John Giannelos committed
200 201
        else:
            if entity.attributes['occi.compute.state'] == 'inactive':
202
                entity.actions = [infrastructure.START]
John Giannelos's avatar
John Giannelos committed
203
            if entity.attributes['occi.compute.state'] == 'active': 
204
                entity.actions = [infrastructure.STOP, infrastructure.SUSPEND, infrastructure.RESTART]
205 206 207 208


    def delete(self, entity, extras):

John Giannelos's avatar
John Giannelos committed
209
        #Deleting compute instance
210
        snf = extras['snf']
211 212
        vm_id = int(entity.attributes['occi.core.id'])
        snf.delete_server(vm_id)
213
        print "Deleting VM" + str(vm_id)
214 215


216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    def get_vm_actions(self, entity ,vm_state):
        
        actions = []
        
        status_dict = {'ACTIVE' : 'active',
                       'STOPPED' : 'inactive',
                       'REBOOT' : 'inactive',
                       'ERROR' : 'inactive',
                       'BUILD' : 'inactive',
                       'DELETED' : 'inactive',
                       'UNKNOWN' : 'inactive'
                       }

        if vm_state in status_dict:
            
            entity.attributes['occi.compute.state'] = status_dict[vm_state]
            if vm_state == 'ACTIVE':
                actions.append(infrastructure.STOP)
                actions.append(infrastructure.RESTART)
            elif vm_state in ('STOPPED'):
                actions.append(infrastructure.START)
                
            return actions
        else:
            raise HTTPError(500, 'Undefined status of the VM')

    def action(self, entity, action, attributes, extras):
243

John Giannelos's avatar
John Giannelos committed
244 245
        #Triggering action to compute instances

246 247
        client = extras['client']
        snf = extras['snf']
248 249

        vm_id = int(entity.attributes['occi.core.id'])
John Giannelos's avatar
John Giannelos committed
250 251
        vm_info = snf.get_server_details(vm_id)
        vm_state = vm_info['status']
252 253 254 255
        
        # Define the allowed actions depending on the state of the VM
        entity.actions = self.get_vm_actions(entity,vm_state)
        
256

John Giannelos's avatar
John Giannelos committed
257 258
        if vm_state == 'ERROR':
            raise HTTPError(500, 'ERROR building the compute instance')
259

John Giannelos's avatar
John Giannelos committed
260 261
        else:
            if action not in entity.actions:
262
                raise AttributeError("This action is currently no applicable in the current status of the VM (CURRENT_STATE = " + str(vm_state)+ ").")
263
            
264 265
            elif action == infrastructure.START:
                print "Starting VM" + str(vm_id)
John Giannelos's avatar
John Giannelos committed
266 267
                client.start_server(vm_id)
                
268 269
            elif action == infrastructure.STOP:
                print "Stopping VM"  + str(vm_id)
John Giannelos's avatar
John Giannelos committed
270 271
                client.shutdown_server(vm_id)
    
272 273
            elif action == infrastructure.RESTART:
                print "Restarting VM" + str(vm_id)
John Giannelos's avatar
John Giannelos committed
274
                snf.reboot_server(vm_id)
275

276
            elif action == infrastructure.SUSPEND:
277
                raise HTTPError(501, "This actions is currently no applicable")