fix bug in delete tenant
[plstackapi.git] / planetstack / openstack / driver.py
1 import commands
2 from planetstack.config import Config
3 from openstack.client import OpenStackClient
4
5 class OpenStackDriver:
6
7     def __init__(self, config = None, client=None): 
8         if config:
9             self.config = Config(config)
10         else:
11             self.config = Config() 
12
13         self.admin_client = OpenStackClient()
14         self.admin_user = self.admin_client.keystone.users.find(name=self.admin_client.keystone.username)
15
16         if client:
17             self.shell = client
18         else:
19             self.shell = OpenStackClient()
20
21     def create_role(self, name): 
22         roles = self.shell.keystone.roles.findall(name=name)
23         if not roles:
24             role = self.shell.keystone.roles.create(name)
25         else:
26             role = roles[0] 
27         return role
28
29     def delete_role(self, filter):
30         roles = self.shell.keystone.roles.findall(**filter)
31         for role in roles:
32             self.shell.keystone.roles.delete(role)
33         return 1
34
35     def create_tenant(self, tenant_name, enabled, description):
36         """Create keystone tenant. Suggested fields: name, description, enabled"""  
37         tenants = self.shell.keystone.tenants.findall(name=tenant_name)
38         if not tenants:
39             fields = {'tenant_name': tenant_name, 'enabled': enabled, 
40                       'description': description}  
41             tenant = self.shell.keystone.tenants.create(**fields)
42         else:
43             tenant = tenants[0]
44
45         # always give the admin user the admin role to any tenant created 
46         # by the driver. 
47         self.add_user_role(self.admin_user.id, tenant.id, 'admin')
48         return tenant
49
50     def update_tenant(self, id, **kwds):
51         return self.shell.keystone.tenants.update(id, **kwds)
52
53     def delete_tenant(self, id):
54         ctx = self.shell.nova_db.ctx
55         tenants = self.shell.keystone.tenants.findall(id=id)
56         for tenant in tenants:
57             # nova does not automatically delete the tenant's instances
58             # so we manually delete instances before deleteing the tenant   
59             instances = self.shell.nova_db.instance_get_all_by_filters(ctx, 
60                        {'project_id': tenant.id}, 'id', 'asc')
61             client = OpenStackClient(tenant=tenant.name)
62             driver = OpenStackDriver(client=client)
63             for instance in instances:
64                 driver.destroy_instance(instance.id)
65             self.shell.keystone.tenants.delete(tenant)
66         return 1
67
68     def create_user(self, name, email, password, enabled):
69         users = self.shell.keystone.users.findall(email=email)
70         if not users:
71             fields = {'name': name, 'email': email, 'password': password,
72                       'enabled': enabled}
73             user = self.shell.keystone.users.create(**fields)
74         else: 
75             user = users[0]
76         return user
77
78     def delete_user(self, id):
79         users = self.shell.keystone.users.findall(id=id)
80         for user in users:
81             # delete users keys
82             keys = self.shell.nova.keypairs.findall()
83             for key in keys:
84                 self.shell.nova.keypairs.delete(key)
85             self.shell.keystone.users.delete(user)
86         return 1 
87
88     def add_user_role(self, kuser_id, tenant_id, role_name):
89         user = self.shell.keystone.users.find(id=kuser_id)
90         tenant = self.shell.keystone.tenants.find(id=tenant_id)
91         role = self.shell.keystone.roles.find(name=role_name)
92
93         role_found = False
94         user_roles = user.list_roles(tenant.id)
95         for user_role in user_roles:
96             if user_role.name == role.name:
97                 role_found = True
98         if not role_found:
99             tenant.add_user(user, role)
100
101         return 1
102
103     def delete_user_role(self, kuser_id, tenant_id, role_name):
104         user = self.shell.keystone.users.find(id=kuser_id)
105         tenant = self.shell.keystone.tenants.find(id=tenant_id)
106         role = self.shell.keystone.roles.find(name=role_name)
107
108         role_found = False
109         user_roles = user.list_roles(tenant.id)
110         for user_role in user_roles:
111             if user_role.name == role.name:
112                 role_found = True
113         if role_found:
114             tenant.remove_user(user, role)
115
116         return 1 
117
118     def update_user(self, id, fields):
119         if 'password' in fields:
120             self.shell.keystone.users.update_password(id, fields['password'])
121         if 'enabled' in fields:
122             self.shell.keystone.users.update_enabled(id, fields['enabled']) 
123         return 1 
124
125     def create_router(self, name, set_gateway=True):
126         routers = self.shell.quantum.list_routers(name=name)['routers']
127         if routers:
128             router = routers[0]
129         else:
130             router = self.shell.quantum.create_router({'router': {'name': name}})['router']
131         # add router to external network
132         if set_gateway:
133             nets = self.shell.quantum.list_networks()['networks']
134             for net in nets:
135                 if net['router:external'] == True: 
136                     self.shell.quantum.add_gateway_router(router['id'],
137                                                           {'network_id': net['id']})
138         
139         return router
140
141     def delete_router(self, id):
142         routers = self.shell.quantum.list_routers(id=id)['routers']
143         for router in routers:
144             self.shell.quantum.delete_router(router['id'])
145             # remove router form external network
146             #nets = self.shell.quantum.list_networks()['networks']
147             #for net in nets:
148             #    if net['router:external'] == True:
149             #        self.shell.quantum.remove_gateway_router(router['id'])
150
151     def add_router_interface(self, router_id, subnet_id):
152         router = self.shell.quantum.show_router(router_id)['router']
153         subnet = self.shell.quantum.show_subnet(subnet_id)['subnet']
154         if router and subnet:
155             self.shell.quantum.add_interface_router(router_id, {'subnet_id': subnet_id})
156
157     def delete_router_interface(self, router_id, subnet_id):
158         router = self.shell.quantum.show_router(router_id)
159         subnet = self.shell.quantum.show_subnet(subnet_id)
160         if router and subnet:
161             self.shell.quantum.remove_interface_router(router_id, {'subnet_id': subnet_id})
162  
163     def create_network(self, name):
164         nets = self.shell.quantum.list_networks(name=name)['networks']
165         if nets: 
166             net = nets[0]
167         else:
168             net = self.shell.quantum.create_network({'network': {'name': name}})['network']
169         return net
170  
171     def delete_network(self, id):
172         nets = self.shell.quantum.list_networks()['networks']
173         for net in nets:
174             if net['id'] == id:
175                 # delete_all ports
176                 self.delete_network_ports(net['id'])
177                 # delete all subnets:
178                 for subnet_id in net['subnets']:
179                     self.delete_subnet(subnet_id)
180                 self.shell.quantum.delete_network(net['id'])
181         return 1
182
183     def delete_network_ports(self, network_id):
184         ports = self.shell.quantum.list_ports()['ports']
185         for port in ports:
186             if port['network_id'] == network_id:
187                 self.shell.quantum.delete_port(port['id'])
188         return 1         
189
190     def delete_subnet_ports(self, subnet_id):
191         ports = self.shell.quantum.list_ports()['ports']
192         for port in ports:
193             delete = False
194             for fixed_ip in port['fixed_ips']:
195                 if fixed_ip['subnet_id'] == subnet_id:
196                     delete=True
197                     break
198             if delete:
199                 self.shell.quantum.delete_port(port['id'])
200         return 1
201  
202     def create_subnet(self, name, network_id, cidr_ip, ip_version, start, end):
203         #nets = self.shell.quantum.list_networks(name=network_name)['networks']
204         #if not nets:
205         #    raise Exception, "No such network: %s" % network_name   
206         #net = nets[0]
207
208         subnet = None 
209         subnets = self.shell.quantum.list_subnets()['subnets']
210         for snet in subnets:
211             if snet['cidr'] == cidr_ip and snet['network_id'] == network_id:
212                 subnet = snet
213  
214         if not subnet:
215             allocation_pools = [{'start': start, 'end': end}]
216             subnet = {'subnet': {'name': name,
217                                  'network_id': network_id,
218                                  'ip_version': ip_version,
219                                  'cidr': cidr_ip,
220                                  'dns_nameservers': ['8.8.8.8', '8.8.4.4'],
221                                  'allocation_pools': allocation_pools}}          
222             subnet = self.shell.quantum.create_subnet(subnet)['subnet']
223             self.add_external_route(subnet)
224         # TODO: Add route to external network
225         # e.g. #  route add -net 10.0.3.0/24 dev br-ex gw 10.100.0.5 
226         return subnet
227
228     def update_subnet(self, id, fields):
229         return self.shell.quantum.update_subnet(id, fields)
230
231     def delete_subnet(self, id):
232         #return self.shell.quantum.delete_subnet(id=id)
233         # inefficient but fault tolerant
234         subnets = self.shell.quantum.list_subnets()['subnets']
235         for subnet in subnets:
236             if subnet['id'] == id:
237                 self.delete_subnet_ports(subnet['id'])
238                 self.shell.quantum.delete_subnet(id)
239                 self.delete_external_route(subnet)
240         return 1
241
242     def get_external_routes(self):
243         status, output = commands.getstatusoutput('route')
244         routes = output.split('\n')[3:]
245         return routes
246
247     def add_external_route(self, subnet, routes=[]):
248         if not routes:
249             routes = self.get_external_routes()
250  
251         ports = self.shell.quantum.list_ports()['ports']
252
253         gw_ip = subnet['gateway_ip']
254         subnet_id = subnet['id']
255
256         # 1. Find the port associated with the subnet's gateway
257         # 2. Find the router associated with that port
258         # 3. Find the port associated with this router and on the external net
259         # 4. Set up route to the subnet through the port from step 3
260         ip_address = None
261         for port in ports:
262             for fixed_ip in port['fixed_ips']:
263                 if fixed_ip['subnet_id'] == subnet_id and fixed_ip['ip_address'] == gw_ip:
264                     gw_port = port
265                     router_id = gw_port['device_id']
266                     router = self.shell.quantum.show_router(router_id)['router']
267                     if router and router.get('external_gateway_info'):
268                         ext_net = router['external_gateway_info']['network_id']
269                         for port in ports:
270                             if port['device_id'] == router_id and port['network_id'] == ext_net:
271                                 ip_address = port['fixed_ips'][0]['ip_address']
272
273         if ip_address:
274             # check if external route already exists
275             route_exists = False
276             if routes:
277                 for route in routes:
278                     if subnet['cidr'] in route and ip_address in route:
279                         route_exists = True
280             if not route_exists:
281                 cmd = "route add -net %s dev br-ex gw %s" % (subnet['cidr'], ip_address)
282                 s, o = commands.getstatusoutput(cmd)
283                 #print cmd, "\n", s, o
284
285         return 1
286
287     def delete_external_route(self, subnet):
288         ports = self.shell.quantum.list_ports()['ports']
289
290         gw_ip = subnet['gateway_ip']
291         subnet_id = subnet['id']
292
293         # 1. Find the port associated with the subnet's gateway
294         # 2. Find the router associated with that port
295         # 3. Find the port associated with this router and on the external net
296         # 4. Set up route to the subnet through the port from step 3
297         ip_address = None
298         for port in ports:
299             for fixed_ip in port['fixed_ips']:
300                 if fixed_ip['subnet_id'] == subnet_id and fixed_ip['ip_address'] == gw_ip:
301                     gw_port = port
302                     router_id = gw_port['device_id']
303                     router = self.shell.quantum.show_router(router_id)['router']
304                     ext_net = router['external_gateway_info']['network_id']
305                     for port in ports:
306                         if port['device_id'] == router_id and port['network_id'] == ext_net:
307                             ip_address = port['fixed_ips'][0]['ip_address']
308
309         if ip_address:
310             cmd = "route delete -net %s" % (subnet['cidr'])
311             commands.getstatusoutput(cmd)
312              
313         return 1
314     
315     def create_keypair(self, name, public_key):
316         keys = self.shell.nova.keypairs.findall(name=name)
317         if keys:
318             key = keys[0]
319             # update key     
320             if key.public_key != public_key:
321                 self.delete_keypair(key.id)
322                 key = self.shell.nova.keypairs.create(name=name, public_key=public_key)
323         else:
324             key = self.shell.nova.keypairs.create(name=name, public_key=public_key)
325         return key
326
327     def delete_keypair(self, id):
328         keys = self.shell.nova.keypairs.findall(id=id)
329         for key in keys:
330             self.shell.nova.keypairs.delete(key) 
331         return 1 
332
333     def spawn_instance(self, name, key_name=None, hostname=None, image_id=None, security_group=None, pubkeys=[]):
334         flavor_name = self.config.nova_default_flavor
335         flavor = self.shell.nova.flavors.find(name=flavor_name)
336         #if not image:
337         #    image = self.config.nova_default_imave
338         if not security_group:
339             security_group = self.config.nova_default_security_group 
340
341         files = {}
342         if pubkeys:    
343             files['/root/.ssh/authorized_keys'] = "\n".join(pubkeys)
344        
345         hints = {}
346         availability_zone = None
347         if hostname:
348             availability_zone = 'nova:%s' % hostname
349         server = self.shell.nova.servers.create(
350                                             name=name,
351                                             key_name = key_name,
352                                             flavor=flavor.id,
353                                             image=image_id,
354                                             security_group = security_group,
355                                             files=files,
356                                             scheduler_hints=hints,
357                                             availability_zone=availability_zone)
358         return server
359           
360     def destroy_instance(self, id):
361         servers = self.shell.nova.servers.findall(id=id)
362         for server in servers:
363             self.shell.nova.servers.delete(server)
364
365     def update_instance_metadata(self, id, metadata):
366         servers = self.shell.nova.servers.findall(id=id)
367         for server in servers:
368             self.shell.nova.servers.set_meta(server, metadata)
369             # note: set_meta() returns a broken Server() object. Don't try to
370             # print it in the shell or it will fail in __repr__.
371
372     def delete_instance_metadata(self, id, metadata):
373         # note: metadata is a dict. Only the keys matter, not the values.
374         servers = self.shell.nova.servers.findall(id=id)
375         for server in servers:
376             self.shell.nova.servers.delete_meta(server, metadata)
377