blob: 8556b6a990e12d22da709d07b2714c45f42426db [file] [log] [blame]
koder aka kdanilov4643fd62015-02-10 16:20:13 -08001import re
2import os
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +03003import stat
koder aka kdanilov4643fd62015-02-10 16:20:13 -08004import time
koder aka kdanilova94dfe12015-08-19 13:04:51 +03005import urllib
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03006import os.path
koder aka kdanilove21d7472015-02-14 19:02:04 -08007import logging
koder aka kdanilov76471642015-08-14 11:44:43 +03008import warnings
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +03009import subprocess
koder aka kdanilovb7197432015-07-15 00:40:43 +030010import collections
koder aka kdanilov4643fd62015-02-10 16:20:13 -080011
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +030012from concurrent.futures import ThreadPoolExecutor
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080013
koder aka kdanilov4500a5f2015-04-17 16:55:17 +030014from novaclient.exceptions import NotFound
koder aka kdanilov4643fd62015-02-10 16:20:13 -080015from novaclient.client import Client as n_client
16from cinderclient.v1.client import Client as c_client
17
koder aka kdanilova94dfe12015-08-19 13:04:51 +030018__doc__ = """
19Module used to reliably spawn set of VM's, evenly distributed across
20openstack cluster. Main functions:
21
22 get_OS_credentials - extract openstack credentials from different sources
23 nova_connect - connect to nova api
24 cinder_connect - connect to cinder api
25 find - find VM with given prefix in name
26 prepare_OS - prepare tenant for usage
27 launch_vms - reliably start set of VM in parallel with volumes and floating IP
28 clear_all - clear VM and volumes
29"""
30
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030031import wally
32from wally.discover import Node
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030033
koder aka kdanilov4643fd62015-02-10 16:20:13 -080034
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030035logger = logging.getLogger("wally.vms")
koder aka kdanilove21d7472015-02-14 19:02:04 -080036
37
koder aka kdanilove87ae652015-04-20 02:14:35 +030038STORED_OPENSTACK_CREDS = None
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030039NOVA_CONNECTION = None
koder aka kdanilove87ae652015-04-20 02:14:35 +030040CINDER_CONNECTION = None
41
42
koder aka kdanilov416b87a2015-05-12 00:26:04 +030043def is_connected():
44 return NOVA_CONNECTION is not None
45
46
koder aka kdanilovb7197432015-07-15 00:40:43 +030047OSCreds = collections.namedtuple("OSCreds",
48 ["name", "passwd",
49 "tenant", "auth_url", "insecure"])
50
51
koder aka kdanilove87ae652015-04-20 02:14:35 +030052def ostack_get_creds():
53 if STORED_OPENSTACK_CREDS is None:
koder aka kdanilovb7197432015-07-15 00:40:43 +030054 return OSCreds(os.environ.get('OS_USERNAME'),
55 os.environ.get('OS_PASSWORD'),
56 os.environ.get('OS_TENANT_NAME'),
57 os.environ.get('OS_AUTH_URL'),
58 os.environ.get('OS_INSECURE', False))
koder aka kdanilove87ae652015-04-20 02:14:35 +030059 else:
60 return STORED_OPENSTACK_CREDS
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030061
62
koder aka kdanilovb7197432015-07-15 00:40:43 +030063def nova_connect(os_creds=None):
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030064 global NOVA_CONNECTION
koder aka kdanilove87ae652015-04-20 02:14:35 +030065 global STORED_OPENSTACK_CREDS
66
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030067 if NOVA_CONNECTION is None:
koder aka kdanilovb7197432015-07-15 00:40:43 +030068 if os_creds is None:
69 os_creds = ostack_get_creds()
koder aka kdanilove87ae652015-04-20 02:14:35 +030070 else:
koder aka kdanilovb7197432015-07-15 00:40:43 +030071 STORED_OPENSTACK_CREDS = os_creds
koder aka kdanilove87ae652015-04-20 02:14:35 +030072
koder aka kdanilovb7197432015-07-15 00:40:43 +030073 NOVA_CONNECTION = n_client('1.1',
74 os_creds.name,
75 os_creds.passwd,
76 os_creds.tenant,
77 os_creds.auth_url,
78 insecure=os_creds.insecure)
koder aka kdanilov1c2b5112015-04-10 16:53:51 +030079 return NOVA_CONNECTION
80
81
koder aka kdanilovb7197432015-07-15 00:40:43 +030082def cinder_connect(os_creds=None):
koder aka kdanilove87ae652015-04-20 02:14:35 +030083 global CINDER_CONNECTION
84 global STORED_OPENSTACK_CREDS
85
86 if CINDER_CONNECTION is None:
koder aka kdanilovb7197432015-07-15 00:40:43 +030087 if os_creds is None:
88 os_creds = ostack_get_creds()
koder aka kdanilove87ae652015-04-20 02:14:35 +030089 else:
koder aka kdanilovb7197432015-07-15 00:40:43 +030090 STORED_OPENSTACK_CREDS = os_creds
91 CINDER_CONNECTION = c_client(os_creds.name,
92 os_creds.passwd,
93 os_creds.tenant,
94 os_creds.auth_url,
95 insecure=os_creds.insecure)
koder aka kdanilove87ae652015-04-20 02:14:35 +030096 return CINDER_CONNECTION
97
98
koder aka kdanilova94dfe12015-08-19 13:04:51 +030099def prepare_os_subpr(nova, params, os_creds, max_vm_per_compute=8):
koder aka kdanilovb7197432015-07-15 00:40:43 +0300100 if os_creds is None:
101 os_creds = ostack_get_creds()
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300102
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300103 serv_groups = " ".join(map(params['aa_group_name'].format,
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300104 range(max_vm_per_compute)))
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300105
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300106 image_name = params['image']['name']
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300107 env = os.environ.copy()
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300108
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300109 env.update(dict(
koder aka kdanilovb7197432015-07-15 00:40:43 +0300110 OS_USERNAME=os_creds.name,
111 OS_PASSWORD=os_creds.passwd,
112 OS_TENANT_NAME=os_creds.tenant,
113 OS_AUTH_URL=os_creds.auth_url,
114 OS_INSECURE="1" if os_creds.insecure else "0",
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300115
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300116 FLAVOR_NAME=params['flavor']['name'],
117 FLAVOR_RAM=str(params['flavor']['ram_size']),
118 FLAVOR_HDD=str(params['flavor']['hdd_size']),
119 FLAVOR_CPU_COUNT=str(params['flavor']['cpu_count']),
120
121 SERV_GROUPS=serv_groups,
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300122
123 SECGROUP=params['security_group'],
124
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300125 IMAGE_NAME=image_name,
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300126 IMAGE_URL=params['image']['url'],
127 ))
128
129 spath = os.path.dirname(os.path.dirname(wally.__file__))
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300130 spath = os.path.join(spath, 'scripts/prepare.sh')
131
koder aka kdanilov76471642015-08-14 11:44:43 +0300132 with warnings.catch_warnings():
133 warnings.simplefilter("ignore")
134 fname = os.tempnam()
135
136 cmd = "bash {spath} >{fname} 2>&1".format(spath=spath, fname=fname)
137 try:
138 subprocess.check_call(cmd, shell=True, env=env)
139 except:
140 logger.error("Prepare failed. Logs in " + fname)
141 with open(fname) as fd:
142 logger.error("Message:\n " + fd.read().replace("\n", "\n "))
143 raise
144 os.unlink(fname)
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300145
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300146 while True:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300147 status = nova.images.find(name=image_name).status
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300148 if status == 'ACTIVE':
149 break
150 msg = "Image {0} is still in {1} state. Waiting 10 more seconds"
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300151 logger.info(msg.format(image_name, status))
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300152 time.sleep(10)
153
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300154 create_keypair(nova,
155 params['keypair_name'],
156 params['keypair_file_public'],
157 params['keypair_file_private'])
158
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300159
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300160def find_vms(nova, name_prefix):
161 for srv in nova.servers.list():
162 if srv.name.startswith(name_prefix):
163 for ips in srv.addresses.values():
164 for ip in ips:
165 if ip.get("OS-EXT-IPS:type", None) == 'floating':
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300166 yield ip['addr'], srv.id
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300167 break
168
169
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300170def pause(ids):
171 def pause_vm(conn, vm_id):
172 vm = conn.servers.get(vm_id)
173 if vm.status == 'ACTIVE':
174 vm.pause()
175
176 conn = nova_connect()
177 with ThreadPoolExecutor(max_workers=16) as executor:
178 futures = [executor.submit(pause_vm, conn, vm_id)
179 for vm_id in ids]
180 for future in futures:
181 future.result()
182
183
184def unpause(ids, max_resume_time=10):
185 def unpause(conn, vm_id):
186 vm = conn.servers.get(vm_id)
187 if vm.status == 'PAUSED':
188 vm.unpause()
189
190 for i in range(max_resume_time * 10):
191 vm = conn.servers.get(vm_id)
192 if vm.status != 'PAUSED':
193 return
194 time.sleep(0.1)
195 raise RuntimeError("Can't unpause vm {0}".format(vm_id))
196
197 conn = nova_connect()
198 with ThreadPoolExecutor(max_workers=16) as executor:
199 futures = [executor.submit(unpause, conn, vm_id)
200 for vm_id in ids]
201
202 for future in futures:
203 future.result()
204
205
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300206def prepare_os(nova, params, max_vm_per_compute=8):
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300207 allow_ssh(nova, params['security_group'])
208
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300209 MAX_VM_PER_NODE = 8
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300210 serv_groups = map(params['aa_group_name'].format,
211 range(MAX_VM_PER_NODE))
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300212
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300213 for serv_groups in serv_groups:
214 get_or_create_aa_group(nova, serv_groups)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300215
216 create_keypair(nova,
217 params['keypair_name'],
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300218 params['keypair_file_public'],
219 params['keypair_file_private'])
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300220
221 create_image(nova, params['image']['name'],
222 params['image']['url'])
223
224 create_flavor(nova, **params['flavor'])
koder aka kdanilov4e9f3ed2015-04-14 11:26:12 +0300225
226
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300227def create_keypair(nova, name, pub_key_path, priv_key_path):
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300228 """create and upload keypair into nova, if doesn't exists yet
229
230 Create and upload keypair into nova, if keypair with given bane
231 doesn't exists yet. Uses key from files, if file doesn't exists -
232 create new keys, and store'em into files.
233
234 parameters:
235 nova: nova connection
236 name: str - ketpair name
237 pub_key_path: str - path for public key
238 priv_key_path: str - path for private key
239
240 returns: None
241 """
242
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300243 pub_key_exists = os.path.exists(pub_key_path)
244 priv_key_exists = os.path.exists(priv_key_path)
245
246 try:
247 kpair = nova.keypairs.find(name=name)
248 # if file not found- delete and recreate
249 except NotFound:
250 kpair = None
251
252 if pub_key_exists and not priv_key_exists:
253 raise EnvironmentError("Private key file doesn't exists")
254
255 if not pub_key_exists and priv_key_exists:
256 raise EnvironmentError("Public key file doesn't exists")
257
258 if kpair is None:
259 if pub_key_exists:
260 with open(pub_key_path) as pub_key_fd:
261 return nova.keypairs.create(name, pub_key_fd.read())
262 else:
263 key = nova.keypairs.create(name)
264
265 with open(priv_key_path, "w") as priv_key_fd:
266 priv_key_fd.write(key.private_key)
267 os.chmod(priv_key_path, stat.S_IREAD | stat.S_IWRITE)
268
269 with open(pub_key_path, "w") as pub_key_fd:
270 pub_key_fd.write(key.public_key)
271 elif not priv_key_exists:
272 raise EnvironmentError("Private key file doesn't exists," +
273 " but key uploaded openstack." +
274 " Either set correct path to private key" +
275 " or remove key from openstack")
276
277
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300278def get_or_create_aa_group(nova, name):
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300279 """create anti-affinity server group, if doesn't exists yet
280
281 parameters:
282 nova: nova connection
283 name: str - group name
284
285 returns: str - group id
286 """
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300287 try:
288 group = nova.server_groups.find(name=name)
289 except NotFound:
290 group = nova.server_groups.create({'name': name,
291 'policies': ['anti-affinity']})
koder aka kdanilov652cd802015-04-13 12:21:07 +0300292
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300293 return group.id
koder aka kdanilov652cd802015-04-13 12:21:07 +0300294
295
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300296def allow_ssh(nova, group_name):
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300297 """create sequrity group for ping and ssh
298
299 parameters:
300 nova: nova connection
301 group_name: str - group name
302
303 returns: str - group id
304 """
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300305 try:
306 secgroup = nova.security_groups.find(name=group_name)
307 except NotFound:
308 secgroup = nova.security_groups.create(group_name,
309 "allow ssh/ping to node")
310
koder aka kdanilov652cd802015-04-13 12:21:07 +0300311 nova.security_group_rules.create(secgroup.id,
312 ip_protocol="tcp",
313 from_port="22",
314 to_port="22",
315 cidr="0.0.0.0/0")
316
317 nova.security_group_rules.create(secgroup.id,
318 ip_protocol="icmp",
319 from_port=-1,
320 cidr="0.0.0.0/0",
321 to_port=-1)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300322 return secgroup.id
koder aka kdanilov652cd802015-04-13 12:21:07 +0300323
324
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300325def create_image(nova, os_creds, name, url):
326 """upload image into glance from given URL, if given image doesn't exisis yet
327
328 parameters:
329 nova: nova connection
330 os_creds: OSCreds object - openstack credentials, should be same,
331 as used when connectiong given novaclient
332 name: str - image name
333 url: str - image download url
334
335 returns: None
336 """
337 try:
338 nova.images.find(name=name)
339 return
340 except NotFound:
341 pass
342
343 tempnam = os.tempnam()
344
345 try:
346 urllib.urlretrieve(url, tempnam)
347
348 cmd = "OS_USERNAME={0.name}"
349 cmd += " OS_PASSWORD={0.passwd}"
350 cmd += " OS_TENANT_NAME={0.tenant}"
351 cmd += " OS_AUTH_URL={0.auth_url}"
352 cmd += " glance {1} image-create --name {2} $opts --file {3}"
353 cmd += " --disk-format qcow2 --container-format bare --is-public true"
354
355 cmd = cmd.format(os_creds,
356 '--insecure' if os_creds.insecure else "",
357 name,
358 tempnam)
359 finally:
360 if os.path.exists(tempnam):
361 os.unlink(tempnam)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300362
363
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300364def create_flavor(nova, name, ram_size, hdd_size, cpu_count):
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300365 """create flavor, if doesn't exisis yet
366
367 parameters:
368 nova: nova connection
369 name: str - flavor name
370 ram_size: int - ram size (UNIT?)
371 hdd_size: int - root hdd size (UNIT?)
372 cpu_count: int - cpu cores
373
374 returns: None
375 """
376 try:
377 nova.flavors.find(name)
378 return
379 except NotFound:
380 pass
381
382 nova.flavors.create(name, cpu_count, ram_size, hdd_size)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300383
384
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800385def create_volume(size, name):
koder aka kdanilove87ae652015-04-20 02:14:35 +0300386 cinder = cinder_connect()
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800387 vol = cinder.volumes.create(size=size, display_name=name)
388 err_count = 0
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300389
koder aka kdanilove87ae652015-04-20 02:14:35 +0300390 while vol.status != 'available':
391 if vol.status == 'error':
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800392 if err_count == 3:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800393 logger.critical("Fail to create volume")
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800394 raise RuntimeError("Fail to create volume")
395 else:
396 err_count += 1
397 cinder.volumes.delete(vol)
398 time.sleep(1)
399 vol = cinder.volumes.create(size=size, display_name=name)
400 continue
401 time.sleep(1)
koder aka kdanilove87ae652015-04-20 02:14:35 +0300402 vol = cinder.volumes.get(vol.id)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800403 return vol
404
405
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300406def wait_for_server_active(nova, server, timeout=300):
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300407 """waiting till server became active
408
409 parameters:
410 nova: nova connection
411 server: server object
412 timeout: int - seconds to wait till raise an exception
413
414 returns: None
415 """
416
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800417 t = time.time()
418 while True:
koder aka kdanilov3f356262015-02-13 08:06:14 -0800419 time.sleep(1)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800420 sstate = getattr(server, 'OS-EXT-STS:vm_state').lower()
421
422 if sstate == 'active':
423 return True
424
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800425 if sstate == 'error':
426 return False
427
428 if time.time() - t > timeout:
429 return False
430
431 server = nova.servers.get(server)
432
433
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800434class Allocate(object):
435 pass
436
437
438def get_floating_ips(nova, pool, amount):
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300439 """allocate flationg ips
440
441 parameters:
442 nova: nova connection
443 pool:str floating ip pool name
444 amount:int - ip count
445
446 returns: [ip object]
447 """
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800448 ip_list = nova.floating_ips.list()
449
450 if pool is not None:
451 ip_list = [ip for ip in ip_list if ip.pool == pool]
452
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800453 return [ip for ip in ip_list if ip.instance_id is None][:amount]
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800454
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800455
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300456def launch_vms(params, already_has_count=0):
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300457 logger.debug("Calculating new vm count")
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300458 count = params['count']
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300459 nova = nova_connect()
460 lst = nova.services.list(binary='nova-compute')
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300461 srv_count = len([srv for srv in lst if srv.status == 'enabled'])
koder aka kdanilovda45e882015-04-06 02:24:42 +0300462
463 if isinstance(count, basestring):
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300464 if count.startswith("x"):
465 count = srv_count * int(count[1:])
466 else:
467 assert count.startswith('=')
468 count = int(count[1:]) - already_has_count
469
470 if count <= 0:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300471 logger.debug("Not need new vms")
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300472 return
koder aka kdanilovda45e882015-04-06 02:24:42 +0300473
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300474 logger.debug("Starting new nodes on openstack")
475
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300476 assert isinstance(count, (int, long))
477
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300478 srv_params = "img: {image[name]}, flavor: {flavor[name]}".format(**params)
koder aka kdanilov66839a92015-04-11 13:22:31 +0300479 msg_templ = "Will start {0} servers with next params: {1}"
koder aka kdanilovcee43342015-04-14 22:52:53 +0300480 logger.info(msg_templ.format(count, srv_params))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300481
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300482 vm_params = dict(
483 img_name=params['image']['name'],
484 flavor_name=params['flavor']['name'],
485 group_name=params['group_name'],
486 keypair_name=params['keypair_name'],
487 vol_sz=params.get('vol_sz'),
488 network_zone_name=params.get("network_zone_name"),
489 flt_ip_pool=params.get('flt_ip_pool'),
490 name_templ=params.get('name_templ'),
491 scheduler_hints={"group": params['aa_group_name']},
492 security_group=params['security_group'],
493 sec_group_size=srv_count
494 )
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300495
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300496 # precache all errors before start creating vms
497 private_key_path = params['keypair_file_private']
498 creds = params['image']['creds']
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300499
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300500 for ip, os_node in create_vms_mt(NOVA_CONNECTION, count, **vm_params):
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300501 conn_uri = creds.format(ip=ip, private_key_path=private_key_path)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300502 yield Node(conn_uri, []), os_node.id
koder aka kdanilovda45e882015-04-06 02:24:42 +0300503
504
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300505def get_free_server_grpoups(nova, template=None):
506 for g in nova.server_groups.list():
507 if g.members == []:
508 if re.match(template, g.name):
Michael Semenov8ba6e232015-08-28 10:57:18 +0000509 yield str(g.id)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300510
511
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300512def create_vms_mt(nova, amount, group_name, keypair_name, img_name,
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800513 flavor_name, vol_sz=None, network_zone_name=None,
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300514 flt_ip_pool=None, name_templ='wally-{id}',
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300515 scheduler_hints=None, security_group=None,
516 sec_group_size=None):
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800517
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800518 with ThreadPoolExecutor(max_workers=16) as executor:
koder aka kdanilov97644f92015-02-13 11:11:08 -0800519 if network_zone_name is not None:
520 network_future = executor.submit(nova.networks.find,
521 label=network_zone_name)
522 else:
523 network_future = None
524
525 fl_future = executor.submit(nova.flavors.find, name=flavor_name)
526 img_future = executor.submit(nova.images.find, name=img_name)
527
528 if flt_ip_pool is not None:
529 ips_future = executor.submit(get_floating_ips,
530 nova, flt_ip_pool, amount)
koder aka kdanilove21d7472015-02-14 19:02:04 -0800531 logger.debug("Wait for floating ip")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800532 ips = ips_future.result()
533 ips += [Allocate] * (amount - len(ips))
534 else:
535 ips = [None] * amount
536
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800537 logger.debug("Getting flavor object")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800538 fl = fl_future.result()
koder aka kdanilov7dec9df2015-02-15 21:35:19 -0800539 logger.debug("Getting image object")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800540 img = img_future.result()
541
542 if network_future is not None:
koder aka kdanilove21d7472015-02-14 19:02:04 -0800543 logger.debug("Waiting for network results")
koder aka kdanilov97644f92015-02-13 11:11:08 -0800544 nics = [{'net-id': network_future.result().id}]
545 else:
546 nics = None
547
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300548 names = []
549 for i in range(amount):
550 names.append(name_templ.format(group=group_name, id=i))
koder aka kdanilov97644f92015-02-13 11:11:08 -0800551
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800552 futures = []
koder aka kdanilov168f6092015-04-19 02:33:38 +0300553 logger.debug("Requesting new vm's")
koder aka kdanilov6e2ae792015-03-04 18:02:24 -0800554
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300555 orig_scheduler_hints = scheduler_hints.copy()
556
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300557 MAX_SHED_GROUPS = 32
558 for start_idx in range(MAX_SHED_GROUPS):
559 pass
560
561 group_name_template = scheduler_hints['group'].format("\\d+")
562 groups = list(get_free_server_grpoups(nova, group_name_template + "$"))
563 groups.sort()
564
565 for idx, (name, flt_ip) in enumerate(zip(names, ips), 2):
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300566
567 scheduler_hints = None
568 if orig_scheduler_hints is not None and sec_group_size is not None:
569 if "group" in orig_scheduler_hints:
570 scheduler_hints = orig_scheduler_hints.copy()
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300571 scheduler_hints['group'] = groups[idx // sec_group_size]
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300572
573 if scheduler_hints is None:
574 scheduler_hints = orig_scheduler_hints.copy()
575
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800576 params = (nova, name, keypair_name, img, fl,
577 nics, vol_sz, flt_ip, scheduler_hints,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300578 flt_ip_pool, [security_group])
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800579
580 futures.append(executor.submit(create_vm, *params))
koder aka kdanilove21d7472015-02-14 19:02:04 -0800581 res = [future.result() for future in futures]
582 logger.debug("Done spawning")
583 return res
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800584
585
586def create_vm(nova, name, keypair_name, img,
587 fl, nics, vol_sz=None,
588 flt_ip=False,
589 scheduler_hints=None,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300590 pool=None,
591 security_groups=None):
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800592 for i in range(3):
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800593 srv = nova.servers.create(name,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300594 flavor=fl,
595 image=img,
596 nics=nics,
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800597 key_name=keypair_name,
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300598 scheduler_hints=scheduler_hints,
599 security_groups=security_groups)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800600
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800601 if not wait_for_server_active(nova, srv):
602 msg = "Server {0} fails to start. Kill it and try again"
koder aka kdanilove21d7472015-02-14 19:02:04 -0800603 logger.debug(msg.format(srv))
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800604 nova.servers.delete(srv)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800605
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300606 try:
607 for j in range(120):
608 srv = nova.servers.get(srv.id)
609 time.sleep(1)
610 else:
611 msg = "Server {0} delete timeout".format(srv.id)
612 raise RuntimeError(msg)
613 except NotFound:
614 pass
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800615 else:
616 break
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300617 else:
618 raise RuntimeError("Failed to start server".format(srv.id))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800619
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800620 if vol_sz is not None:
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800621 vol = create_volume(vol_sz, name)
koder aka kdanilove87ae652015-04-20 02:14:35 +0300622 nova.volumes.create_server_volume(srv.id, vol.id, None)
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800623
624 if flt_ip is Allocate:
625 flt_ip = nova.floating_ips.create(pool)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300626
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800627 if flt_ip is not None:
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800628 srv.add_floating_ip(flt_ip)
Yulia Portnova0e64ea22015-03-20 17:27:22 +0200629
koder aka kdanilovda45e882015-04-06 02:24:42 +0300630 return flt_ip.ip, nova.servers.get(srv.id)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800631
632
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300633def clear_nodes(nodes_ids):
634 clear_all(NOVA_CONNECTION, nodes_ids, None)
gstepanov023c1e42015-04-08 15:50:19 +0300635
636
koder aka kdanilove87ae652015-04-20 02:14:35 +0300637def clear_all(nova, ids=None, name_templ=None):
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300638
639 def need_delete(srv):
640 if name_templ is not None:
641 return re.match(name_templ.format("\\d+"), srv.name) is not None
642 else:
643 return srv.id in ids
644
koder aka kdanilove87ae652015-04-20 02:14:35 +0300645 volumes_to_delete = []
646 cinder = cinder_connect()
647 for vol in cinder.volumes.list():
648 for attachment in vol.attachments:
649 if attachment['server_id'] in ids:
650 volumes_to_delete.append(vol)
651 break
652
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800653 deleted_srvs = set()
654 for srv in nova.servers.list():
koder aka kdanilov1c2b5112015-04-10 16:53:51 +0300655 if need_delete(srv):
koder aka kdanilove21d7472015-02-14 19:02:04 -0800656 logger.debug("Deleting server {0}".format(srv.name))
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800657 nova.servers.delete(srv)
658 deleted_srvs.add(srv.id)
659
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300660 count = 0
661 while True:
662 if count % 60 == 0:
663 logger.debug("Waiting till all servers are actually deleted")
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800664 all_id = set(srv.id for srv in nova.servers.list())
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300665 if len(all_id.intersection(deleted_srvs)) == 0:
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800666 break
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300667 count += 1
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800668 time.sleep(1)
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300669 logger.debug("Done, deleting volumes")
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800670
671 # wait till vm actually deleted
672
koder aka kdanilov6b1341a2015-04-21 22:44:21 +0300673 # logger.warning("Volume deletion commented out")
674 for vol in volumes_to_delete:
675 logger.debug("Deleting volume " + vol.display_name)
676 cinder.volumes.delete(vol)
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800677
koder aka kdanilove21d7472015-02-14 19:02:04 -0800678 logger.debug("Clearing done (yet some volumes may still deleting)")