261 lines
7.7 KiB
Python
261 lines
7.7 KiB
Python
#. -*- coding: utf-8 -
|
|
# required proxmox permissions: PVESysAdmin, PVEVMAdmin
|
|
#
|
|
# afx 2015-2016
|
|
|
|
# import site packages
|
|
import logging
|
|
import falcon
|
|
import sys
|
|
import json
|
|
|
|
#import local packages
|
|
import ioconfig
|
|
import grid
|
|
import plugin
|
|
|
|
config = ioconfig.parser
|
|
logger = ioconfig.logger
|
|
|
|
|
|
def welcome():
|
|
"""displays motd in log as welcome message"""
|
|
logger.info('# proxmaster ][ (c) 2015-2016 afx #')
|
|
|
|
|
|
def selector(fn, req, vmid=0):
|
|
""" try to exec commands """
|
|
json = req.context['doc']
|
|
#print(json)
|
|
apipass = json['apikey']
|
|
if apipass != config.get('general', 'apipass'):
|
|
status = falcon.HTTP_403
|
|
body = falcon.HTTP_403
|
|
logger.error('grid> access denied. bad api key!')
|
|
return status, body
|
|
|
|
try:
|
|
if fn == 'vmcreate':
|
|
body = plugin.vmcreate(json)
|
|
|
|
elif fn == 'vmstatus':
|
|
body = plugin.vmstatus(vmid)
|
|
|
|
elif fn == 'vmdelete':
|
|
body = plugin.vmdelete(vmid)
|
|
|
|
elif fn == 'vmsuspend':
|
|
body = plugin.vmsuspend(vmid)
|
|
|
|
elif fn == 'vmresume':
|
|
body = plugin.vmresume(vmid)
|
|
|
|
elif fn == 'vmstart':
|
|
body = plugin.vmstart(vmid)
|
|
|
|
elif fn == 'vmshutdown':
|
|
body = plugin.vmshutdown(vmid)
|
|
|
|
elif fn == 'vmstop':
|
|
body = plugin.vmstop(vmid)
|
|
|
|
elif fn == 'vmrrd':
|
|
body = plugin.vmrrd(vmid)
|
|
|
|
elif fn == 'vmvnc':
|
|
body = plugin.vmvnc(vmid)
|
|
|
|
except:
|
|
logger.critical('grid> {} error'.format(fn))
|
|
status = falcon.HTTP_404
|
|
raise
|
|
|
|
else:
|
|
#logger.info('grid> {} 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_400,
|
|
'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 CreateResource(object):
|
|
@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('vmcreate', 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('vmstatus', 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('vmdelete', req, vmid)
|
|
req.context['result'] = response
|
|
|
|
class SuspendResource(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('vmsuspend', req, vmid)
|
|
req.context['result'] = response
|
|
|
|
class ResumeResource(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('vmresume', 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('vmstart', 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('vmshutdown', 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('vmstop', req, vmid)
|
|
req.context['result'] = response
|
|
|
|
class RRDResource(object):
|
|
@falcon.before(max_body(64 * 1024))
|
|
def on_post(self, req, resp, vmid):
|
|
""" Generate rrd pngs """
|
|
logger.info('grid> rrd ' + str(vmid))
|
|
resp.status, response = selector('vmrrd', 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('vmvnc', 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_create = CreateResource()
|
|
api.add_route('/vmcreate', res_create)
|
|
|
|
res_status = StatusResource()
|
|
api.add_route('/vmstatus/{vmid}', res_status)
|
|
|
|
res_delete = DeleteResource()
|
|
api.add_route('/vmdelete/{vmid}', res_delete)
|
|
|
|
res_suspend = SuspendResource()
|
|
api.add_route('/vmsuspend/{vmid}', res_suspend)
|
|
|
|
res_resume = ResumeResource()
|
|
api.add_route('/vmresume/{vmid}', res_resume)
|
|
|
|
res_start = StartResource()
|
|
api.add_route('/vmstart/{vmid}', res_start)
|
|
|
|
res_shutdown = ShutdownResource()
|
|
api.add_route('/vmshutdown/{vmid}', res_shutdown)
|
|
|
|
res_stop = StopResource()
|
|
api.add_route('/vmstop/{vmid}', res_stop)
|
|
|
|
res_rrd = RRDResource()
|
|
api.add_route('/vmrrd/{vmid}', res_rrd)
|
|
|
|
res_vnc = VNCResource()
|
|
api.add_route('/vmvnc/{vmid}', res_vnc)
|
|
|
|
#display motd
|
|
welcome()
|
|
|