blob: 8135fa619e4e4a5baa07eb6b022ac2c84c6ab979 [file] [log] [blame]
Scott Bakerb63ea792016-08-11 10:24:48 -07001import commands
2import hashlib
Matteo Scandoloceccb1f2017-06-05 10:35:44 -07003from xosconfig import Config
Scott Bakeraf599eb2017-03-21 12:43:26 -07004from synchronizers.new_base.modelaccessor import *
Scott Bakerb63ea792016-08-11 10:24:48 -07005
6try:
Scott Baker04a37f52016-08-11 10:52:21 -07007 from synchronizers.openstack.client import OpenStackClient
Scott Bakerb63ea792016-08-11 10:24:48 -07008 has_openstack = True
9except:
10 has_openstack = False
11
Matteo Scandoloceccb1f2017-06-05 10:35:44 -070012manager_enabled = Config.get("nova.enabled")
Scott Bakerb63ea792016-08-11 10:24:48 -070013
14class OpenStackDriver:
15
16 def __init__(self, config = None, client=None):
Scott Bakerb63ea792016-08-11 10:24:48 -070017
18 if client:
19 self.shell = client
20
21 self.enabled = manager_enabled
22 self.has_openstack = has_openstack
23 self.controller = None
24 self.admin_user = None
25
26 def client_driver(self, caller=None, tenant=None, controller=None):
27 if caller:
28 auth = {'username': caller.email,
29 'password': hashlib.md5(caller.password).hexdigest()[:6],
30 'tenant': tenant}
Matteo Scandoloceccb1f2017-06-05 10:35:44 -070031 client = OpenStackClient(controller=controller, cacert=Config.get("nova.ca_ssl_cert"), **auth)
Scott Bakerb63ea792016-08-11 10:24:48 -070032 else:
33 admin_driver = self.admin_driver(tenant=tenant, controller=controller)
34 client = OpenStackClient(tenant=tenant, controller=admin_driver.controller)
35
36 driver = OpenStackDriver(client=client)
37 #driver.admin_user = admin_driver.admin_user
38 #driver.controller = admin_driver.controller
39 return driver
40
41 def admin_driver(self, tenant=None, controller=None):
42 if isinstance(controller, int):
43 controller = Controller.objects.get(id=controller.id)
44 if not tenant:
45 tenant = controller.admin_tenant
Matteo Scandoloceccb1f2017-06-05 10:35:44 -070046 client = OpenStackClient(tenant=tenant, controller=controller, cacert=Config.get("nova.ca_ssl_cert"))
Scott Bakerb63ea792016-08-11 10:24:48 -070047 driver = OpenStackDriver(client=client)
48 driver.admin_user = client.keystone.users.find(name=controller.admin_user)
49 driver.controller = controller
50 return driver
51
52 def create_role(self, name):
53 roles = self.shell.keystone.roles.findall(name=name)
54 roles_title = self.shell.keystone.roles.findall(name=name.title())
55 roles_found = roles + roles_title
56 if not roles_found:
57 role = self.shell.keystone.roles.create(name)
58 else:
59 role = roles_found[0]
60 return role
61
62 def delete_role(self, filter):
63 roles = self.shell.keystone.roles.findall(**filter)
64 for role in roles:
65 self.shell.keystone.roles.delete(role)
66 return 1
67
68 def create_tenant(self, tenant_name, enabled, description):
69 """Create keystone tenant. Suggested fields: name, description, enabled"""
70 tenants = self.shell.keystone.tenants.findall(name=tenant_name)
71 if not tenants:
72 fields = {'tenant_name': tenant_name, 'enabled': enabled,
73 'description': description}
74 tenant = self.shell.keystone.tenants.create(**fields)
75 else:
76 tenant = tenants[0]
77
78 # always give the admin user the admin role to any tenant created
79 # by the driver.
80 self.add_user_role(self.admin_user.id, tenant.id, 'admin')
81 return tenant
82
83 def update_tenant(self, id, **kwds):
84 return self.shell.keystone.tenants.update(id, **kwds)
85
86 def delete_tenant(self, id):
87 # FIXME: nova_db is commented out in clients.py, throws errors.
88 # Commenting this out for the time being until actually fixed
89
90 #ctx = self.shell.nova_db.ctx
91 tenants = self.shell.keystone.tenants.findall(id=id)
92 for tenant in tenants:
93 # nova does not automatically delete the tenant's instances
94 # so we manually delete instances before deleting the tenant
95 #instances = self.shell.nova_db.instance_get_all_by_filters(ctx,
96 # {'project_id': tenant.id}, 'id', 'asc')
97 #client = OpenStackClient(tenant=tenant.name)
98 #driver = OpenStackDriver(client=client)
99 #for instance in instances:
100 # driver.destroy_instance(instance.id)
101 self.shell.keystone.tenants.delete(tenant)
102 return 1
103
104 def create_user(self, name, email, password, enabled):
105 users = self.shell.keystone.users.findall(email=email)
106 if not users:
107 fields = {'name': name, 'email': email, 'password': password,
108 'enabled': enabled}
109 user = self.shell.keystone.users.create(**fields)
110 else:
111 user = users[0]
112 return user
113
114 def delete_user(self, id):
115 users = self.shell.keystone.users.findall(id=id)
116 for user in users:
117 # delete users keys
118 keys = self.shell.nova.keypairs.findall()
119 for key in keys:
120 self.shell.nova.keypairs.delete(key)
121 self.shell.keystone.users.delete(user)
122 return 1
123
124 def get_admin_role(self):
125 role = None
126 for admin_role_name in ['admin', 'Admin']:
127 roles = self.shell.keystone.roles.findall(name=admin_role_name)
128 if roles:
129 role = roles[0]
130 break
131 return role
132
133 def add_user_role(self, kuser_id, tenant_id, role_name):
134 user = self.shell.keystone.users.find(id=kuser_id)
135 tenant = self.shell.keystone.tenants.find(id=tenant_id)
136 # admin role can be lowercase or title. Look for both
137 role = None
138 if role_name.lower() == 'admin':
139 role = self.get_admin_role()
140 else:
141 # look up non admin role or force exception when admin role isnt found
142 role = self.shell.keystone.roles.find(name=role_name)
143
144 role_found = False
145 user_roles = user.list_roles(tenant.id)
146 for user_role in user_roles:
147 if user_role.name == role.name:
148 role_found = True
149 if not role_found:
150 tenant.add_user(user, role)
151
152 return 1
153
154 def delete_user_role(self, kuser_id, tenant_id, role_name):
155 user = self.shell.keystone.users.find(id=kuser_id)
156 tenant = self.shell.keystone.tenants.find(id=tenant_id)
157 # admin role can be lowercase or title. Look for both
158 role = None
159 if role_name.lower() == 'admin':
160 role = self.get_admin_role()
161 else:
162 # look up non admin role or force exception when admin role isnt found
163 role = self.shell.keystone.roles.find(name=role_name)
164
165 role_found = False
166 user_roles = user.list_roles(tenant.id)
167 for user_role in user_roles:
168 if user_role.name == role.name:
169 role_found = True
170 if role_found:
171 tenant.remove_user(user, role)
172
173 return 1
174
175 def update_user(self, id, fields):
176 if 'password' in fields:
177 self.shell.keystone.users.update_password(id, fields['password'])
178 if 'enabled' in fields:
179 self.shell.keystone.users.update_enabled(id, fields['enabled'])
180 return 1
181
182 def create_router(self, name, set_gateway=True):
183 routers = self.shell.neutron.list_routers(name=name)['routers']
184 if routers:
185 router = routers[0]
186 else:
187 router = self.shell.neutron.create_router({'router': {'name': name}})['router']
188 # add router to external network
189 if set_gateway:
190 nets = self.shell.neutron.list_networks()['networks']
191 for net in nets:
192 if net['router:external'] == True:
193 self.shell.neutron.add_gateway_router(router['id'],
194 {'network_id': net['id']})
195
196 return router
197
198 def delete_router(self, id):
199 routers = self.shell.neutron.list_routers(id=id)['routers']
200 for router in routers:
201 self.shell.neutron.delete_router(router['id'])
202 # remove router form external network
203 #nets = self.shell.neutron.list_networks()['networks']
204 #for net in nets:
205 # if net['router:external'] == True:
206 # self.shell.neutron.remove_gateway_router(router['id'])
207
208 def add_router_interface(self, router_id, subnet_id):
209 router = self.shell.neutron.show_router(router_id)['router']
210 subnet = self.shell.neutron.show_subnet(subnet_id)['subnet']
211 if router and subnet:
212 self.shell.neutron.add_interface_router(router_id, {'subnet_id': subnet_id})
213
214 def delete_router_interface(self, router_id, subnet_id):
215 router = self.shell.neutron.show_router(router_id)
216 subnet = self.shell.neutron.show_subnet(subnet_id)
217 if router and subnet:
218 self.shell.neutron.remove_interface_router(router_id, {'subnet_id': subnet_id})
219
220 def create_network(self, name, shared=False):
221 nets = self.shell.neutron.list_networks(name=name)['networks']
222 if nets:
223 net = nets[0]
224 else:
225 net = self.shell.neutron.create_network({'network': {'name': name, 'shared': shared}})['network']
226 return net
227
228 def delete_network(self, id):
229 nets = self.shell.neutron.list_networks()['networks']
230 for net in nets:
231 if net['id'] == id:
232 # delete_all ports
233 self.delete_network_ports(net['id'])
234 # delete all subnets:
235 for subnet_id in net['subnets']:
236 self.delete_subnet(subnet_id)
237 self.shell.neutron.delete_network(net['id'])
238 return 1
239
240 def delete_network_ports(self, network_id):
241 ports = self.shell.neutron.list_ports()['ports']
242 for port in ports:
243 if port['network_id'] == network_id:
244 self.shell.neutron.delete_port(port['id'])
245 return 1
246
247 def delete_subnet_ports(self, subnet_id):
248 ports = self.shell.neutron.list_ports()['ports']
249 for port in ports:
250 delete = False
251 for fixed_ip in port['fixed_ips']:
252 if fixed_ip['subnet_id'] == subnet_id:
253 delete=True
254 break
255 if delete:
256 self.shell.neutron.delete_port(port['id'])
257 return 1
258
259 def create_subnet(self, name, network_id, cidr_ip, ip_version, start, end):
260 #nets = self.shell.neutron.list_networks(name=network_name)['networks']
261 #if not nets:
262 # raise Exception, "No such network: %s" % network_name
263 #net = nets[0]
264
265 subnet = None
266 subnets = self.shell.neutron.list_subnets()['subnets']
267 for snet in subnets:
268 if snet['cidr'] == cidr_ip and snet['network_id'] == network_id:
269 subnet = snet
270
271 if not subnet:
272 # HACK: Add metadata route -- Neutron does not reliably supply this
273 metadata_ip = cidr_ip.replace("0/24", "3")
274
275 allocation_pools = [{'start': start, 'end': end}]
276 subnet = {'subnet': {'name': name,
277 'network_id': network_id,
278 'ip_version': ip_version,
279 'cidr': cidr_ip,
280 #'dns_nameservers': ['8.8.8.8', '8.8.4.4'],
281 'host_routes': [{'destination':'169.254.169.254/32','nexthop':metadata_ip}],
282 'gateway_ip': None,
283 'allocation_pools': allocation_pools}}
284 subnet = self.shell.neutron.create_subnet(subnet)['subnet']
285 # self.add_external_route(subnet)
286
287 return subnet
288
289 def update_subnet(self, id, fields):
290 return self.shell.neutron.update_subnet(id, fields)
291
292 def delete_subnet(self, id):
293 #return self.shell.neutron.delete_subnet(id=id)
294 # inefficient but fault tolerant
295 subnets = self.shell.neutron.list_subnets()['subnets']
296 for subnet in subnets:
297 if subnet['id'] == id:
298 self.delete_subnet_ports(subnet['id'])
299 self.shell.neutron.delete_subnet(id)
300 self.delete_external_route(subnet)
301 return 1
302
303 def get_external_routes(self):
304 status, output = commands.getstatusoutput('route')
305 routes = output.split('\n')[3:]
306 return routes
307
308 def add_external_route(self, subnet, routes=[]):
309 if not routes:
310 routes = self.get_external_routes()
311
312 ports = self.shell.neutron.list_ports()['ports']
313
314 gw_ip = subnet['gateway_ip']
315 subnet_id = subnet['id']
316
317 # 1. Find the port associated with the subnet's gateway
318 # 2. Find the router associated with that port
319 # 3. Find the port associated with this router and on the external net
320 # 4. Set up route to the subnet through the port from step 3
321 ip_address = None
322 for port in ports:
323 for fixed_ip in port['fixed_ips']:
324 if fixed_ip['subnet_id'] == subnet_id and fixed_ip['ip_address'] == gw_ip:
325 gw_port = port
326 router_id = gw_port['device_id']
327 router = self.shell.neutron.show_router(router_id)['router']
328 if router and router.get('external_gateway_info'):
329 ext_net = router['external_gateway_info']['network_id']
330 for port in ports:
331 if port['device_id'] == router_id and port['network_id'] == ext_net:
332 ip_address = port['fixed_ips'][0]['ip_address']
333
334 if ip_address:
335 # check if external route already exists
336 route_exists = False
337 if routes:
338 for route in routes:
339 if subnet['cidr'] in route and ip_address in route:
340 route_exists = True
341 if not route_exists:
342 cmd = "route add -net %s dev br-ex gw %s" % (subnet['cidr'], ip_address)
343 s, o = commands.getstatusoutput(cmd)
344 #print cmd, "\n", s, o
345
346 return 1
347
348 def delete_external_route(self, subnet):
349 ports = self.shell.neutron.list_ports()['ports']
350
351 gw_ip = subnet['gateway_ip']
352 subnet_id = subnet['id']
353
354 # 1. Find the port associated with the subnet's gateway
355 # 2. Find the router associated with that port
356 # 3. Find the port associated with this router and on the external net
357 # 4. Set up route to the subnet through the port from step 3
358 ip_address = None
359 for port in ports:
360 for fixed_ip in port['fixed_ips']:
361 if fixed_ip['subnet_id'] == subnet_id and fixed_ip['ip_address'] == gw_ip:
362 gw_port = port
363 router_id = gw_port['device_id']
364 router = self.shell.neutron.show_router(router_id)['router']
365 ext_net = router['external_gateway_info']['network_id']
366 for port in ports:
367 if port['device_id'] == router_id and port['network_id'] == ext_net:
368 ip_address = port['fixed_ips'][0]['ip_address']
369
370 if ip_address:
371 cmd = "route delete -net %s" % (subnet['cidr'])
372 commands.getstatusoutput(cmd)
373
374 return 1
375
376 def create_keypair(self, name, public_key):
377 keys = self.shell.nova.keypairs.findall(name=name)
378 if keys:
379 key = keys[0]
380 # update key
381 if key.public_key != public_key:
382 self.delete_keypair(key.id)
383 key = self.shell.nova.keypairs.create(name=name, public_key=public_key)
384 else:
385 key = self.shell.nova.keypairs.create(name=name, public_key=public_key)
386 return key
387
388 def delete_keypair(self, id):
389 keys = self.shell.nova.keypairs.findall(id=id)
390 for key in keys:
391 self.shell.nova.keypairs.delete(key)
392 return 1
393
394 def get_private_networks(self, tenant=None):
395 if not tenant:
396 tenant = self.shell.nova.tenant
397 tenant = self.shell.keystone.tenants.find(name=tenant)
398 search_opts = {"tenant_id": tenant.id, "shared": False}
399 private_networks = self.shell.neutron.list_networks(**search_opts)
400 return private_networks
401
402 def get_shared_networks(self):
403 search_opts = {"shared": True}
404 shared_networks = self.shell.neutron.list_networks(**search_opts)
405 return shared_networks
406
407 def get_network_subnet(self, network_id):
408 subnet_id = None
409 subnet = None
410 if network_id:
411 os_networks = self.shell.neutron.list_networks(id=network_id)["networks"]
412 if os_networks:
413 os_network = os_networks[0]
414 if os_network['subnets']:
415 subnet_id = os_network['subnets'][0]
416 os_subnets = self.shell.neutron.list_subnets(id=subnet_id)['subnets']
417 if os_subnets:
418 subnet = os_subnets[0]['cidr']
419
420 return (subnet_id, subnet)
421
422 def spawn_instance(self, name, key_name=None, availability_zone=None, hostname=None, image_id=None, security_group=None, pubkeys=[], nics=None, metadata=None, userdata=None, flavor_name=None):
423 if not flavor_name:
Matteo Scandoloceccb1f2017-06-05 10:35:44 -0700424 flavor_name = Config.get("nova.default_flavor")
Scott Bakerb63ea792016-08-11 10:24:48 -0700425
426 flavor = self.shell.nova.flavors.find(name=flavor_name)
427
428 if not security_group:
Matteo Scandoloceccb1f2017-06-05 10:35:44 -0700429 security_group = Config.get("nova.default_security_group")
Scott Bakerb63ea792016-08-11 10:24:48 -0700430
431 files = {}
432 #if pubkeys:
433 # files["/root/.ssh/authorized_keys"] = "\n".join(pubkeys).encode('base64')
434 hints = {}
435
436 # determine availability zone and compute host
437 availability_zone_filter = None
438 if availability_zone is None or not availability_zone:
439 availability_zone_filter = 'nova'
440 else:
441 availability_zone_filter = availability_zone
442 if hostname:
443 availability_zone_filter += ':%s' % hostname
444
445 server = self.shell.nova.servers.create(
446 name=name,
447 key_name = key_name,
448 flavor=flavor.id,
449 image=image_id,
450 security_group = security_group,
451 #files = files,
452 scheduler_hints=hints,
453 availability_zone=availability_zone_filter,
454 nics=nics,
455 networks=nics,
456 meta=metadata,
457 userdata=userdata)
458 return server
459
460 def destroy_instance(self, id):
461 if (self.shell.nova.tenant=="admin"):
462 # findall() is implemented as a list() followed by a python search of the
463 # list. Since findall() doesn't accept "all_tenants", we do this using
464 # list() ourselves. This allows us to delete an instance as admin.
465 servers = self.shell.nova.servers.list(search_opts={"all_tenants": True})
466 else:
467 servers = self.shell.nova.servers.list()
468 for server in servers:
469 if server.id == id:
470 result=self.shell.nova.servers.delete(server)
471
472 def update_instance_metadata(self, id, metadata):
473 servers = self.shell.nova.servers.findall(id=id)
474 for server in servers:
475 self.shell.nova.servers.set_meta(server, metadata)
476 # note: set_meta() returns a broken Server() object. Don't try to
477 # print it in the shell or it will fail in __repr__.
478
479 def delete_instance_metadata(self, id, metadata):
480 # note: metadata is a dict. Only the keys matter, not the values.
481 servers = self.shell.nova.servers.findall(id=id)
482 for server in servers:
483 self.shell.nova.servers.delete_meta(server, metadata)
484