2016-05-04 04:13:31 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
|
|
import argparse
|
2016-05-10 00:01:06 +00:00
|
|
|
import fetcher
|
2016-05-04 04:13:31 +00:00
|
|
|
import json
|
2016-05-11 05:02:58 +00:00
|
|
|
import lib
|
2016-05-09 15:40:41 -07:00
|
|
|
import os
|
2016-05-04 04:13:31 +00:00
|
|
|
import socket
|
2016-05-10 23:19:52 +00:00
|
|
|
import subprocess
|
2016-05-04 04:13:31 +00:00
|
|
|
import time
|
2016-05-10 00:23:04 +00:00
|
|
|
import update_grub
|
2016-05-04 04:13:31 +00:00
|
|
|
from ws4py.client import threadedclient
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='iconograph fetcher')
|
2016-05-10 00:23:04 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--boot-dir',
|
|
|
|
|
dest='boot_dir',
|
|
|
|
|
action='store',
|
|
|
|
|
required=True)
|
2016-05-04 04:13:31 +00:00
|
|
|
parser.add_argument(
|
2016-05-09 15:40:41 -07:00
|
|
|
'--config',
|
|
|
|
|
dest='config',
|
2016-05-04 04:13:31 +00:00
|
|
|
action='store',
|
2016-05-09 15:40:41 -07:00
|
|
|
default='/etc/iconograph.json')
|
2016-05-10 00:01:06 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--ca-cert',
|
|
|
|
|
dest='ca_cert',
|
|
|
|
|
action='store',
|
|
|
|
|
required=True)
|
2016-05-04 04:13:31 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--https-ca-cert',
|
|
|
|
|
dest='https_ca_cert',
|
|
|
|
|
action='store',
|
|
|
|
|
required=True)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'--https-client-cert',
|
|
|
|
|
dest='https_client_cert',
|
|
|
|
|
action='store',
|
|
|
|
|
required=True)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'--https-client-key',
|
|
|
|
|
dest='https_client_key',
|
|
|
|
|
action='store',
|
|
|
|
|
required=True)
|
2016-05-10 00:01:06 +00:00
|
|
|
parser.add_argument(
|
|
|
|
|
'--image-dir',
|
|
|
|
|
dest='image_dir',
|
|
|
|
|
action='store',
|
|
|
|
|
required=True)
|
2016-05-05 00:01:12 +00:00
|
|
|
parser.add_argument(
|
2016-05-09 15:40:41 -07:00
|
|
|
'--server',
|
|
|
|
|
dest='server',
|
2016-05-05 00:01:12 +00:00
|
|
|
action='store',
|
|
|
|
|
required=True)
|
2016-05-04 04:13:31 +00:00
|
|
|
FLAGS = parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Client(threadedclient.WebSocketClient):
|
|
|
|
|
|
2016-05-09 15:40:41 -07:00
|
|
|
def __init__(self, config_path, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
with open(config_path, 'r') as fh:
|
|
|
|
|
self._config = json.load(fh)
|
|
|
|
|
|
2016-05-04 04:13:31 +00:00
|
|
|
def Loop(self):
|
|
|
|
|
self.daemon = True
|
|
|
|
|
self.connect()
|
2016-05-10 00:31:13 +00:00
|
|
|
self._OnNewManifest2()
|
2016-05-04 04:13:31 +00:00
|
|
|
while True:
|
2016-05-11 19:56:32 +00:00
|
|
|
self._SendReport()
|
2016-05-04 04:13:31 +00:00
|
|
|
time.sleep(5.0)
|
|
|
|
|
|
2016-05-11 20:29:41 +00:00
|
|
|
def _SendReport(self, status=None):
|
2016-05-11 19:56:32 +00:00
|
|
|
report = {
|
|
|
|
|
'hostname': socket.gethostname(),
|
|
|
|
|
'uptime_seconds': self._Uptime(),
|
|
|
|
|
'next_timestamp': self._NextTimestamp(),
|
|
|
|
|
'next_volume_id': lib.GetVolumeID('/isodevice/iconograph/current'),
|
|
|
|
|
}
|
2016-05-11 20:29:41 +00:00
|
|
|
if status:
|
|
|
|
|
report['status'] = status
|
2016-05-11 19:56:32 +00:00
|
|
|
report.update(self._config)
|
|
|
|
|
self.send(json.dumps({
|
|
|
|
|
'type': 'report',
|
|
|
|
|
'data': report,
|
|
|
|
|
}))
|
|
|
|
|
|
2016-05-04 04:13:31 +00:00
|
|
|
def _Uptime(self):
|
|
|
|
|
with open('/proc/uptime', 'r') as fh:
|
|
|
|
|
return int(float(fh.readline().split(' ', 1)[0]))
|
|
|
|
|
|
2016-05-09 15:40:41 -07:00
|
|
|
def _NextTimestamp(self):
|
2016-05-11 05:12:43 +00:00
|
|
|
next_image = lib.GetCurrentImage()
|
2016-05-09 15:40:41 -07:00
|
|
|
return int(next_image.split('.', 1)[0])
|
|
|
|
|
|
2016-05-10 00:01:06 +00:00
|
|
|
def _OnImageTypes(self, data):
|
|
|
|
|
assert self._config['image_type'] in data['image_types']
|
|
|
|
|
|
|
|
|
|
def _OnNewManifest(self, data):
|
|
|
|
|
if data['image_type'] != self._config['image_type']:
|
|
|
|
|
return
|
2016-05-10 00:31:13 +00:00
|
|
|
self._OnNewManifest2()
|
|
|
|
|
|
2016-05-11 18:56:00 +00:00
|
|
|
def _GetFetcher(self):
|
|
|
|
|
return fetcher.Fetcher(
|
2016-05-10 00:01:06 +00:00
|
|
|
'https://%s/image/%s' % (FLAGS.server, self._config['image_type']),
|
|
|
|
|
FLAGS.ca_cert,
|
|
|
|
|
FLAGS.image_dir,
|
|
|
|
|
FLAGS.https_ca_cert,
|
|
|
|
|
FLAGS.https_client_cert,
|
|
|
|
|
FLAGS.https_client_key)
|
2016-05-11 18:56:00 +00:00
|
|
|
|
2016-05-11 19:01:17 +00:00
|
|
|
def _UpdateGrub(self):
|
2016-05-10 00:23:04 +00:00
|
|
|
update = update_grub.GrubUpdater(
|
|
|
|
|
FLAGS.image_dir,
|
|
|
|
|
FLAGS.boot_dir)
|
|
|
|
|
update.Update()
|
|
|
|
|
|
2016-05-11 19:01:17 +00:00
|
|
|
def _OnNewManifest2(self):
|
|
|
|
|
fetch = self._GetFetcher()
|
|
|
|
|
fetch.Fetch()
|
|
|
|
|
fetch.DeleteOldImages(skip={'%d.iso' % self._config['timestamp']})
|
|
|
|
|
self._UpdateGrub()
|
|
|
|
|
|
2016-05-11 00:33:53 +00:00
|
|
|
def _OnCommand(self, data):
|
|
|
|
|
if data['command'] == 'reboot':
|
2016-05-11 18:56:00 +00:00
|
|
|
self._OnReboot(data)
|
|
|
|
|
|
|
|
|
|
def _OnReboot(self, data):
|
|
|
|
|
if data['timestamp']:
|
|
|
|
|
fetch = self._GetFetcher()
|
|
|
|
|
fetch.Fetch(data['timestamp'])
|
2016-05-11 19:01:17 +00:00
|
|
|
self._UpdateGrub()
|
2016-05-11 20:29:41 +00:00
|
|
|
self._SendReport('Rebooting into %d...' % data['timestamp'])
|
|
|
|
|
else:
|
|
|
|
|
send._SendReport('Rebooting...')
|
2016-05-11 18:56:00 +00:00
|
|
|
|
|
|
|
|
subprocess.check_call(['reboot'])
|
2016-05-11 00:33:53 +00:00
|
|
|
|
2016-05-05 00:01:12 +00:00
|
|
|
def received_message(self, msg):
|
2016-05-09 15:40:41 -07:00
|
|
|
parsed = json.loads(msg.data.decode('utf8'))
|
2016-05-05 00:01:12 +00:00
|
|
|
if parsed['type'] == 'image_types':
|
2016-05-10 00:01:06 +00:00
|
|
|
self._OnImageTypes(parsed['data'])
|
|
|
|
|
elif parsed['type'] == 'new_manifest':
|
|
|
|
|
self._OnNewManifest(parsed['data'])
|
2016-05-11 00:33:53 +00:00
|
|
|
elif parsed['type'] == 'command':
|
|
|
|
|
self._OnCommand(parsed['data'])
|
2016-05-05 00:01:12 +00:00
|
|
|
|
2016-05-04 04:13:31 +00:00
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
ssl_options = {
|
|
|
|
|
'keyfile': FLAGS.https_client_key,
|
|
|
|
|
'certfile': FLAGS.https_client_cert,
|
|
|
|
|
'ca_certs': FLAGS.https_ca_cert,
|
|
|
|
|
}
|
2016-05-09 15:40:41 -07:00
|
|
|
client = Client(
|
|
|
|
|
FLAGS.config,
|
|
|
|
|
'wss://%s/ws/slave' % FLAGS.server,
|
|
|
|
|
protocols=['iconograph-slave'],
|
|
|
|
|
ssl_options=ssl_options)
|
2016-05-04 04:13:31 +00:00
|
|
|
client.Loop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|