Initial commit

master
informatic 2018-01-27 12:58:56 +01:00
commit bc19af1a92
5 changed files with 141 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.py[co]
config.py

11
README.md Normal file
View File

@ -0,0 +1,11 @@
spejsiot-libvirt
================
Simple spejsiot to libvirt adapter. Control your domains with simple attribute
writes.
Running
-------
`pip install -r requirements.txt`, copy `config.py.dist` to `config.py` and run
`spejsvirt.py`. (you might want to take a look at `--help` too)

2
config.py.dist Normal file
View File

@ -0,0 +1,2 @@
TOPIC_PREFIX = 'iot/craptrap/'
DOMAINS = ['windows10']

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
libvirt-python==4.0.0
paho-mqtt==1.3.1

124
spejsvirt.py Normal file
View File

@ -0,0 +1,124 @@
import argparse
import logging
from functools import wraps
import paho.mqtt.client as mqtt
import libvirt
DOMAIN_STATES = {
0: "nostate",
1: "running",
2: "blocked",
3: "paused",
4: "shutdown",
5: "shutoff",
6: "crashed",
7: "pmsuspended",
}
parser = argparse.ArgumentParser(description='libvirt-to-spejsiot adapter')
parser.add_argument('--libvirt', help='libvirt connection URI')
parser.add_argument('--broker', default='mqtt.waw.hackerspace.pl', help='MQTT broker')
def report(fn):
"""Wraps random function and logs its exceptions while returning None"""
@wraps(fn)
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except:
logging.exception('An error occured')
return None
return wrapped
class LibvirtSpejsIOTClient(mqtt.Client):
topic_prefix = None
def __init__(self, config, *args, **kwargs):
super(LibvirtSpejsIOTClient, self).__init__(*args, **kwargs)
self.logger = logging.getLogger(self.__class__.__name__)
self.config = config
self.topic_prefix = self.config.TOPIC_PREFIX
def on_connect(self, client, userdata, flags, rc):
self.subscribe(self.topic_prefix + '+/+/set')
self.logger.info('Connected')
for dom in self.config.DOMAINS:
self.publish_state(self.conn.lookupByName(dom))
@report
def on_message(self, client, userdata, msg):
topic = msg.topic[len(self.topic_prefix):]
self.logger.info('mqtt -> %r %r', topic, msg.payload)
node, attrib, _ = topic.split('/')
if not msg.payload:
return
if node not in self.config.DOMAINS:
self.logger.warning('Forbidden domain')
return
if attrib != 'state':
self.logger.warning('Unknown attribute')
return
# We don't like persistence here
self.publish(msg.topic, '', retain=True)
dom = self.conn.lookupByName(node)
state = dom.state(0)[0]
if msg.payload == 'running':
if state == libvirt.VIR_DOMAIN_PAUSED:
dom.resume()
elif state == libvirt.VIR_DOMAIN_PMSUSPENDED:
dom.pMWakeup()
else:
dom.create()
elif msg.payload == 'paused':
dom.suspend()
elif msg.payload == 'pmsuspended':
dom.pMSuspendForDuration(0, 0)
elif msg.payload in ['shutoff', 'shutdown']:
dom.shutdown()
elif msg.payload == 'kill':
dom.destroy()
elif msg.payload == 'reset':
dom.reset()
def lifecycle_callback(self, connection, domain, event, detail, console):
self.logger.info('%s -> %r / %r', domain.name(), event, detail)
self.publish_state(domain)
def publish_state(self, domain):
domname = domain.name()
if domname not in self.config.DOMAINS:
return
state = DOMAIN_STATES.get(domain.state(0)[0])
self.publish('%s%s/state' % (self.topic_prefix, domname), state)
def run(self, args):
libvirt.virEventRegisterDefaultImpl()
self.conn = libvirt.open(args.libvirt)
self.conn.domainEventRegisterAny(
None, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
self.lifecycle_callback, self.conn)
self.connect(args.broker)
self.loop_start()
while True:
self.logger.debug('*beep*')
libvirt.virEventRunDefaultImpl()
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
args = parser.parse_args()
import config
LibvirtSpejsIOTClient(config).run(args)