678452c0cf2f3a56f5f72deb2314f2d9b883197b
[snf-occi] / snf-occi-server.py
1 #!/usr/bin/env python
2
3 from kamaki.clients.compute import ComputeClient
4 from kamaki.config  import Config
5
6 from occi.core_model import Mixin, Resource, Link, Entity
7 from occi.backend import ActionBackend, KindBackend, MixinBackend
8 from occi.extensions.infrastructure import COMPUTE, START, STOP, SUSPEND, RESTART, RESOURCE_TEMPLATE, OS_TEMPLATE
9
10 from occi.wsgi import Application
11
12 from wsgiref.simple_server import make_server
13 from wsgiref.validate import validator
14
15
16 class MyBackend(KindBackend, ActionBackend):
17     '''
18     An very simple abstract backend which handles update and replace for
19     attributes. Support for links and mixins would need to added.
20     '''
21
22     def update(self, old, new, extras):
23         # here you can check what information from new_entity you wanna bring
24         # into old_entity
25
26         # trigger your hypervisor and push most recent information
27         print('Updating a resource with id: ' + old.identifier)
28         for item in new.attributes.keys():
29             old.attributes[item] = new.attributes[item]
30
31     def replace(self, old, new, extras):
32         print('Replacing a resource with id: ' + old.identifier)
33         old.attributes = {}
34         for item in new.attributes.keys():
35             old.attributes[item] = new.attributes[item]
36         old.attributes['occi.compute.state'] = 'inactive'
37
38
39 class ComputeBackend(MyBackend):
40     '''
41     Backend for Cyclades/Openstack compute instances
42     '''
43
44     def create(self, entity, extras):
45     
46         for mixin in entity.mixins:
47             if mixin.related[0].term == 'os_tpl':
48                 image = mixin
49                 image_id = mixin.attributes['occi.core.id']
50             if mixin.related[0].term == 'resource_tpl':
51                 flavor = mixin
52                 flavor_id = mixin.attributes['occi.core.id']
53                 
54         entity.attributes['occi.compute.state'] = 'active'
55         entity.actions = [STOP, SUSPEND, RESTART]
56
57         #Registry identifier is the uuid key occi.handler assigns
58         #attribute 'occi.core.id' will be the snf-server id
59
60         snf = ComputeClient(Config())
61         vm_name = entity.attributes['occi.compute.hostname']
62         info = snf.create_server(vm_name, flavor_id, image_id)
63         entity.attributes['occi.core.id'] = str(info['id'])
64         entity.attributes['occi.compute.cores'] = flavor.attributes['occi.compute.cores']
65         entity.attributes['occi.compute.memory'] = flavor.attributes['occi.compute.memory']
66
67     def retrieve(self, entity, extras):
68         
69         # triggering cyclades to retrieve up to date information
70
71         snf = ComputeClient(Config())
72
73         vm_id = int(entity.attributes['occi.core.id'])
74         vm_info = snf.get_server_details(vm_id)
75         vm_state = vm_info['status']
76         
77         status_dict = {'ACTIVE' : 'active',
78                        'STOPPED' : 'inactive',
79                        'ERROR' : 'inactive',
80                        'BUILD' : 'inactive',
81                        'DELETED' : 'inactive',
82                        }
83         
84         entity.attributes['occi.compute.state'] = status_dict[vm_state]
85
86         if entity.attributes['occi.compute.state'] == 'inactive':
87             entity.actions = [START]
88         if entity.attributes['occi.compute.state'] == 'active': 
89             entity.actions = [STOP, SUSPEND, RESTART]
90         if entity.attributes['occi.compute.state'] == 'suspended':
91             entity.actions = [START]
92
93         
94
95
96     def delete(self, entity, extras):
97         # delete vm with vm_id = entity.attributes['occi.core.id']
98         snf = ComputeClient(Config())
99         vm_id = int(entity.attributes['occi.core.id'])
100         snf.delete_server(vm_id)
101
102
103     def action(self, entity, action, extras):
104         if action not in entity.actions:
105             raise AttributeError("This action is currently no applicable.")
106         elif action == START:
107             entity.attributes['occi.compute.state'] = 'active'
108             entity.actions = [STOP, SUSPEND, RESTART]
109             # read attributes from action and do something with it :-)
110             print('Starting virtual machine with id' + entity.identifier)
111         elif action == STOP:
112             entity.attributes['occi.compute.state'] = 'inactive'
113             entity.actions = [START]
114             # read attributes from action and do something with it :-)
115             print('Stopping virtual machine with id' + entity.identifier)
116         elif action == RESTART:
117             entity.actions = [STOP, SUSPEND, RESTART]
118             entity.attributes['occi.compute.state'] = 'active'
119             # read attributes from action and do something with it :-)
120             print('Restarting virtual machine with id' + entity.identifier)
121         elif action == SUSPEND:
122             entity.attributes['occi.compute.state'] = 'suspended'
123             entity.actions = [START]
124             # read attributes from action and do something with it :-)
125             print('Suspending virtual machine with id' + entity.identifier)
126
127
128 class MyAPP(Application):
129     '''
130     An OCCI WSGI application.
131     '''
132
133     def __call__(self, environ, response):
134         sec_obj = {'username': 'password'}
135
136         
137         #Refresh registry entries with current Cyclades state
138         snf = ComputeClient(Config())
139
140         '''
141         images = snf.list_images()
142         for image in images:
143             IMAGE_ATTRIBUTES = {'occi.core.id': str(image['id'])}
144             IMAGE = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(image['name']), [OS_TEMPLATE], attributes = IMAGE_ATTRIBUTES)
145             self.register_backend(IMAGE, MixinBackend())
146
147         flavors = snf.list_flavors()
148         for flavor in flavors:
149             FLAVOR_ATTRIBUTES = {'occi.core.id': flavor['id'],
150                                  'occi.compute.cores': snf.get_flavor_details(flavor['id'])['cpu'],
151                                  'occi.compute.memory': snf.get_flavor_details(flavor['id'])['ram'],
152                                  'occi.storage.size': snf.get_flavor_details(flavor['id'])['disk'],
153                                  }
154             FLAVOR = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(flavor['name']), [RESOURCE_TEMPLATE], attributes = FLAVOR_ATTRIBUTES)
155             self.register_backend(FLAVOR, MixinBackend())
156             '''       
157
158         #TODO show only current VM instances
159         
160         return self._call_occi(environ, response, security=sec_obj, foo=None)
161
162 if __name__ == '__main__':
163
164     APP = MyAPP()
165
166     COMPUTE_BACKEND = ComputeBackend()
167
168     APP.register_backend(COMPUTE, COMPUTE_BACKEND)
169     APP.register_backend(START, COMPUTE_BACKEND)
170     APP.register_backend(STOP, COMPUTE_BACKEND)
171     APP.register_backend(RESTART, COMPUTE_BACKEND)
172     APP.register_backend(SUSPEND, COMPUTE_BACKEND)
173     APP.register_backend(RESOURCE_TEMPLATE, MixinBackend())
174     APP.register_backend(OS_TEMPLATE, MixinBackend())
175     
176     snf = ComputeClient(Config())
177     
178     images = snf.list_images()
179     for image in images:
180         IMAGE_ATTRIBUTES = {'occi.core.id': str(image['id'])}
181         IMAGE = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(image['name']), [OS_TEMPLATE], attributes = IMAGE_ATTRIBUTES)
182         APP.register_backend(IMAGE, MixinBackend())
183
184     flavors = snf.list_flavors()
185     for flavor in flavors:
186         FLAVOR_ATTRIBUTES = {'occi.core.id': flavor['id'],
187                              'occi.compute.cores': snf.get_flavor_details(flavor['id'])['cpu'],
188                              'occi.compute.memory': snf.get_flavor_details(flavor['id'])['ram'],
189                              'occi.storage.size': snf.get_flavor_details(flavor['id'])['disk'],
190                              }
191         FLAVOR = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(flavor['name']), [RESOURCE_TEMPLATE], attributes = FLAVOR_ATTRIBUTES)
192         APP.register_backend(FLAVOR, MixinBackend())
193
194  
195     VALIDATOR_APP = validator(APP)
196     HTTPD = make_server('', 8888, VALIDATOR_APP)
197     HTTPD.serve_forever()
198