Commit cfb70ccf authored by Anael Beutot's avatar Anael Beutot
Browse files

Host functionalities all implemented as previous ccnode.

parent ab5b4d74
Loading
Loading
Loading
Loading
+103 −0
Original line number Diff line number Diff line
import inspect
import logging
from subprocess import Popen, PIPE, STDOUT

from sjrpc.utils import pure

from ccnode.node import DefaultHandler
from ccnode.tags import Tag, DynamicTags
from ccnode.host import tags


logger = logging.getLogger(__name__)


class Handler(DefaultHandler):
    """Handler for host role."""
    def __init__(self, *args, **kwargs):
        DefaultHandler.__init__(self, *args, **kwargs)

        # add host tags to self.tags dict
        [
            'cpu',
            'cpuuse',
            'arch',
            'chaserial',
            'cpufreq',
            'disk',
            'hmodel',
            'hserial',
            'hvendor',
            'hbios',
            'load',
            'mem',
            'memfree',
            'memused',
            'os',
            'platform',
            'sto',
            'uname',
            'uptime',
        ]
        logger.debug(u'Begin introspection')
        for n, m in inspect.getmembers(tags):  # (name, member)
            # only keep strings or functions as tags
            if getattr(m, '__module__', None) != 'ccnode.host.tags' or (
                n.startswith('_')):
                continue
            elif isinstance(m, (str, unicode)):
                # if string, it means it is constant, then ttl = -1
                ttl = -1
            elif inspect.isfunction(m):
                # if function take function ttl argument or set -1 by default
                ttl = getattr(m, 'ttl', -1)
            elif isinstance(m, DynamicTags):
                logger.debug('plop')
                for t in m.iter_tags():
                    self.tags[t.name] = t
                    logger.debug('Introspected %s with ttl %s.' % (t.name,
                                                                   t.ttl))
                continue
            else:
                continue

            logger.debug('Introspected %s with ttl %s.' % (n, ttl))

            # finally add the tag
            self.tags[n] = Tag(n, m, ttl)
        logger.debug(u'End introspection')

    @pure
    def execute_command(self, command):
        """Execute an arbitrary shell command on the host.
        
        :param string command: shell command to run
        """
        # return stdout and stderr mixed in
        return Popen(command, shell=True, bufsize=-1, stdin=PIPE, stdout=PIPE,
                     stderr=STDOUT).communicate()[0] or None

    @pure
    def node_shutdown(self, reboot=True, gracefull=True):
        """Halt/Reboot the node."""
        args = ['/sbin/shutdown']
        if reboot:
            args.append('-r')
            if gracefull:
                logger.info(u'Going to reboot the host...')
                args.append('-f')
            else:
                logger.info(u'Going to force the reboot of the host...')
                args.append('-n')
        else:
            # halt
            args.append('-h -P')
            if not gracefull:
                logger.info(u'Going to halt the host...')
                args.append('-n')
            else:
                logger.info(u'Going to force the halt of the host...')

        args.append('0')

        return self.execute_command(' '.join(args))

ccnode/host/handler.py

deleted100644 → 0
+0 −0

Empty file deleted.

ccnode/host/tags.py

0 → 100644
+170 −0
Original line number Diff line number Diff line
"""This module acts as a little framework for defining tags in a simple way.
Just define a string or a function and it will be introspected and used as a
tag value.
"""
import re
import os as os_
import platform as platform_
from multiprocessing import cpu_count
from socket import gethostname, gethostbyaddr

import psutil

from ccnode.tags import Tag, DynamicTags


def h():
    """Hostname tag."""
    hn = gethostname()

    return gethostbyaddr(hn)[0] or hn
h.ttl = 3600 * 24  # one day


# CPU related tags
def arch():
    """Hardware CPU architecture."""
    return {
        u'i386': u'x86',
        u'i486': u'x86',
        u'i586': u'x86',
        u'i686': u'x86',
        u'x86_64': u'x64',
        u'': None,
    }[platform_.machine()]


def cpu():
    """Number of CPU (core) on the host."""
    try:
        return unicode(cpu_count())
    except NotImplementedError:
        return None


def cpuuse():
    """CPU usage in percentage."""
    return u'%.1f' % psutil.cpu_percent()
cpuuse.ttl = 5


# memory related tags
def mem():
    """Total physical memory available on system."""
    return unicode(psutil.avail_phymem() + psutil.used_phymem())
mem.ttl = -1  # FIXME validate ttl


def memfree():
    """Available physical memory on system."""
    return unicode(psutil.avail_phymem())
memfree.ttl = 60


def memused():
    """Used physical memory on system."""
    return unicode(psutil.used_phymem())
memused.ttl = 60


# disks related tags
def disk():
    """List of disk devices on the host."""
    disk_pattern = re.compile(r'[sh]d[a-z]+')

    return u' '.join(d for d in os_.listdir(
        '/sys/block/') if disk_pattern.match(d))


def _disk_tag_value(disk_name):
    def size():
        s = open(os_.path.join('/sys/block', disk_name, 'size')).read().strip()
        try:
            s = int(s)
            if s > 0:
                return s * 512
            else:
                return None
        except ValueError:
            return None

    return size


class DynamicDisks(DynamicTags):
    def __init__(self):
        DynamicTags.__init__(self)

        for d in disk().split():
            self.tags.append(Tag(u'disk%s_size' % d, _disk_tag_value(d), 3600))


disks_size = DynamicDisks()


# other hardware related tags
def chaserial():
    """Blade chassis serial number."""
    return open('/sys/class/dmi/id/chassis_serial').read().strip() or None


def chaasset():
    """Blade chassis asset tag."""
    return open('/sys/class/dmi/id/chassis_asset_tag').read().strip() or None


def hmodel():
    """Host hardware model."""
    return open('/sys/class/dmi/id/product_name').read().strip() or None


def hserial():
    """Host hardware serial number."""
    return open('/sys/class/dmi/id/product_serial').read().strip() or None


def hvendor():
    """Host hardware vendor."""
    return open('/sys/class/dmi/id/sys_vendor').read().strip() or None


def hbios():
    """Host BIOS version."""
    return u'%s (%s)' % (
        open('/sys/class/dmi/id/bios_version').read().strip() or None,
        open('/sys/class/dmi/id/bios_date').read().strip() or None,
    )


# Operating system related tags
def os():
    """Operating system (linux/windows)."""
    return unicode(platform_.system().lower())


def platform():
    """Python platform.platform() info."""
    return unicode(platform_.platform())


def uname():
    """As uname command (see python os.uname)."""
    return u' '.join(os_.uname()) or None


def uptime():
    """Uptime of the system in seconds."""
    return open('/proc/uptime').read().split()[0].split(u'.')[0] or None
uptime.ttl = 5


def load():
    """Average of the number of processes in the run queue over the last 1, 5
    and 15 minutes."""
    load_ = None
    try:
        load_ = u' '.join(unicode(l) for l in os_.getloadavg())
    except OSError:
        pass
    return load_
load.ttl = 5
+20 −8
Original line number Diff line number Diff line
@@ -27,7 +27,10 @@ class DefaultHandler(RpcHandler):
        :param iterable tags: list of tags to return
        :param iterable noresolve_tags: list of tags to not return
        """
        logger.debug('Tags: %s, %s' % (unicode(tags), unicode(noresolve_tags)))
        logger.debug('Tags request: %s, %s' % (
            unicode(tags),
            unicode(noresolve_tags),
        ))

        tags = set(tags) - set(noresolve_tags) if tags is not None else None

@@ -36,6 +39,8 @@ class DefaultHandler(RpcHandler):
        else:
            tags = tags & set(self.tags.iterkeys())

        profile = time.time()
        try:
            result = dict((
                t,  # tag name
                dict(
@@ -43,6 +48,10 @@ class DefaultHandler(RpcHandler):
                    ttl=self.tags[t].ttl,
                ),
            ) for t in tags)
        except Exception:
            logger.exception(u'SHould not happend, result.')

        logger.debug(u'Profiling: %f seconds.' % (time.time() - profile))
        logger.debug(u'Returning: %s' % unicode(result))
        return result

@@ -98,6 +107,9 @@ class Node(Thread):
        # set handler according to which role was returned by the cc-server
        if role == u'host':
            logger.debug(u'Role host affected.')
            from ccnode.host import Handler as HostHandler
            # FIXME bad API
            self.manager._connection.set_handler(HostHandler())
            self.role = u'host'
        elif role == u'hv':
            logger.debug(u'Role hypervisor affected.')
+20 −1
Original line number Diff line number Diff line
import logging
from inspect import isfunction


logger = logging.getLogger(__name__)


class Tag(object):
    """Class that abstract tags."""
    def __init__(self, name, valuable, ttl):
@@ -17,6 +21,21 @@ class Tag(object):
    def value(self):
        """Returns tag value."""
        if isfunction(self._value):
            try:
                return self._value()
            except Exception:
                logger.exception(u'Calculating tag %s failed:' % self.name)
                return None

        return self._value


class DynamicTags(object):
    """Set tags dynamicaly."""
    def __init__(self):
        """Here define the tags."""
        self.tags = []

    def iter_tags(self):
        for t in self.tags:
            yield t