Skip to content
Snippets Groups Projects
Commit cffa3cba authored by Anael Beutot's avatar Anael Beutot
Browse files

Basics working with libev.

Authentication and host handler.
parent fc74c9dc
No related branches found
No related tags found
No related merge requests found
import time
import signal
import logging
from threading import Thread, Lock
import logging.config
import pyev
from sjrpc.core import RpcConnection
from sjrpc.utils import ConnectionProxy, RpcHandler
from ccnode import __version__
from ccnode.config import NodeConfigParser
from ccnode.tags import Tag, get_tags
......@@ -42,124 +45,130 @@ class DefaultHandler(RpcHandler):
self.tags.pop(tag_name, None)
class Node(Thread):
"""Main class for ccnode."""
def __init__(self, server_host, server_port, user_name, user_passwd):
"""
:param string server_host: hostname for cc-server
:param int server_port: port for cc-server
:param string user_name: account name for authentication to cc-server
:param string user_passwd: password for cc-server authentication
"""
Thread.__init__(self)
# run thread as daemon
self.daemon = True
# settings used as read only
self.server_host = server_host
self.server_port = int(server_port)
self.user_name = user_name
self.user_passwd = user_passwd
#: ``sjRpc`` proxy
self.proxy = None
#: ``sjRpc`` connection
self.connection = None
#: role returned by cc-server (set to None unless the authentication
#: has succeed)
self.role = None
self._connection_lock = Lock()
def init_rpc(self):
"""Init a new connection to ``cc-server``, create a ``sjRpc`` proxy.
"""
self.connection = RpcConnection.from_addr_ssl(
addr=self.server_host,
port=self.server_port,
handler=DefaultHandler(),
class AuthHandler(object):
"""Handles rpc authentication to the remote cc-server."""
def __init__(self, loop):
self.loop = loop
self.watcher = loop.loop.timer(.5, 5, self.cb)
self.auth_id = None
def cb(self, watcher, revents):
logger.debug('Callback auth')
# check is fallback mode on sjrpc is set otherwise our call would block
# the loop
if not self.loop.rpc_con._event_fallback.is_set():
logger.debug('Will try authentication again latter')
return
if self.auth_id is not None:
logger.error('Authentication is taking longer than expected')
return
# try to authenticate
self.auth_id = self.loop.rpc_con.rpc.async_call_cb(
self.cb_auth,
'authentify',
self.loop.config.server_user,
self.loop.config.server_passwd,
)
self.proxy = ConnectionProxy(self.connection)
def authentify(self):
"""Try to authenticate to the server. If successfull, import and then
set :class:`Handler` corresponding to the role returned by the
``cc-server``.
def cb_auth(self, call_id, response=None, error=None):
assert call_id == self.auth_id
:raise: exception raised by :func:`proxy.authentify`
"""
try:
role = self.proxy.authentify(self.user_name, self.user_passwd)
except Exception:
logger.exception('Unknow exception while authentifying')
raise
if error is not None:
# we got an error
logger.error('Error while authenticating with cc-server: %s("%s")',
error['exception'], error.get('message', ''))
# try to recconnect in 5 seconds
return
# set handler according to which role was returned by the cc-server
if role == u'host':
if response == u'host':
logger.debug('Role host affected')
from ccnode.host import Handler as HostHandler
self.connection.rpc.set_handler(HostHandler())
self.role = u'host'
elif role == u'hv':
self.loop.rpc_con.rpc.set_handler(HostHandler())
self.loop.role = u'host'
elif response == u'hv':
logger.debug('Role hypervisor affected')
from ccnode.hypervisor import Handler as HypervisorHandler
self.connection.rpc.set_handler(HypervisorHandler(
proxy=self.proxy, hypervisor_name=self.user_name))
self.role = u'hv'
self.loop.rpc_con.rpc.set_handler(HypervisorHandler(
proxy=self.loop.proxy,
hypervisor_name=self.loop.config.server_user))
self.loop.role = u'hv'
else:
logger.debug('Wrong role returned: %s', role)
role = None
time.sleep(2)
self.role = role
def rpc(self):
"""Runs ``sjRpc`` main loop. Catches exceptions. :func:`shutdown` the
connection before returning."""
try:
self.connection.run()
except Exception:
logger.exception('Unknown exception:')
finally:
self.shutdown()
def run(self):
"""Node main loop."""
while True:
# init rpc connection
while True:
try:
self.init_rpc()
except Exception as e:
logger.exception('Error in init.')
else:
break
time.sleep(2)
# launch main rpc thread
rpc_thread = Thread(target=self.rpc)
rpc_thread.daemon = True
rpc_thread.start()
# launch auth thread, make sure rpc is still running
while rpc_thread.is_alive() and self.role is None:
auth_thread = Thread(target=self.authentify)
auth_thread.daemon = True
auth_thread.start()
auth_thread.join()
# FIXME for debug
time.sleep(4)
# wait for rpc thread to terminates (it means error)
rpc_thread.join()
logger.error('Reconnecting to server.')
# reset settings
self.role = None
def shutdown(self):
"""Shutdown the ``sjRpc`` connection and reset object state."""
with self._connection_lock:
if self.connection is not None:
self.connection.shutdown()
self.connection = None
self.role = None
logger.info('Failed authentication, role returned: %s', response)
self.loop.role = None
self.auth_id = None
return
if self.loop.role is not None:
self.watcher.stop()
logger.info('Successfully authenticated with role %s', response)
self.auth_id = None
def start(self):
self.watcher.start()
class MainLoop(object):
def __init__(self, config_path):
self.loop = pyev.default_loop()
self.config_path = config_path
# set signal watchers
self.signals = {
signal.SIGINT: self.stop,
signal.SIGTERM: self.stop,
signal.SIGUSR1: self.reload,
}
# turn into real watchers
self.signals = dict((
signal,
self.loop.signal(signal, cb),
) for signal, cb in self.signals.iteritems())
# load config variables
self.config = NodeConfigParser(self.config_path)
# configure logging
logging.config.fileConfig(self.config_path)
# rpc connection
self.rpc_con = None
self.auth = AuthHandler(self)
# role
self.role = None
def rpc_connect(self):
# TODO async and error handling
self.rpc_con = RpcConnection.from_addr_ssl(
addr=self.config.server_host,
port=self.config.server_port,
handler=DefaultHandler(),
loop=self.loop,
)
self.proxy = ConnectionProxy(self.rpc_con)
def start(self):
for signal in self.signals.itervalues():
signal.start()
logger.debug('About to connect')
self.rpc_connect()
self.auth.start()
logger.debug('About to start ev_loop')
self.loop.start()
def stop(self, watcher=None, revents=None):
logger.info('Exiting node...')
if self.rpc_con is not None:
self.rpc_con.shutdown()
self.loop.stop()
def reload(self, watcher=None, revents=None):
logger.info(u'Reloading logging configuration...')
logging.config.fileConfig(self.config_path)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment