Authorize only list of selected VOs
[snf-occi] / snfOCCI / APIserver.py
1 #!/usr/bin/env python
2
3 import re
4 import string
5 from snfOCCI.registry import snfRegistry
6 from snfOCCI.compute import ComputeBackend
7 from snfOCCI.config import SERVER_CONFIG, KAMAKI_CONFIG
8
9 from kamaki.clients.compute import ComputeClient
10 from kamaki.clients.cyclades import CycladesClient
11 from kamaki.config  import Config
12
13 from occi.core_model import Mixin, Resource
14 from occi.backend import MixinBackend
15 from occi.extensions.infrastructure import COMPUTE, START, STOP, SUSPEND, RESTART, RESOURCE_TEMPLATE, OS_TEMPLATE
16 from occi.wsgi import Application
17 from occi.exceptions import HTTPError
18
19 from wsgiref.simple_server import make_server
20 from wsgiref.validate import validator
21
22 import voms
23
24
25 class MyAPP(Application):
26     '''
27     An OCCI WSGI application.
28     '''
29
30     def refresh_images(self, snf, client):
31
32         images = snf.list_images()
33         for image in images:
34             IMAGE_ATTRIBUTES = {'occi.core.id': str(image['id'])}
35             IMAGE = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(image['name']), [OS_TEMPLATE], attributes = IMAGE_ATTRIBUTES)
36             self.register_backend(IMAGE, MixinBackend())
37
38     def refresh_flavors(self, snf, client):
39         
40         flavors = snf.list_flavors()
41         for flavor in flavors:
42             details = snf.get_flavor_details(flavor['id'])
43             FLAVOR_ATTRIBUTES = {'occi.core.id': flavor['id'],
44                                  'occi.compute.cores': details['cpu'],
45                                  'occi.compute.memory': details['ram'],
46                                  'occi.storage.size': details['disk'],
47                                  }
48             FLAVOR = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(flavor['name']), [RESOURCE_TEMPLATE], attributes = FLAVOR_ATTRIBUTES)
49             self.register_backend(FLAVOR, MixinBackend())
50
51
52     def refresh_compute_instances(self, snf):
53         '''Syncing registry with cyclades resources'''
54
55         servers = snf.list_servers()
56         snf_keys = []
57         for server in servers:
58             snf_keys.append(str(server['id']))
59
60         resources = self.registry.resources
61         occi_keys = resources.keys()
62         
63         #Compute instances in synnefo not available in registry
64         diff = [x for x in snf_keys if '/compute/'+x not in occi_keys]
65         for key in diff:
66
67             details = snf.get_server_details(int(key))
68             flavor = snf.get_flavor_details(details['flavorRef'])
69             image = snf.get_image_details(details['imageRef'])
70
71             for i in self.registry.backends:
72                 if i.term == str(image['name']):
73                     rel_image = i
74                 if i.term == str(flavor['name']):
75                     rel_flavor = i
76
77             resource = Resource(key, COMPUTE, [rel_flavor, rel_image])
78             resource.actions = [START]
79             resource.attributes['occi.core.id'] = key
80             resource.attributes['occi.compute.state'] = 'inactive'
81             resource.attributes['occi.compute.architecture'] = SERVER_CONFIG['compute_arch']
82             resource.attributes['occi.compute.cores'] = flavor['cpu']
83             resource.attributes['occi.compute.memory'] = flavor['ram']
84             resource.attributes['occi.compute.hostname'] = SERVER_CONFIG['hostname'] % {'id':int(key)}
85
86             self.registry.add_resource(key, resource, None)
87
88         #Compute instances in registry not available in synnefo
89         diff = [x for x in occi_keys if x[9:] not in snf_keys]
90         for key in diff:
91             self.registry.delete_resource(key, None)
92
93
94     def __call__(self, environ, response):
95
96         compClient = ComputeClient(KAMAKI_CONFIG['compute_url'], environ['HTTP_AUTH_TOKEN'])
97         cyclClient = CycladesClient(KAMAKI_CONFIG['compute_url'], environ['HTTP_AUTH_TOKEN'])
98
99         #Up-to-date flavors and images
100         self.refresh_images(compClient,cyclClient)
101         self.refresh_flavors(compClient,cyclClient)
102         self.refresh_compute_instances(compClient)
103
104         ssl_dict = dict()
105
106         #Regular expression in HTTP headers
107         #environ[HTTP_SSL] contains PEM certificates in wrong format
108         
109         pem_re = r'^(-----BEGIN CERTIFICATE----- )(.*|\s]*)( -----END CERTIFICATE-----)'
110
111         client_cert = re.search(pem_re, environ["HTTP_SSL_CLIENT_CERT"])
112         client_chain = re.search(pem_re, environ["HTTP_SSL_CLIENT_CERT_CHAIN_0"])
113
114         client_cert_list=[]
115         client_chain_list=[]
116
117         for i in range(1,4):
118             client_cert_list.append(string.strip(client_cert.group(i)))
119
120         for i in range(1,4):
121             client_chain_list.append(string.strip(client_chain.group(i)))
122
123
124         cert = client_cert_list[0]+"\n"+client_cert_list[1].replace(" "," \n")+"\n"+client_cert_list[2]
125         chain = client_chain_list[0]+"\n"+client_chain_list[1].replace(" "," \n")+"\n"+client_chain_list[2]
126
127         ssl_dict["SSL_CLIENT_S_DN"] = environ["HTTP_SSL_CLIENT_S_DN"]
128         ssl_dict["SSL_CLIENT_CERT"] = cert
129         ssl_dict["SSL_CLIENT_CERT_CHAIN_0"] = chain
130
131         (user_dn, user_vo, user_fqans) = voms.authenticate(ssl_dict)
132         print (user_dn, user_vo, user_fqans)
133
134
135         #Authenticate only VOs in list
136         VOs = ['see','fedcloud.egi.eu']
137         #Always authenticated, only for testing purposes 
138         
139         authenticated  = False
140
141         if user_vo in VOs:
142             authenticated = True
143
144         if authenticated:
145             # token will be represented in self.extras
146             return self._call_occi(environ, response, security = None, token = environ['HTTP_AUTH_TOKEN'], snf = compClient, client = cyclClient)
147         else:
148             raise HTTPError(404, "Unauthorized access")
149
150
151 def main():
152
153     APP = MyAPP(registry = snfRegistry())
154     COMPUTE_BACKEND = ComputeBackend()
155
156     APP.register_backend(COMPUTE, COMPUTE_BACKEND)
157     APP.register_backend(START, COMPUTE_BACKEND)
158     APP.register_backend(STOP, COMPUTE_BACKEND)
159     APP.register_backend(RESTART, COMPUTE_BACKEND)
160     APP.register_backend(SUSPEND, COMPUTE_BACKEND)
161     APP.register_backend(RESOURCE_TEMPLATE, MixinBackend())
162     APP.register_backend(OS_TEMPLATE, MixinBackend())
163  
164     VALIDATOR_APP = validator(APP)
165     HTTPD = make_server('', SERVER_CONFIG['port'], VALIDATOR_APP)
166     HTTPD.serve_forever()
167