forked from CSCfi/pebbles
-
Notifications
You must be signed in to change notification settings - Fork 0
/
base_driver.py
214 lines (179 loc) · 8.18 KB
/
base_driver.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
import select
import shlex
import json
import subprocess
import time
import datetime
import os
import logging
import abc
import six
from pouta_blueprints.logger import PBInstanceLogHandler
from pouta_blueprints.client import PBClient
from pouta_blueprints.models import Instance
@six.add_metaclass(abc.ABCMeta)
class ProvisioningDriverBase(object):
config = {}
def __init__(self, logger, config):
self.logger = logger
self.config = config
def get_m2m_credentials(self):
if getattr(self, '_m2m_credentials', None):
self.logger.debug('m2m creds: found cached m2m creds')
return self._m2m_credentials
m2m_credential_store = self.config['M2M_CREDENTIAL_STORE']
self._m2m_credentials = {}
try:
self._m2m_credentials = json.load(open(m2m_credential_store))
debug_str = ['m2m_creds:']
for key in self._m2m_credentials.keys():
if key == 'OS_PASSWORD':
debug_str.append('OS_PASSWORD is set (not shown)')
elif key in ('OS_USERNAME', 'OS_TENANT_NAME', 'OS_TENANT_ID', 'OS_AUTH_URL'):
debug_str.append('%s: %s' % (key, self._m2m_credentials[key]))
else:
debug_str.append('unknown key %s' % key)
self.logger.debug(' '.join(debug_str))
except (IOError, ValueError) as e:
self.logger.warn("Unable to parse M2M credentials from path %s %s" % (m2m_credential_store, e))
return self._m2m_credentials
def get_configuration(self):
return {
'schema': {
'type': 'object',
'properties': {
'name': {
'type': 'string'
}
},
},
'form': [
{'type': 'help', 'helpvalue': 'config is empty'},
'*',
{'style': 'btn-info', 'title': 'Create', 'type': 'submit'}
], 'model': {}}
def update(self, token, instance_id):
self.logger.debug("update('%s')" % instance_id)
pbclient = PBClient(token, self.config['INTERNAL_API_BASE_URL'], ssl_verify=False)
instance = pbclient.get_instance(instance_id)
if not instance['to_be_deleted'] and instance['state'] in [Instance.STATE_QUEUEING]:
self.provision(token, instance_id)
elif instance['to_be_deleted'] and instance['state'] not in [Instance.STATE_DELETED]:
self.deprovision(token, instance_id)
else:
self.logger.debug("update('%s') - nothing to do for %s" % (instance_id, instance))
def update_connectivity(self, token, instance_id):
self.logger.debug('update connectivity')
self.do_update_connectivity(token, instance_id)
def provision(self, token, instance_id):
self.logger.debug('starting provisioning')
pbclient = PBClient(token, self.config['INTERNAL_API_BASE_URL'], ssl_verify=False)
try:
pbclient.do_instance_patch(instance_id, {'state': Instance.STATE_PROVISIONING})
self.logger.debug('calling subclass do_provision')
new_state = self.do_provision(token, instance_id)
if not new_state:
new_state = Instance.STATE_RUNNING
pbclient.do_instance_patch(instance_id, {'state': new_state})
except Exception as e:
self.logger.exception('do_provision raised %s' % e)
pbclient.do_instance_patch(instance_id, {'state': Instance.STATE_FAILED})
raise e
def deprovision(self, token, instance_id):
self.logger.debug('starting deprovisioning')
pbclient = PBClient(token, self.config['INTERNAL_API_BASE_URL'], ssl_verify=False)
try:
pbclient.do_instance_patch(instance_id, {'state': Instance.STATE_DELETING})
self.logger.debug('calling subclass do_deprovision')
self.do_deprovision(token, instance_id)
self.logger.debug('finishing deprovisioning')
pbclient.do_instance_patch(instance_id, {'deprovisioned_at': datetime.datetime.utcnow()})
pbclient.do_instance_patch(instance_id, {'state': Instance.STATE_DELETED})
except Exception as e:
self.logger.exception('do_deprovision raised %s' % e)
pbclient.do_instance_patch(instance_id, {'state': Instance.STATE_FAILED})
raise e
def housekeep(self, token):
self.logger.debug('housekeep')
self.do_housekeep(token)
@abc.abstractmethod
def do_housekeep(self, token):
pass
@abc.abstractmethod
def do_update_connectivity(self, token, instance_id):
pass
@abc.abstractmethod
def do_provision(self, token, instance_id):
pass
@abc.abstractmethod
def do_deprovision(self, token, instance_id):
pass
def create_prov_log_uploader(self, token, instance_id, log_type):
uploader = logging.getLogger('%s-%s' % (instance_id, log_type))
uploader.setLevel(logging.INFO)
for handler in uploader.handlers:
uploader.removeHandler(handler)
if not self.config.get('TEST_MODE', False):
# check if the custom handler is already there
if len(uploader.handlers) == 0:
uploader.addHandler(PBInstanceLogHandler(
self.config['INTERNAL_API_BASE_URL'],
instance_id,
token,
log_type,
ssl_verify=self.config['SSL_VERIFY']))
return uploader
def run_logged_process(self, cmd, cwd='.', shell=False, env=None, log_uploader=None):
if shell:
args = [cmd]
else:
args = shlex.split(cmd)
p = subprocess.Popen(
args, cwd=cwd, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
)
poller = select.poll()
poller.register(p.stdout)
poller.register(p.stderr)
log_buffer = []
last_upload = time.time()
with open('%s/instance_stdout.log' % cwd, 'a') as stdout:
with open('%s/instance__stderr.log' % cwd, 'a') as stderr:
stdout_open = stderr_open = True
while stdout_open or stderr_open:
poll_results = poller.poll(500)
for fd, mask in poll_results:
if fd == p.stdout.fileno():
if mask & select.POLLIN > 0:
line = p.stdout.readline()
self.logger.debug('STDOUT: ' + line.strip('\n'))
stdout.write(line)
stdout.flush()
log_buffer.append('STDOUT %s' % line)
elif mask & select.POLLHUP > 0:
stdout_open = False
elif fd == p.stderr.fileno():
if mask & select.POLLIN > 0:
line = p.stderr.readline()
self.logger.info('STDERR: ' + line.strip('\n'))
stderr.write(line)
stderr.flush()
if log_uploader:
log_buffer.append('STDERR %s' % line)
elif mask & select.POLLHUP > 0:
stderr_open = False
if log_uploader and (last_upload < time.time() - 10 or len(log_buffer) > 100):
if len(log_buffer) > 0:
log_uploader.info(''.join(log_buffer))
log_buffer = []
last_upload = time.time()
if log_uploader and len(log_buffer) > 0:
log_uploader.info(''.join(log_buffer))
def create_openstack_env(self):
m2m_creds = self.get_m2m_credentials()
env = os.environ.copy()
for key in ('OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_NAME', 'OS_TENANT_ID', 'OS_AUTH_URL'):
if key in m2m_creds:
env[key] = m2m_creds[key]
env['PYTHONUNBUFFERED'] = '1'
env['ANSIBLE_HOST_KEY_CHECKING'] = '0'
return env