Newer
Older
#!/usr/bin/env python
#coding=utf8
import inspect
import logging
from sjrpc.utils import RpcHandler, pure
from conf import CCConf
from exceptions import (AlreadyRegistered, AuthenticationError, RightError,
ReservedTagError)
def listed(func):
func.__listed__ = True
return func
class Reporter(object):
'''
Simple class used to report error, warning and success of command execution.
'''
def __init__(self):
self._reports = {}
def get_dict(self):
return self._reports.copy()
def success(self, oid, message):
self._reports[oid] = ('success', message)
def warn(self, oid, message):
self._reports[oid] = ('warn', message)
def error(self, oid, message):
self._reports[oid] = ('error', message)
class CCHandler(RpcHandler):
'''
Base class for handlers of CloudControl server.
'''
def __init__(self, server):
self._server = server
def __getitem__(self, name):
if name.startswith('_'):
# Filter the private members access:
raise KeyError('Remote name %s is private.' % repr(name))
else:
logging.debug('Called %s.%s' % (self.__class__.__name__, name))
return super(CCHandler, self).__getitem__(name)
@pure
def list_commands(self):
cmd_list = []
for attr in dir(self):
attr = getattr(self, attr, None)
if getattr(attr, '__listed__', False):
cmd = {}
cmd['name'] = attr.__name__
doc = inspect.getdoc(attr)
if doc:
cmd['description'] = inspect.cleandoc(doc)
class OnlineCCHandler(CCHandler):
def on_disconnect(self, conn):
self._server.unregister(conn)
class HypervisorHandler(OnlineCCHandler):
class ClientHandler(OnlineCCHandler):
@listed
def list(self, conn, query):
'''
List all objects registered on this instance.
'''
logging.debug('Executed list function with query %s' % query)
return self._server.list(query)
Antoine Millet
committed
def _vm_action(self, query, method, *args, **kwargs):
vms = self._server.list(query + '$vm$role')
hypervisors = list(self._server.iter_connected_role('hypervisor'))
Antoine Millet
committed
for hv in hypervisors:
vm_to_start = []
for vm in vms:
if vm['role'] != 'vm':
pass
elif vm['id'].split('.')[0] == hv.login:
vm_to_start.append(vm['vm'])
Antoine Millet
committed
if vm_to_start:
hv.connection.call(method, vm_to_start, *args, **kwargs)
@listed
def start(self, conn, query):
Antoine Millet
committed
self._vm_action(query, 'start_vm')
@listed
def stop(self, conn, query, force=False):
Antoine Millet
committed
self._vm_action(query, 'stop_vm', force)
Antoine Millet
committed
@listed
def destroy(self, conn, query):
Antoine Millet
committed
self.stop(query, force=True)
Antoine Millet
committed
@listed
def pause(self, conn, query):
Antoine Millet
committed
self._vm_action(query, 'suspend_vm')
@listed
def resume(self, conn, query):
Antoine Millet
committed
self._vm_action(query, 'resume_vm')
def passwd(self, conn, query, password, method='ssha'):
'''
Define a new password for specified user.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.set_password(obj['a'], password, method)
def addaccount(self, conn, login, role, password=None):
'''
Create a new account with specified login.
'''
self._server.conf.create_account(login, role, password)
def addtag(self, conn, query, tag_name, tag_value):
Add a tag to the account which match the specified query.
if tag_name in self._server.RESERVED_TAGS:
raise ReservedTagError('Tag %r is read-only' % tag_name)
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.add_tag(obj['a'], tag_name, tag_value)
def deltag(self, conn, query, tag):
'''
Remove a tag of the account with specified login.
'''
if tag_name in self._server.RESERVED_TAGS:
raise ReservedTagError('Tag %r is read-only' % tag_name)
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.remove_tag(obj['a'], tag)
def tags(self, conn, query):
'''
Return all static tags attached to the specified login.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
otags = self._server.conf.show(obj['a'])['tags']
otags.update({'id': obj['id']})
tags.append(otags)
@listed
def delaccount(self, conn, query):
'''
Delete the account with specified login.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.remove_account(obj['a'])
@listed
def close(self, conn, query):
'''
Close an account without deleting it.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.add_tag(obj['a'], 'close', 'yes')
@listed
def declose(self, conn, query):
'''
Re-open an closed account.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.remove_tag(obj['a'], 'close')
@listed
def kill(self, conn, query):
'''
Disconnect all connected accounts selected by query.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.kill(obj['a'])
def rights(self, conn, query):
'''
Get the rights of an object set.
'''
objects = self._server.list(query)
rules = {}
for obj in objects:
if 'a' in obj:
rules[obj['a']] = self._server.conf.show(obj['a'])['rights']
else:
raise BadObjectError('All objects must have the "a" tag.')
return rules
def addright(self, conn, query, tql, method=None, allow=True, index=None):
'''
Add a right rule to the selected objects.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.add_right(obj['a'], tql, method, allow, index)
def delright(self, conn, query, index):
'''
Remove a right rule from the selected objects.
'''
objects = self._server.list(query)
for obj in objects:
if 'a' not in obj:
raise BadObjectError('All objects must have the "a" tag.')
for obj in objects:
self._server.conf.remove_right(obj['a'], index)
def proxy_client(self, conn, login, command, *args, **kwargs):
client = self._server.get_connection(login)
return client.connection.call(command, *args, **kwargs)
class WelcomeHandler(CCHandler):
'''
Default handler used on client connections of the server.
:cvar ROLES: role name/handler mapping
'''
ROLES = {
'client': ClientHandler,
'hypervisor': HypervisorHandler,
}
@listed
def authentify(self, conn, login, password):
conf = self._server.conf
conf.acquire()
try:
role = self._server.conf.authentify(login, password)
except CCConf.UnknownAccount:
raise AuthenticationError('Unknown login')
else:
if 'close' in self._server.conf.show(login)['tags']:
conf.release()
raise AuthenticationError('Account is closed')
logging.info('New authentication from %s: '
'failure' % login.encode('ascii', 'ignore'))
raise AuthenticationError('Bad login/password')
# If authentication is a success, try to register the client:
try:
self._server.register(login, role, conn)
except AlreadyRegistered:
raise AuthenticationError('Already connected')
conn.set_handler(WelcomeHandler.ROLES.get(role)(self._server))