root / snfOCCI / APIserver.py @ c1106cd7
History | View | Annotate | Download (7.4 kB)
1 |
#!/usr/bin/env python
|
---|---|
2 |
|
3 |
import re |
4 |
import sys |
5 |
from optparse import OptionParser, OptionValueError |
6 |
import string |
7 |
import sqlite3 |
8 |
|
9 |
from snfOCCI.registry import snfRegistry |
10 |
from snfOCCI.compute import ComputeBackend |
11 |
from snfOCCI.config import SERVER_CONFIG, KAMAKI_CONFIG |
12 |
|
13 |
from kamaki.clients.compute import ComputeClient |
14 |
from kamaki.clients.cyclades import CycladesClient |
15 |
from kamaki.config import Config |
16 |
|
17 |
from occi.core_model import Mixin, Resource |
18 |
from occi.backend import MixinBackend |
19 |
from occi.extensions.infrastructure import COMPUTE, START, STOP, SUSPEND, RESTART, RESOURCE_TEMPLATE, OS_TEMPLATE |
20 |
from occi.wsgi import Application |
21 |
from occi.exceptions import HTTPError |
22 |
|
23 |
from wsgiref.simple_server import make_server |
24 |
from wsgiref.validate import validator |
25 |
|
26 |
import voms |
27 |
|
28 |
def parse_arguments(args): |
29 |
|
30 |
kw = {} |
31 |
kw["usage"] = "%prog [options]" |
32 |
kw["description"] = "OCCI interface to synnefo API" |
33 |
|
34 |
parser = OptionParser(**kw) |
35 |
parser.disable_interspersed_args() |
36 |
|
37 |
parser.add_option("--enable_voms", action="store_true", dest="enable_voms", default=False, help="Enable voms authorization") |
38 |
parser.add_option("--voms_db", action="store", type="string", dest="voms_db", help="Path to sqlite database file") |
39 |
|
40 |
(opts, args) = parser.parse_args(args) |
41 |
|
42 |
if opts.enable_voms and not opts.voms_db: |
43 |
print "--voms_db option required" |
44 |
parser.print_help() |
45 |
|
46 |
return (opts, args)
|
47 |
|
48 |
class MyAPP(Application): |
49 |
'''
|
50 |
An OCCI WSGI application.
|
51 |
'''
|
52 |
|
53 |
def refresh_images(self, snf, client): |
54 |
|
55 |
images = snf.list_images() |
56 |
for image in images: |
57 |
IMAGE_ATTRIBUTES = {'occi.core.id': str(image['id'])} |
58 |
IMAGE = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(image['name']), [OS_TEMPLATE], attributes = IMAGE_ATTRIBUTES) |
59 |
self.register_backend(IMAGE, MixinBackend())
|
60 |
|
61 |
def refresh_flavors(self, snf, client): |
62 |
|
63 |
flavors = snf.list_flavors() |
64 |
for flavor in flavors: |
65 |
details = snf.get_flavor_details(flavor['id'])
|
66 |
FLAVOR_ATTRIBUTES = {'occi.core.id': flavor['id'], |
67 |
'occi.compute.cores': details['cpu'], |
68 |
'occi.compute.memory': details['ram'], |
69 |
'occi.storage.size': details['disk'], |
70 |
} |
71 |
FLAVOR = Mixin("http://schemas.ogf.org/occi/infrastructure#", str(flavor['name']), [RESOURCE_TEMPLATE], attributes = FLAVOR_ATTRIBUTES) |
72 |
self.register_backend(FLAVOR, MixinBackend())
|
73 |
|
74 |
|
75 |
def refresh_compute_instances(self, snf): |
76 |
'''Syncing registry with cyclades resources'''
|
77 |
|
78 |
servers = snf.list_servers() |
79 |
snf_keys = [] |
80 |
for server in servers: |
81 |
snf_keys.append(str(server['id'])) |
82 |
|
83 |
resources = self.registry.resources
|
84 |
occi_keys = resources.keys() |
85 |
|
86 |
#Compute instances in synnefo not available in registry
|
87 |
diff = [x for x in snf_keys if '/compute/'+x not in occi_keys] |
88 |
for key in diff: |
89 |
|
90 |
details = snf.get_server_details(int(key))
|
91 |
flavor = snf.get_flavor_details(details['flavorRef'])
|
92 |
image = snf.get_image_details(details['imageRef'])
|
93 |
|
94 |
for i in self.registry.backends: |
95 |
if i.term == str(image['name']): |
96 |
rel_image = i |
97 |
if i.term == str(flavor['name']): |
98 |
rel_flavor = i |
99 |
|
100 |
resource = Resource(key, COMPUTE, [rel_flavor, rel_image]) |
101 |
resource.actions = [START] |
102 |
resource.attributes['occi.core.id'] = key
|
103 |
resource.attributes['occi.compute.state'] = 'inactive' |
104 |
resource.attributes['occi.compute.architecture'] = SERVER_CONFIG['compute_arch'] |
105 |
resource.attributes['occi.compute.cores'] = flavor['cpu'] |
106 |
resource.attributes['occi.compute.memory'] = flavor['ram'] |
107 |
resource.attributes['occi.compute.hostname'] = SERVER_CONFIG['hostname'] % {'id':int(key)} |
108 |
|
109 |
self.registry.add_resource(key, resource, None) |
110 |
|
111 |
#Compute instances in registry not available in synnefo
|
112 |
diff = [x for x in occi_keys if x[9:] not in snf_keys] |
113 |
for key in diff: |
114 |
self.registry.delete_resource(key, None) |
115 |
|
116 |
|
117 |
def __call__(self, environ, response): |
118 |
|
119 |
#Authorization
|
120 |
|
121 |
if ENABLE_VOMS:
|
122 |
|
123 |
global VOMS_DB
|
124 |
conn = sqlite3.connect(VOMS_DB) |
125 |
|
126 |
ssl_dict = dict()
|
127 |
|
128 |
#Regular expression in HTTP headers
|
129 |
#raw environ[HTTP_SSL] contains PEM certificates in wrong format
|
130 |
|
131 |
pem_re = r'^(-----BEGIN CERTIFICATE----- )(.*|\s]*)( -----END CERTIFICATE-----)'
|
132 |
|
133 |
client_cert = re.search(pem_re, environ["HTTP_SSL_CLIENT_CERT"])
|
134 |
client_chain = re.search(pem_re, environ["HTTP_SSL_CLIENT_CERT_CHAIN_0"])
|
135 |
|
136 |
client_cert_list=[] |
137 |
client_chain_list=[] |
138 |
|
139 |
for i in range(1,4): |
140 |
client_cert_list.append(string.strip(client_cert.group(i))) |
141 |
|
142 |
for i in range(1,4): |
143 |
client_chain_list.append(string.strip(client_chain.group(i))) |
144 |
|
145 |
|
146 |
cert = client_cert_list[0]+"\n"+client_cert_list[1].replace(" "," \n")+"\n"+client_cert_list[2] |
147 |
chain = client_chain_list[0]+"\n"+client_chain_list[1].replace(" "," \n")+"\n"+client_chain_list[2] |
148 |
|
149 |
ssl_dict["SSL_CLIENT_S_DN"] = environ["HTTP_SSL_CLIENT_S_DN"] |
150 |
ssl_dict["SSL_CLIENT_CERT"] = cert
|
151 |
ssl_dict["SSL_CLIENT_CERT_CHAIN_0"] = chain
|
152 |
|
153 |
(user_dn, user_vo, user_fqans) = voms.authenticate(ssl_dict) |
154 |
print (user_dn, user_vo, user_fqans)
|
155 |
|
156 |
cursor = conn.cursor() |
157 |
query = "SELECT token FROM vo_map WHERE vo_name=?"
|
158 |
cursor.execute(query,[(user_vo)]) |
159 |
|
160 |
(token,) = cursor.fetchone() |
161 |
|
162 |
if token:
|
163 |
compClient = ComputeClient(KAMAKI_CONFIG['compute_url'], token)
|
164 |
cyclClient = CycladesClient(KAMAKI_CONFIG['compute_url'], token)
|
165 |
|
166 |
self.refresh_images(compClient,cyclClient)
|
167 |
self.refresh_flavors(compClient,cyclClient)
|
168 |
self.refresh_compute_instances(compClient)
|
169 |
|
170 |
|
171 |
return self._call_occi(environ, response, security = None, token = token, snf = compClient, client = cyclClient) |
172 |
else:
|
173 |
raise HTTPError(404, "Unauthorized access") |
174 |
|
175 |
else:
|
176 |
#Authorize with user token
|
177 |
compClient = ComputeClient(KAMAKI_CONFIG['compute_url'], environ['HTTP_AUTH_TOKEN']) |
178 |
cyclClient = CycladesClient(KAMAKI_CONFIG['compute_url'], environ['HTTP_AUTH_TOKEN']) |
179 |
|
180 |
return self._call_occi(environ, response, security = None, token = environ['HTTP_AUTH_TOKEN'], snf = compClient, client = cyclClient) |
181 |
|
182 |
|
183 |
def main(): |
184 |
|
185 |
global ENABLE_VOMS, VOMS_DB
|
186 |
(opts, args) = parse_arguments(sys.argv[1:])
|
187 |
|
188 |
ENABLE_VOMS = opts.enable_voms |
189 |
VOMS_DB = opts.voms_db |
190 |
|
191 |
APP = MyAPP(registry = snfRegistry()) |
192 |
|
193 |
COMPUTE_BACKEND = ComputeBackend() |
194 |
APP.register_backend(COMPUTE, COMPUTE_BACKEND) |
195 |
APP.register_backend(START, COMPUTE_BACKEND) |
196 |
APP.register_backend(STOP, COMPUTE_BACKEND) |
197 |
APP.register_backend(RESTART, COMPUTE_BACKEND) |
198 |
APP.register_backend(SUSPEND, COMPUTE_BACKEND) |
199 |
APP.register_backend(RESOURCE_TEMPLATE, MixinBackend()) |
200 |
APP.register_backend(OS_TEMPLATE, MixinBackend()) |
201 |
|
202 |
VALIDATOR_APP = validator(APP) |
203 |
HTTPD = make_server('', SERVER_CONFIG['port'], VALIDATOR_APP) |
204 |
HTTPD.serve_forever() |
205 |
|