blob: b3c141adb94e4460a599edc2076639fa075794a2 [file] [log] [blame]
koder aka kdanilov4643fd62015-02-10 16:20:13 -08001import re
2import os
3import time
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03004import os.path
koder aka kdanilove21d7472015-02-14 19:02:04 -08005import logging
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +03006import subprocess
koder aka kdanilov4643fd62015-02-10 16:20:13 -08007
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08008from concurrent.futures import ThreadPoolExecutor
9
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030010from novaclient.exceptions import NotFound
koder aka kdanilov4643fd62015-02-10 16:20:13 -080011from novaclient.client import Client as n_client
12from cinderclient.v1.client import Client as c_client
13
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030014import wally
15from wally.discover import Node
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030016
koder aka kdanilov4643fd62015-02-10 16:20:13 -080017
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030018logger = logging.getLogger("wally.vms")
koder aka kdanilove21d7472015-02-14 19:02:04 -080019
20
koder aka kdanilov4643fd62015-02-10 16:20:13 -080021def ostack_get_creds():
22 env = os.environ.get
23 name = env('OS_USERNAME')
24 passwd = env('OS_PASSWORD')
25 tenant = env('OS_TENANT_NAME')
26 auth_url = env('OS_AUTH_URL')
gstepanov023c1e42015-04-08 15:50:19 +030027
koder aka kdanilov4643fd62015-02-10 16:20:13 -080028 return name, passwd, tenant, auth_url
29
30
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030031NOVA_CONNECTION = None
32
33
34def nova_connect(name=None, passwd=None, tenant=None, auth_url=None):
35 global NOVA_CONNECTION
36 if NOVA_CONNECTION is None:
37 if name is None:
38 name, passwd, tenant, auth_url = ostack_get_creds()
39 NOVA_CONNECTION = n_client('1.1', name, passwd, tenant, auth_url)
40 return NOVA_CONNECTION
41
42
43def nova_disconnect():
44 global NOVA_CONNECTION
45 if NOVA_CONNECTION is not None:
46 NOVA_CONNECTION.close()
47 NOVA_CONNECTION = None
koder aka kdanilov4643fd62015-02-10 16:20:13 -080048
49
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030050def prepare_os_subpr(name=None, passwd=None, tenant=None, auth_url=None):
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030051 if name is None:
52 name, passwd, tenant, auth_url = ostack_get_creds()
53
54 params = {
55 'OS_USERNAME': name,
56 'OS_PASSWORD': passwd,
57 'OS_TENANT_NAME': tenant,
58 'OS_AUTH_URL': auth_url
59 }
60
61 params_s = " ".join("{}={}".format(k, v) for k, v in params.items())
62
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030063 spath = os.path.dirname(wally.__file__)
64 spath = os.path.dirname(spath)
65 spath = os.path.join(spath, 'scripts/prepare.sh')
66
67 cmd_templ = "env {params} bash {spath} >/dev/null"
68 cmd = cmd_templ.format(params=params_s, spath=spath)
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030069 subprocess.call(cmd, shell=True)
70
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030071
72def prepare_os(nova, params):
73 allow_ssh(nova, params['security_group'])
74
75 shed_ids = []
76 for shed_group in params['schedulers_groups']:
77 shed_ids.append(get_or_create_aa_group(nova, shed_group))
78
79 create_keypair(nova,
80 params['keypair_name'],
81 params['pub_key_path'],
82 params['priv_key_path'])
83
84 create_image(nova, params['image']['name'],
85 params['image']['url'])
86
87 create_flavor(nova, **params['flavor'])
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +030088
89
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030090def get_or_create_aa_group(nova, name):
91 try:
92 group = nova.server_groups.find(name=name)
93 except NotFound:
94 group = nova.server_groups.create({'name': name,
95 'policies': ['anti-affinity']})
koder aka kdanilov652cd802015-04-13 12:21:07 +030096
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030097 return group.id
koder aka kdanilov652cd802015-04-13 12:21:07 +030098
99
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300100def allow_ssh(nova, group_name):
101 try:
102 secgroup = nova.security_groups.find(name=group_name)
103 except NotFound:
104 secgroup = nova.security_groups.create(group_name,
105 "allow ssh/ping to node")
106
koder aka kdanilov652cd802015-04-13 12:21:07 +0300107 nova.security_group_rules.create(secgroup.id,
108 ip_protocol="tcp",
109 from_port="22",
110 to_port="22",
111 cidr="0.0.0.0/0")
112
113 nova.security_group_rules.create(secgroup.id,
114 ip_protocol="icmp",
115 from_port=-1,
116 cidr="0.0.0.0/0",
117 to_port=-1)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300118 return secgroup.id
koder aka kdanilov652cd802015-04-13 12:21:07 +0300119
120
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300121def create_image(nova, name, url):
122 pass
123
124
125def create_flavor(nova, name, **params):
126 pass
127
128
129def create_keypair(nova, name, pub_key_path, priv_key_path):
130 try:
131 nova.keypairs.find(name=name)
132 except NotFound:
133 if os.path.exists(pub_key_path):
134 with open(pub_key_path) as pub_key_fd:
135 return nova.keypairs.create(name, pub_key_fd.read())
136 else:
137 key = nova.keypairs.create(name)
138
139 with open(priv_key_path, "w") as priv_key_fd:
140 priv_key_fd.write(key.private_key)
141
142 with open(pub_key_path, "w") as pub_key_fd:
143 pub_key_fd.write(key.public_key)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800144
145
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800146def create_volume(size, name):
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800147 cinder = c_client(*ostack_get_creds())
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800148 vol = cinder.volumes.create(size=size, display_name=name)
149 err_count = 0
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300150
151 while vol['status'] != 'available':
152 if vol['status'] == 'error':
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800153 if err_count == 3:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800154 logger.critical("Fail to create volume")
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800155 raise RuntimeError("Fail to create volume")
156 else:
157 err_count += 1
158 cinder.volumes.delete(vol)
159 time.sleep(1)
160 vol = cinder.volumes.create(size=size, display_name=name)
161 continue
162 time.sleep(1)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300163 vol = cinder.volumes.get(vol['id'])
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800164 return vol
165
166
167def wait_for_server_active(nova, server, timeout=240):
168 t = time.time()
169 while True:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800170 time.sleep(1)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800171 sstate = getattr(server, 'OS-EXT-STS:vm_state').lower()
172
173 if sstate == 'active':
174 return True
175
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800176 if sstate == 'error':
177 return False
178
179 if time.time() - t > timeout:
180 return False
181
182 server = nova.servers.get(server)
183
184
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800185class Allocate(object):
186 pass
187
188
189def get_floating_ips(nova, pool, amount):
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800190 ip_list = nova.floating_ips.list()
191
192 if pool is not None:
193 ip_list = [ip for ip in ip_list if ip.pool == pool]
194
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800195 return [ip for ip in ip_list if ip.instance_id is None][:amount]
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800196
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800197
koder aka kdanilovcee43342015-04-14 22:52:53 +0300198def launch_vms(params):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300199 logger.debug("Starting new nodes on openstack")
koder aka kdanilovcee43342015-04-14 22:52:53 +0300200 params = params.copy()
koder aka kdanilovda45e882015-04-06 02:24:42 +0300201 count = params.pop('count')
202
203 if isinstance(count, basestring):
204 assert count.startswith("x")
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300205 lst = NOVA_CONNECTION.services.list(binary='nova-compute')
koder aka kdanilovda45e882015-04-06 02:24:42 +0300206 srv_count = len([srv for srv in lst if srv.status == 'enabled'])
207 count = srv_count * int(count[1:])
208
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300209 srv_params = "img: {image[name]}, flavor: {flavor[name]}".format(**params)
koder aka kdanilov66839a92015-04-11 13:22:31 +0300210 msg_templ = "Will start {0} servers with next params: {1}"
koder aka kdanilovcee43342015-04-14 22:52:53 +0300211 logger.info(msg_templ.format(count, srv_params))
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300212 vm_creds = params.pop('creds')
koder aka kdanilovda45e882015-04-06 02:24:42 +0300213
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300214 params = params.copy()
215
216 params['img_name'] = params['image']['name']
217 params['flavor_name'] = params['flavor']['name']
218
219 del params['image']
220 del params['flavor']
221 del params['scheduler_group_name']
222 private_key_path = params.pop('private_key_path')
223
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300224 for ip, os_node in create_vms_mt(NOVA_CONNECTION, count, **params):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300225 conn_uri = vm_creds.format(ip=ip, private_key_path=private_key_path)
226 yield Node(conn_uri, []), os_node.id
koder aka kdanilovda45e882015-04-06 02:24:42 +0300227
228
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300229def create_vms_mt(nova, amount, group_name, keypair_name, img_name,
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800230 flavor_name, vol_sz=None, network_zone_name=None,
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300231 flt_ip_pool=None, name_templ='wally-{id}',
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300232 scheduler_hints=None, security_group=None):
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800233
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800234 with ThreadPoolExecutor(max_workers=16) as executor:
koder aka kdanilov97644f92015-02-13 11:11:08 -0800235 if network_zone_name is not None:
236 network_future = executor.submit(nova.networks.find,
237 label=network_zone_name)
238 else:
239 network_future = None
240
241 fl_future = executor.submit(nova.flavors.find, name=flavor_name)
242 img_future = executor.submit(nova.images.find, name=img_name)
243
244 if flt_ip_pool is not None:
245 ips_future = executor.submit(get_floating_ips,
246 nova, flt_ip_pool, amount)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800247 logger.debug("Wait for floating ip")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800248 ips = ips_future.result()
249 ips += [Allocate] * (amount - len(ips))
250 else:
251 ips = [None] * amount
252
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800253 logger.debug("Getting flavor object")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800254 fl = fl_future.result()
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800255 logger.debug("Getting image object")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800256 img = img_future.result()
257
258 if network_future is not None:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800259 logger.debug("Waiting for network results")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800260 nics = [{'net-id': network_future.result().id}]
261 else:
262 nics = None
263
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300264 names = []
265 for i in range(amount):
266 names.append(name_templ.format(group=group_name, id=i))
koder aka kdanilov97644f92015-02-13 11:11:08 -0800267
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800268 futures = []
koder aka kdanilov168f6092015-04-19 02:33:38 +0300269 logger.debug("Requesting new vm's")
koder aka kdanilov6e2ae792015-03-04 18:02:24 -0800270
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800271 for name, flt_ip in zip(names, ips):
272 params = (nova, name, keypair_name, img, fl,
273 nics, vol_sz, flt_ip, scheduler_hints,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300274 flt_ip_pool, [security_group])
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800275
276 futures.append(executor.submit(create_vm, *params))
koder aka kdanilove21d7472015-02-14 19:02:04 -0800277 res = [future.result() for future in futures]
278 logger.debug("Done spawning")
279 return res
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800280
281
282def create_vm(nova, name, keypair_name, img,
283 fl, nics, vol_sz=None,
284 flt_ip=False,
285 scheduler_hints=None,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300286 pool=None,
287 security_groups=None):
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800288 for i in range(3):
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800289 srv = nova.servers.create(name,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300290 flavor=fl,
291 image=img,
292 nics=nics,
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800293 key_name=keypair_name,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300294 scheduler_hints=scheduler_hints,
295 security_groups=security_groups)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800296
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800297 if not wait_for_server_active(nova, srv):
298 msg = "Server {0} fails to start. Kill it and try again"
koder aka kdanilove21d7472015-02-14 19:02:04 -0800299 logger.debug(msg.format(srv))
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800300 nova.servers.delete(srv)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800301
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300302 for j in range(120):
koder aka kdanilov3f356262015-02-13 08:06:14 -0800303 # print "wait till server deleted"
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800304 all_id = set(alive_srv.id for alive_srv in nova.servers.list())
305 if srv.id not in all_id:
306 break
307 time.sleep(1)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300308 else:
309 raise RuntimeError("Server {0} delete timeout".format(srv.id))
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800310 else:
311 break
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300312 else:
313 raise RuntimeError("Failed to start server".format(srv.id))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800314
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800315 if vol_sz is not None:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800316 # print "creating volume"
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800317 vol = create_volume(vol_sz, name)
koder aka kdanilov3f356262015-02-13 08:06:14 -0800318 # print "attach volume to server"
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300319 nova.volumes.create_server_volume(srv.id, vol['id'], None)
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800320
321 if flt_ip is Allocate:
322 flt_ip = nova.floating_ips.create(pool)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300323
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800324 if flt_ip is not None:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800325 # print "attaching ip to server"
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800326 srv.add_floating_ip(flt_ip)
Yulia Portnova0e64ea22015-03-20 17:27:22 +0200327
koder aka kdanilovda45e882015-04-06 02:24:42 +0300328 return flt_ip.ip, nova.servers.get(srv.id)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800329
330
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300331def clear_nodes(nodes_ids):
332 clear_all(NOVA_CONNECTION, nodes_ids, None)
gstepanov023c1e42015-04-08 15:50:19 +0300333
334
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300335def clear_all(nova, ids=None, name_templ="ceph-test-{0}"):
336
337 def need_delete(srv):
338 if name_templ is not None:
339 return re.match(name_templ.format("\\d+"), srv.name) is not None
340 else:
341 return srv.id in ids
342
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800343 deleted_srvs = set()
344 for srv in nova.servers.list():
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300345 if need_delete(srv):
koder aka kdanilove21d7472015-02-14 19:02:04 -0800346 logger.debug("Deleting server {0}".format(srv.name))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800347 nova.servers.delete(srv)
348 deleted_srvs.add(srv.id)
349
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300350 count = 0
351 while True:
352 if count % 60 == 0:
353 logger.debug("Waiting till all servers are actually deleted")
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800354 all_id = set(srv.id for srv in nova.servers.list())
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300355 if len(all_id.intersection(deleted_srvs)) == 0:
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800356 break
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300357 count += 1
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800358 time.sleep(1)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300359 logger.debug("Done, deleting volumes")
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800360
361 # wait till vm actually deleted
362
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300363 if name_templ is not None:
364 cinder = c_client(*ostack_get_creds())
365 for vol in cinder.volumes.list():
366 if isinstance(vol.display_name, basestring):
367 if re.match(name_templ.format("\\d+"), vol.display_name):
368 if vol.status in ('available', 'error'):
369 logger.debug("Deleting volume " + vol.display_name)
370 cinder.volumes.delete(vol)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800371
koder aka kdanilove21d7472015-02-14 19:02:04 -0800372 logger.debug("Clearing done (yet some volumes may still deleting)")