#. -*- coding: utf-8 - # required proxmox permissions: PVESysAdmin, PVEVMAdmin # # afx 2015-2016 # import site packages import logging import falcon import sys import json import urllib.parse #import local packages import ioconfig import grid import plugin import clientsdb config = ioconfig.parser logger = ioconfig.logger def welcome(): """displays motd in log as welcome message""" logger.info('###################################') logger.info('# proxmaster ][ (c) 2015-2016 afx #') logger.info('###################################') def selector(fn, req, vmid=0): """ TRY to exec commands """ json = req.context['doc'] apipass = json['apikey'] if apipass != config.get('general', 'apipass'): status = falcon.HTTP_404 body = '404 Not Found' logger.error('grid> access denied. bad api key!') fn = '404' try: if fn == 'validate': clientemail = json['clientemail'] passwd = json['password'] #logger.info('grid> access requested for {} with {}'.format(clientemail, passwd)) body = clientsdb.validate(clientemail, passwd) elif fn == 'create': body = urllib.parse.urlencode(plugin.vmcreate(req.params)) elif fn == 'status': body = urllib.parse.urlencode(plugin.vmstatus(vmid)) elif fn == 'delete': body = urllib.parse.urlencode(plugin.vmdelete(vmid)) elif fn == 'suspend': body = urllib.parse.urlencode(plugin.vmsuspend(vmid)) elif fn == 'resume': body = urllib.parse.urlencode(plugin.vmresume(vmid)) elif fn == 'start': body = urllib.parse.urlencode(plugin.vmstart(vmid)) elif fn == 'shutdown': body = urllib.parse.urlencode(plugin.vmshutdown(vmid)) elif fn == 'stop': body = urllib.parse.urlencode(plugin.vmstop(vmid)) elif fn == 'vnc': body = urllib.parse.urlencode(plugin.vmvnc(vmid)) except: logger.critical('grid> {} malfunction!'.format(fn)) body = '793 Zombie Apocalypse' status = falcon.HTTP_404 else: #logger.info('{}> 200 OK'.format(fn)) status = falcon.HTTP_202 return status, body class RequireJSON(object): def process_request(self, req, resp): if not req.client_accepts_json: raise falcon.HTTPNotAcceptable( 'This API only supports responses encoded as JSON.', href='http://docs.examples.com/api/json') if req.method in ('POST', 'PUT'): if 'application/json' not in req.content_type: raise falcon.HTTPUnsupportedMediaType( 'This API only supports requests encoded as JSON.', href='http://docs.examples.com/api/json') class JSONTranslator(object): def process_request(self, req, resp): # req.stream corresponds to the WSGI wsgi.input environ variable, # and allows you to read bytes from the request body. # # See also: PEP 3333 if req.content_length in (None, 0): # Nothing to do return body = req.stream.read() if not body: raise falcon.HTTPBadRequest('Empty request body', 'A valid JSON document is required.') try: req.context['doc'] = json.loads(body.decode('utf-8')) except (ValueError, UnicodeDecodeError): raise falcon.HTTPError(falcon.HTTP_753, 'Malformed JSON', 'Could not decode the request body. The ' 'JSON was incorrect or not encoded as ' 'UTF-8.') def process_response(self, req, resp, resource): if 'result' not in req.context: return resp.body = json.dumps(req.context['result']) def max_body(limit): def hook(req, resp, resource, params): length = req.content_length if length is not None and length > limit: msg = ('The size of the request is too large. The body must not ' 'exceed ' + str(limit) + ' bytes in length.') raise falcon.HTTPRequestEntityTooLarge( 'Request body is too large', msg) return hook class ValidateResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp): """ get clientemail and password and compare it with the client db and returns a list of managed object IDs """ resp.status, response = selector('validate', req) req.context['result'] = response class ClusterResource(object): #def on_get(self, req, resp): # """ TEST . List cluster nodes . TEST """ # logger.info('grid> cache status') # response = grid.sync(False) # response = 'poke' # req.context['result'] = response @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp): """Create a cluster node, returns array of: status, vmid, pass, ipv4, """ logger.info('grid> create ' + str(req.params)) resp.status, response = selector('create', req) req.context['result'] = response class StatusResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ check vm status """ logger.info('grid> status ' + str(vmid)) resp.status, response = selector('status', req, vmid) req.context['result'] = response class DeleteResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ delete machine completely""" logger.info('grid> delete ' + str(vmid)) resp.status, response = selector('delete', req, vmid) req.context['result'] = response class ArchivateResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ Temporary suspend the instance """ logger.info('grid> suspend ' + str(vmid)) resp.status, response = selector('suspend', req, vmid) req.context['result'] = response class UnArchiveResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ Unuspend the instance """ logger.info('grid> resume ' + str(vmid)) resp.status, response = selector('resume', req, vmid) req.context['result'] = response class StartResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ Start the instance """ logger.info('grid> start ' + str(vmid)) resp.status, response = selector('start', req, vmid) req.context['result'] = response class ShutdownResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ ACPI Shutdown the instance """ logger.info('grid> shutdown ' + str(vmid)) resp.status, response = selector('shutdown', req, vmid) req.context['result'] = response class StopResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ Stop the instance """ logger.info('grid> stop ' + str(vmid)) resp.status, response = selector('stop', req, vmid) req.context['result'] = response class VNCResource(object): @falcon.before(max_body(64 * 1024)) def on_post(self, req, resp, vmid): """ Create a VNC link to the instance """ logger.info('grid> vnc ' + str(vmid)) resp.status, response = selector('vnc', req, vmid) req.context['result'] = response if __name__ == '__main__': sys.exit("invoke proxmaster via uwsgi. thanks. bye. o/") wsgi_app = api = application = falcon.API(middleware=[ RequireJSON(), JSONTranslator(), ]) # setup routes res_validate = ValidateResource() api.add_route('/validate', res_validate) res_cluster = ClusterResource() api.add_route('/instance', res_cluster) res_status = StatusResource() api.add_route('/instance/{vmid}', res_status) res_delete = DeleteResource() api.add_route('/instance/delete/{vmid}', res_delete) res_archivate = ArchivateResource() api.add_route('/instance/archivate/{vmid}', res_archivate) res_unarchive = UnArchiveResource() api.add_route('/instance/unarchive/{vmid}', res_unarchive) res_start = StartResource() api.add_route('/instance/start/{vmid}', res_start) res_shutdown = ShutdownResource() api.add_route('/instance/shutdown/{vmid}', res_shutdown) res_stop = StopResource() api.add_route('/instance/stop/{vmid}', res_stop) res_vnc = VNCResource() api.add_route('/instance/vnc/{vmid}', res_vnc) #display motd welcome()