forked from Villaz/vcycle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvcycleBase.py
286 lines (230 loc) · 12.8 KB
/
vcycleBase.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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
import os
import time, random
import abc
import shutil
import math
import VCYCLE
class vcycleBase(object):
'''Base Class where other class inherit'''
__metaclass__ = abc.ABCMeta
creationsPerCycle = 5
def __init__(self):
pass
def oneCycle(self, tenancyName, tenancy, servers):
'''Principal method.
Checks every vm running.
If the vm is stopped or it was running more than
a period of time the vm will be deleted.
If there are free space, the method will create new vms.'''
VCYCLE.logLine(tenancyName, 'Processing tenancy ' + tenancyName)
if int(tenancy['max_machines']) > 50:
self.creationsPerCycle = math.floor(tenancy['max_machines'] * 0.1)
totalRunning = 0
totalFound = 0
notPassedFizzleSeconds = {}
foundPerVmtype = {}
runningPerVmtype = {}
serverNames = []
for vmtypeName,vmtype in tenancy['vmtypes'].iteritems():
notPassedFizzleSeconds[vmtypeName] = 0
foundPerVmtype[vmtypeName] = 0
runningPerVmtype[vmtypeName] = 0
self.tenancyName = tenancyName
self.tenancy = tenancy
self.client = self._create_client()
#Update the servers running on the site
try:
servers_in_tenancy = self._servers_list()
except Exception as e:
VCYCLE.logLine(tenancyName, 'client.servers.list() fails with exception ' + str(e))
return
#Get the running and total found servers inside tenancy
for oneServer in servers_in_tenancy:
(totalRunning, totalFound) = self.for_server_in_list(oneServer, totalRunning, totalFound, notPassedFizzleSeconds, foundPerVmtype, runningPerVmtype, servers)
if not oneServer is None and oneServer.name[:7] == 'vcycle-':
serverNames.append(oneServer.name)
VCYCLE.logLine(tenancyName, 'Tenancy ' + tenancyName + ' has %d ACTIVE:running vcycle VMs out of %d found in any state for any vmtype or none' % (totalRunning, totalFound))
for vmtypeName,vmtype in tenancy['vmtypes'].iteritems():
VCYCLE.logLine(tenancyName, 'vmtype ' + vmtypeName + ' has %d ACTIVE:running out of %d found in any state' % (runningPerVmtype[vmtypeName], foundPerVmtype[vmtypeName]))
# Get rid of directories about old VMs
self.cleanupDirectories(tenancyName, serverNames)
# Now decide whether to create new VMs
createdThisCycle = 0
# Keep going till limits exhausted
while True:
createdThisPass = 0
# Go through vmtypes, possibly creating one from each, before starting again
vmtypeNames = tenancy['vmtypes'].keys()
random.shuffle(vmtypeNames)
for vmtypeName in vmtypeNames:
vmtype = tenancy['vmtypes'][vmtypeName]
if totalFound >= tenancy['max_machines']:
VCYCLE.logLine(tenancyName, 'Reached limit (%d) on number of machines to create for tenancy %s' % (tenancy['max_machines'], tenancyName))
return
elif foundPerVmtype[vmtypeName] >= vmtype['max_machines']:
VCYCLE.logLine(tenancyName, 'Reached limit (%d) on number of machines to create for vmtype %s' % (vmtype['max_machines'], vmtypeName))
elif createdThisCycle >= self.creationsPerCycle:
VCYCLE.logLine(tenancyName, 'Free capacity found ... but already created %d this cycle' % createdThisCycle )
return
elif int(time.time()) < (VCYCLE.lastFizzles[tenancyName][vmtypeName] + vmtype['backoff_seconds']):
VCYCLE.logLine(tenancyName, 'Free capacity found for %s ... but only %d seconds after last fizzle' % (vmtypeName, int(time.time()) - VCYCLE.lastFizzles[tenancyName][vmtypeName]) )
elif (int(time.time()) < (VCYCLE.lastFizzles[tenancyName][vmtypeName] + vmtype['backoff_seconds'] + vmtype['fizzle_seconds'])) and (notPassedFizzleSeconds[vmtypeName] > 0):
VCYCLE.logLine(tenancyName, 'Free capacity found for %s ... but still within fizzleSeconds+backoffSeconds(%d) of last fizzle (%ds ago) and %d running but not yet passed fizzleSeconds (%d)' %
(vmtypeName, vmtype['fizzle_seconds'] + vmtype['backoff_seconds'], int(time.time()) - VCYCLE.lastFizzles[tenancyName][vmtypeName], notPassedFizzleSeconds[vmtypeName], vmtype['fizzle_seconds']))
else:
VCYCLE.logLine(tenancyName, 'Free capacity found for ' + vmtypeName + ' within ' + tenancyName + ' ... creating')
errorMessage = self.createMachine(vmtypeName, servers, proxy='proxy' in tenancy)
if errorMessage:
VCYCLE.logLine(tenancyName, errorMessage)
else:
createdThisCycle += 1
createdThisPass += 1
totalFound += 1
foundPerVmtype[vmtypeName] += 1
notPassedFizzleSeconds[vmtypeName] += 1
if createdThisPass == 0:
# Run out of things to do, so finish the cycle for this tenancy
return
def for_server_in_list(self, server, totalRunning, totalFound,
notPassedFizzleSeconds, foundPerVmtype, runningPerVmtype, servers):
'''Executes for every server found in the tenancy, if the server is stopped or it has been running
more than an specific time, the method will delete the server.'''
tenancyName = self.tenancyName
# This includes VMs that we didn't create and won't manage, to avoid going above tenancy limit
totalFound += 1
# Just in case other VMs are in this tenancy
if server is None or server.name[:7] != 'vcycle-':
return (totalRunning , totalFound)
try:
fileTenancyName = open('/var/lib/vcycle/machines/' + server.name + '/tenancy_name', 'r').read().strip()
except:
# Not one of ours? Cleaned up directory too early?
#server.delete()
try:
shutil.rmtree('/var/lib/vcycle/machines/' + server.name)
except:
pass
if self.tenancy['delete_no_tenancy']:
server.delete()
VCYCLE.logLine(tenancyName, 'Deleted ' + server.name + ' which has no tenancy name')
totalFound -= 1
else:
VCYCLE.logLine(tenancyName, 'Skipping ' + server.name + ' which has no tenancy name')
return (totalRunning , totalFound)
else:
# Weird inconsistency, maybe the name changed? So log a warning and ignore this VM
if fileTenancyName != self.tenancyName:
VCYCLE.logLine(tenancyName, 'Skipping ' + server.name + ' which is in ' + self.tenancy['tenancy_name'] + ' but has tenancy_name=' + fileTenancyName)
return (totalRunning , totalFound)
try:
vmtypeName = open('/var/lib/vcycle/machines/' + server.name + '/vmtype_name', 'r').read().strip()
except:
# Not one of ours? Something went wrong?
VCYCLE.logLine(tenancyName, 'Skipping ' + server.name + ' which has no vmtype name')
return (totalRunning , totalFound)
if vmtypeName not in foundPerVmtype:
foundPerVmtype[vmtypeName] = 1
else:
foundPerVmtype[vmtypeName] += 1
properties = self._retrieve_properties(server, vmtypeName, servers)
totalRunning = self._update_properties(server, vmtypeName, runningPerVmtype, notPassedFizzleSeconds, properties, totalRunning)
if self._delete(server, vmtypeName, properties):
foundPerVmtype[vmtypeName] -= 1
totalFound -= 1
return (totalRunning , totalFound)
def createMachine(self, vmtypeName, servers, proxy=False):
'''Creates a new VM'''
tenancyName = self.tenancyName
serverName = self._server_name(name=tenancyName)
os.makedirs('/var/lib/vcycle/machines/' + serverName + '/machinefeatures')
os.makedirs('/var/lib/vcycle/machines/' + serverName + '/jobfeatures')
os.makedirs('/var/lib/vcycle/machines/' + serverName + '/machineoutputs')
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/vmtype_name', vmtypeName, 0644)
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/tenancy_name', tenancyName, 0644)
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/machinefeatures/phys_cores', '1', 0644)
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/machinefeatures/vac_vmtype', vmtypeName, 0644)
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/machinefeatures/vac_space', VCYCLE.tenancies[tenancyName]['vmtypes'][vmtypeName]['ce_name'],0644)
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/jobfeatures/cpu_limit_secs', str(VCYCLE.tenancies[tenancyName]['vmtypes'][vmtypeName]['max_wallclock_seconds']), 0644)
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/jobfeatures/wall_limit_secs', str(VCYCLE.tenancies[tenancyName]['vmtypes'][vmtypeName]['max_wallclock_seconds']), 0644)
try:
server = self._create_machine(serverName, vmtypeName, proxy=proxy)
servers[server.id] = {'start_time':time.time()}
except Exception as e:
return 'Error creating new server: ' + str(e)
if not server is None:
VCYCLE.createFile('/var/lib/vcycle/machines/' + serverName + '/machinefeatures/vac_uuid', server.id, 0644)
VCYCLE.makeJsonFile('/var/lib/vcycle/machines/' + serverName + '/machinefeatures')
VCYCLE.makeJsonFile('/var/lib/vcycle/machines/' + serverName + '/jobfeatures')
if not server is None:
VCYCLE.logLine(tenancyName, 'Created ' + serverName + ' (' + server.id + ') for ' + vmtypeName + ' within ' + tenancyName)
else:
VCYCLE.logLine(tenancyName, 'Created ' + serverName + ' for ' + vmtypeName + ' within ' + tenancyName)
return None
def cleanupDirectories(self, tenancyName, serverNames):
if not VCYCLE.tenancies[tenancyName]['delete_old_files']:
return
try:
dirslist = os.listdir('/var/lib/vcycle/machines/')
except:
return
# Go through the per-machine directories
for onedir in dirslist:
# Get the tenancy name
try:
fileTenancyName = open('/var/lib/vcycle/machines/' + onedir + '/tenancy_name', 'r').read().strip()
except:
continue
# Ignore if not in this tenancy, unless not in any defined tenancy
if fileTenancyName in VCYCLE.tenancies and (fileTenancyName != tenancyName):
continue
try:
onedirCtime = int(os.stat('/var/lib/vcycle/machines/' + onedir).st_ctime)
except:
continue
# Ignore directories created in the last 60 minutes to avoid race conditions
# (with other Vcycle instances? OpenStack latencies?)
if onedirCtime > (time.time() - 3600):
continue
# If the VM still exists then no deletion either
if onedir in serverNames:
continue
try:
import os.path
if os.path.isfile('/var/lib/vcycle/machines/' + onedir+'/machineoutputs/shutdown_message'):
shutil.copytree('/var/lib/vcycle/machines/' + onedir, '/var/lib/vcycle/end_machines/'+ onedir)
shutil.rmtree('/var/lib/vcycle/machines/' + onedir)
VCYCLE.logLine(tenancyName, 'Deleted /var/lib/vcycle/machines/' + onedir + ' (' + fileTenancyName + ' ' + str(int(time.time()) - onedirCtime) + 's)')
except:
VCYCLE.logLine(tenancyName, 'Failed deleting /var/lib/vcycle/machines/' + onedir + ' (' + fileTenancyName + ' ' + str(int(time.time()) - onedirCtime) + 's)')
@abc.abstractmethod
def _create_client(self):
'''Creates a new Client. It is an abstract method'''
pass
@abc.abstractmethod
def _servers_list(self):
'''Returns a list with of the servers created in a tenancy. It is an abstract method'''
pass
@abc.abstractmethod
def _retrieve_properties(self, server, vmtypeName, servers):
'''Returns the properties of a VM. It is an abstract method'''
pass
@abc.abstractmethod
def _update_properties(self, server, vmtypeName,runningPerVmtype, notPassedFizzleSeconds, properties, totalRunning):
'''Updates the properties of a VM'''
pass
@abc.abstractmethod
def _describe(self, server):
'''Returns the description of a VM.'''
pass
@abc.abstractmethod
def _delete(self, server, vmtypeName, properties):
'''Deletes a VM'''
pass
@abc.abstractmethod
def _server_name(self,name=None):
'''Returns the name of a VM'''
pass
@abc.abstractmethod
def _create_machine(self, serverName, vmtypeName, proxy=False):
'''Creates a new VM inside a tenancy'''
pass