/
ops.py
239 lines (217 loc) · 8.38 KB
/
ops.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import logging
import json
import os
import requests
import teuthology.provision
from teuthology import misc
from teuthology.config import config
from teuthology.contextutil import safe_while
import util
import keys
log = logging.getLogger(__name__)
def lock_many_openstack(ctx, num, machine_type, user=None, description=None,
arch=None):
os_type = teuthology.provision.get_distro(ctx)
os_version = teuthology.provision.get_distro_version(ctx)
if hasattr(ctx, 'config'):
resources_hint = ctx.config.get('openstack')
else:
resources_hint = None
machines = teuthology.provision.openstack.ProvisionOpenStack().create(
num, os_type, os_version, arch, resources_hint)
result = {}
for machine in machines:
lock_one(machine, user, description)
result[machine] = None # we do not collect ssh host keys yet
return result
def lock_many(ctx, num, machine_type, user=None, description=None,
os_type=None, os_version=None, arch=None):
if user is None:
user = misc.get_user()
if not util.vps_version_or_type_valid(
ctx.machine_type,
os_type,
os_version
):
log.error('Invalid os-type or version detected -- lock failed')
return
# In the for loop below we can safely query for all bare-metal machine_type
# values at once. So, if we're being asked for 'plana,mira,burnupi', do it
# all in one shot. If we are passed 'plana,mira,burnupi,vps', do one query
# for 'plana,mira,burnupi' and one for 'vps'
machine_types_list = misc.get_multi_machine_types(machine_type)
if machine_types_list == ['vps']:
machine_types = machine_types_list
elif machine_types_list == ['openstack']:
return lock_many_openstack(ctx, num, machine_type,
user=user,
description=description,
arch=arch)
elif 'vps' in machine_types_list:
machine_types_non_vps = list(machine_types_list)
machine_types_non_vps.remove('vps')
machine_types_non_vps = '|'.join(machine_types_non_vps)
machine_types = [machine_types_non_vps, 'vps']
else:
machine_types_str = '|'.join(machine_types_list)
machine_types = [machine_types_str, ]
for machine_type in machine_types:
uri = os.path.join(config.lock_server, 'nodes', 'lock_many', '')
data = dict(
locked_by=user,
count=num,
machine_type=machine_type,
description=description,
)
# Only query for os_type/os_version if non-vps and non-libcloud, since
# in that case we just create them.
vm_types = ['vps'] + teuthology.provision.cloud.get_types()
if machine_type not in vm_types:
if os_type:
data['os_type'] = os_type
if os_version:
data['os_version'] = os_version
if arch:
data['arch'] = arch
log.debug("lock_many request: %s", repr(data))
response = requests.post(
uri,
data=json.dumps(data),
headers={'content-type': 'application/json'},
)
if response.ok:
machines = {misc.canonicalize_hostname(machine['name']):
machine['ssh_pub_key'] for machine in response.json()}
log.debug('locked {machines}'.format(
machines=', '.join(machines.keys())))
if machine_type in vm_types:
ok_machs = {}
for machine in machines:
if teuthology.provision.create_if_vm(ctx, machine):
ok_machs[machine] = machines[machine]
else:
log.error('Unable to create virtual machine: %s',
machine)
unlock_one(ctx, machine, user)
ok_machs = keys.do_update_keys(ok_machs.keys())[1]
return ok_machs
return machines
elif response.status_code == 503:
log.error('Insufficient nodes available to lock %d %s nodes.',
num, machine_type)
log.error(response.text)
else:
log.error('Could not lock %d %s nodes, reason: unknown.',
num, machine_type)
return []
def lock_one(name, user=None, description=None):
name = misc.canonicalize_hostname(name, user=None)
if user is None:
user = misc.get_user()
request = dict(name=name, locked=True, locked_by=user,
description=description)
uri = os.path.join(config.lock_server, 'nodes', name, 'lock', '')
response = requests.put(uri, json.dumps(request))
success = response.ok
if success:
log.debug('locked %s as %s', name, user)
else:
try:
reason = response.json().get('message')
except ValueError:
reason = str(response.status_code)
log.error('failed to lock {node}. reason: {reason}'.format(
node=name, reason=reason))
return response
def unlock_many(names, user):
fixed_names = [misc.canonicalize_hostname(name, user=None) for name in
names]
names = fixed_names
uri = os.path.join(config.lock_server, 'nodes', 'unlock_many', '')
data = dict(
locked_by=user,
names=names,
)
response = requests.post(
uri,
data=json.dumps(data),
headers={'content-type': 'application/json'},
)
if response.ok:
log.debug("Unlocked: %s", ', '.join(names))
else:
log.error("Failed to unlock: %s", ', '.join(names))
return response.ok
def unlock_one(ctx, name, user, description=None):
name = misc.canonicalize_hostname(name, user=None)
if not teuthology.provision.destroy_if_vm(ctx, name, user, description):
log.error('destroy failed for %s', name)
return False
request = dict(name=name, locked=False, locked_by=user,
description=description)
uri = os.path.join(config.lock_server, 'nodes', name, 'lock', '')
with safe_while(
sleep=1, increment=0.5, action="unlock %s" % name) as proceed:
while proceed():
try:
response = requests.put(uri, json.dumps(request))
break
# Work around https://github.com/kennethreitz/requests/issues/2364
except requests.ConnectionError as e:
log.warn("Saw %s while unlocking; retrying...", str(e))
success = response.ok
if success:
log.info('unlocked %s', name)
else:
try:
reason = response.json().get('message')
except ValueError:
reason = str(response.status_code)
log.error('failed to unlock {node}. reason: {reason}'.format(
node=name, reason=reason))
return success
def update_lock(name, description=None, status=None, ssh_pub_key=None):
name = misc.canonicalize_hostname(name, user=None)
updated = {}
if description is not None:
updated['description'] = description
if status is not None:
updated['up'] = (status == 'up')
if ssh_pub_key is not None:
updated['ssh_pub_key'] = ssh_pub_key
if updated:
uri = os.path.join(config.lock_server, 'nodes', name, '')
response = requests.put(
uri,
json.dumps(updated))
return response.ok
return True
def update_inventory(node_dict):
"""
Like update_lock(), but takes a dict and doesn't try to do anything smart
by itself
"""
name = node_dict.get('name')
if not name:
raise ValueError("must specify name")
if not config.lock_server:
return
uri = os.path.join(config.lock_server, 'nodes', name, '')
log.info("Updating %s on lock server", name)
response = requests.put(
uri,
json.dumps(node_dict),
headers={'content-type': 'application/json'},
)
if response.status_code == 404:
log.info("Creating new node %s on lock server", name)
uri = os.path.join(config.lock_server, 'nodes', '')
response = requests.post(
uri,
json.dumps(node_dict),
headers={'content-type': 'application/json'},
)
if not response.ok:
log.error("Node update/creation failed for %s: %s",
name, response.text)
return response.ok