Commit b6d5c41a by Szeberényi Imre

Initial version, Python 2.7

parents
# Python bytecode:
*.py[co]
# Packaging files:
*.egg*
# Editor temp files:
*.swp
*.swo
*~
# Sphinx docs:
build
_build
# SQLite3 database files:
*.db
# Logs:
*.log
.ropeproject
celerybeat-schedule
.coverage
*,cover
coverage.xml
Installation of a development node machine
==========================================
.. highlight:: bash
Preparation
-----------
First create a new Ubuntu 12.04 LTS instance. Set up git.. TODO
Setting up required software
----------------------------
Update package list and install the required softwares::
$ sudo apt-get update
$ sudo apt-get install --yes python-pip virtualenvwrapper git python-dev \
openvswitch-common openvswitch-datapath-dkms openvswitch-switch \
openvswitch-controller libvirt-bin python-libvirt \
libxml2-dev libxslt1-dev zlib1g-dev qemu-kvm
Configuring network
-------------------
Configure Open vSwitch bridge that handle vitual connections::
$ sudo ovs-vsctl add-br cloud
Enable passwordless Open vSwitch commands::
$ sudo tee /etc/sudoers.d/netdriver <<END
cloud ALL = (ALL) NOPASSWD: /usr/bin/ovs-ofctl, /usr/bin/ovs-vsctl, /sbin/ip link set *
END
$ sudo chmod 660 /etc/sudoers.d/netdriver
Configuring the libvirt daemon
------------------------------
Change the libvirt default settings in */etc/libvirt/qemu.conf*::
$ sudo tee -a /etc/libvirt/qemu.conf <<A
clear_emulator_capabilities = 0
user = "root"
group = "root"
cgroup_device_acl = [
"/dev/null", "/dev/full", "/dev/zero",
"/dev/random", "/dev/urandom",
"/dev/ptmx", "/dev/kvm", "/dev/kqemu",
"/dev/rtc", "/dev/hpet", "/dev/net/tun",
]
A
Setting up SSL certificates for migrations::
Add "-l" parameter to /etc/default/libvirt-bin at libvirtd-opts="-d -l"
/etc/libvirt/libvirtd.conf
listen_tcp = 1
auth_tcp = "none"
$ TODO
Installing CIRCLE vmdriver
--------------------------
Clone the git repository::
$ git clone git@git.cloud.ik.bme.hu:circle/vmdriver.git vmdriver
Set up virtualenv profile::
$ source /etc/bash_completion.d/virtualenvwrapper
$ mkvirtualenv vmdriver
Save configuration to virtualenv and activate environment::
$ cat >>/home/cloud/.virtualenvs/vmdriver/bin/postactivate <<END
export LIBVIRT_KEEPALIVE=True
export LIBVIRT_URI=test:///default
export AMQP_URI=amqp://cloud:password@$(hostname)/circle
export HYPERVISOR_TYPE=test
END
Copy the libvrit bindings to the local virtualenv directory::
$ cp /usr/lib/python2.7/dist-packages/*libvirt* ~/.virtualenvs/vmdriver/lib/python2.7/site-packages/
Install the required python packages::
$ pip install -r requirements/test.txt
Copy the upstart scripts for celery services::
$ sudo cp miscellaneous/vmcelery.conf /etc/init/
$ sudo cp miscellaneous/netcelery.conf /etc/init/
Start celery daemons::
$ sudo start vmcelery
$ sudo start netcelery
\ No newline at end of file
# Keys and buttons
#
# Most of the keys/buttons are modeled after USB HUT 1.12
# (see http://www.usb.org/developers/hidpage).
# Abbreviations in the comments:
# AC - Application Control
# AL - Application Launch Button
# SC - System Control
KEY_RESERVED = 0
KEY_ESC = 1
KEY_1 = 2
KEY_2 = 3
KEY_3 = 4
KEY_4 = 5
KEY_5 = 6
KEY_6 = 7
KEY_7 = 8
KEY_8 = 9
KEY_9 = 10
KEY_0 = 11
KEY_MINUS = 12
KEY_EQUAL = 13
KEY_BACKSPACE = 14
KEY_TAB = 15
KEY_Q = 16
KEY_W = 17
KEY_E = 18
KEY_R = 19
KEY_T = 20
KEY_Y = 21
KEY_U = 22
KEY_I = 23
KEY_O = 24
KEY_P = 25
KEY_LEFTBRACE = 26
KEY_RIGHTBRACE = 27
KEY_ENTER = 28
KEY_LEFTCTRL = 29
KEY_A = 30
KEY_S = 31
KEY_D = 32
KEY_F = 33
KEY_G = 34
KEY_H = 35
KEY_J = 36
KEY_K = 37
KEY_L = 38
KEY_SEMICOLON = 39
KEY_APOSTROPHE = 40
KEY_GRAVE = 41
KEY_LEFTSHIFT = 42
KEY_BACKSLASH = 43
KEY_Z = 44
KEY_X = 45
KEY_C = 46
KEY_V = 47
KEY_B = 48
KEY_N = 49
KEY_M = 50
KEY_COMMA = 51
KEY_DOT = 52
KEY_SLASH = 53
KEY_RIGHTSHIFT = 54
KEY_KPASTERISK = 55
KEY_LEFTALT = 56
KEY_SPACE = 57
KEY_CAPSLOCK = 58
KEY_F1 = 59
KEY_F2 = 60
KEY_F3 = 61
KEY_F4 = 62
KEY_F5 = 63
KEY_F6 = 64
KEY_F7 = 65
KEY_F8 = 66
KEY_F9 = 67
KEY_F10 = 68
KEY_NUMLOCK = 69
KEY_SCROLLLOCK = 70
KEY_KP7 = 71
KEY_KP8 = 72
KEY_KP9 = 73
KEY_KPMINUS = 74
KEY_KP4 = 75
KEY_KP5 = 76
KEY_KP6 = 77
KEY_KPPLUS = 78
KEY_KP1 = 79
KEY_KP2 = 80
KEY_KP3 = 81
KEY_KP0 = 82
KEY_KPDOT = 83
KEY_ZENKAKUHANKAKU = 85
KEY_102ND = 86
KEY_F11 = 87
KEY_F12 = 88
KEY_RO = 89
KEY_KATAKANA = 90
KEY_HIRAGANA = 91
KEY_HENKAN = 92
KEY_KATAKANAHIRAGANA = 93
KEY_MUHENKAN = 94
KEY_KPJPCOMMA = 95
KEY_KPENTER = 96
KEY_RIGHTCTRL = 97
KEY_KPSLASH = 98
KEY_SYSRQ = 99
KEY_RIGHTALT = 100
KEY_LINEFEED = 101
KEY_HOME = 102
KEY_UP = 103
KEY_PAGEUP = 104
KEY_LEFT = 105
KEY_RIGHT = 106
KEY_END = 107
KEY_DOWN = 108
KEY_PAGEDOWN = 109
KEY_INSERT = 110
KEY_DELETE = 111
KEY_MACRO = 112
KEY_MUTE = 113
KEY_VOLUMEDOWN = 114
KEY_VOLUMEUP = 115
KEY_POWER = 116
KEY_KPEQUAL = 117
KEY_KPPLUSMINUS = 118
KEY_PAUSE = 119
KEY_SCALE = 120
KEY_KPCOMMA = 121
KEY_HANGEUL = 122
KEY_HANJA = 123
KEY_YEN = 124
KEY_LEFTMETA = 125
KEY_RIGHTMETA = 126
KEY_COMPOSE = 127
KEY_STOP = 128
KEY_AGAIN = 129
KEY_PROPS = 130
KEY_UNDO = 131
KEY_FRONT = 132
KEY_COPY = 133
KEY_OPEN = 134
KEY_PASTE = 135
KEY_FIND = 136
KEY_CUT = 137
KEY_HELP = 138
KEY_MENU = 139
KEY_CALC = 140
KEY_SETUP = 141
KEY_SLEEP = 142
KEY_WAKEUP = 143
KEY_FILE = 144
KEY_SENDFILE = 145
KEY_DELETEFILE = 146
KEY_XFER = 147
KEY_PROG1 = 148
KEY_PROG2 = 149
KEY_WWW = 150
KEY_MSDOS = 151
KEY_COFFEE = 152
KEY_DIRECTION = 153
KEY_CYCLEWINDOWS = 154
KEY_MAIL = 155
KEY_BOOKMARKS = 156
KEY_COMPUTER = 157
KEY_BACK = 158
KEY_FORWARD = 159
KEY_CLOSECD = 160
KEY_EJECTCD = 161
KEY_EJECTCLOSECD = 162
KEY_NEXTSONG = 163
KEY_PLAYPAUSE = 164
KEY_PREVIOUSSONG = 165
KEY_STOPCD = 166
KEY_RECORD = 167
KEY_REWIND = 168
KEY_PHONE = 169
KEY_ISO = 170
KEY_CONFIG = 171
KEY_HOMEPAGE = 172
KEY_REFRESH = 173
KEY_EXIT = 174
KEY_MOVE = 175
KEY_EDIT = 176
KEY_SCROLLUP = 177
KEY_SCROLLDOWN = 178
KEY_KPLEFTPAREN = 179
KEY_KPRIGHTPAREN = 180
KEY_NEW = 181
KEY_REDO = 182
KEY_F13 = 183
KEY_F14 = 184
KEY_F15 = 185
KEY_F16 = 186
KEY_F17 = 187
KEY_F18 = 188
KEY_F19 = 189
KEY_F20 = 190
KEY_F21 = 191
KEY_F22 = 192
KEY_F23 = 193
KEY_F24 = 194
KEY_PLAYCD = 200
KEY_PAUSECD = 201
KEY_PROG3 = 202
KEY_PROG4 = 203
KEY_DASHBOARD = 204
KEY_SUSPEND = 205
KEY_CLOSE = 206
KEY_PLAY = 207
KEY_FASTFORWARD = 208
KEY_BASSBOOST = 209
KEY_PRINT = 210
KEY_HP = 211
KEY_CAMERA = 212
KEY_SOUND = 213
KEY_QUESTION = 214
KEY_EMAIL = 215
KEY_CHAT = 216
KEY_SEARCH = 217
KEY_CONNECT = 218
KEY_FINANCE = 219
KEY_SPORT = 220
KEY_SHOP = 221
KEY_ALTERASE = 222
KEY_CANCEL = 223
KEY_BRIGHTNESSDOWN = 224
KEY_BRIGHTNESSUP = 225
KEY_MEDIA = 226
description "IK Cloud Django Development Server"
respawn
respawn limit 30 30
setuid cloud
setgid libvirtd
instance $NAME
script
cd /home/cloud/vmdriver
. /home/cloud/.virtualenvs/vmdriver/local/bin/activate
. /home/cloud/.virtualenvs/vmdriver/local/bin/postactivate
exec celery -A netcelery worker --loglevel=info -n ${NAME}
end script
[Unit]
Description=netcelery %I
BindsTo=node.service
[Service]
User=cloud
Group=cloud
KillSignal=SIGTERM
TimeoutStopSec=600
Restart=always
WorkingDirectory=/home/cloud/vmdriver
ExecStart=/bin/bash -c "source /etc/profile; workon vmdriver; exec celery -A netcelery worker --loglevel=info -n $(/bin/hostname -s).%I"
description "IK Cloud Django Development Server"
start on runlevel [2345]
stop on runlevel [!2345]
pre-start script
hostname=$(hostname -s)
for inst in vm.fast vm.slow
do
start vmcelery NAME=$hostname.$inst || :
done
for inst in net.fast
do
start netcelery NAME=$hostname.$inst || :
done
end script
post-stop script
for inst in `initctl list|grep "^vmcelery "|awk '{print $2}'|tr -d ')'|tr -d '('`
do
stop vmcelery NAME=$inst || :
done
for inst in `initctl list|grep "^netcelery "|awk '{print $2}'|tr -d ')'|tr -d '('`
do
stop netcelery NAME=$inst || :
done
end script
[Unit]
Description=CIRCLE node
After=network.target
BindsTo=netcelery@net.fast.service
BindsTo=vmcelery@vm.fast.service
BindsTo=vmcelery@vm.slow.service
BindsTo=agentdriver.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/echo
[Install]
WantedBy=multi-user.target
description "IK Cloud Django Development Server"
respawn
respawn limit 30 30
setuid cloud
setgid libvirtd
instance $NAME
script
cd /home/cloud/vmdriver
. /home/cloud/.virtualenvs/vmdriver/local/bin/activate
. /home/cloud/.virtualenvs/vmdriver/local/bin/postactivate
exec celery -A vmcelery worker --loglevel=info -n $NAME
end script
[Unit]
Description=vmcelery %I
BindsTo=node.service
[Service]
User=cloud
Group=cloud
KillSignal=SIGTERM
TimeoutStopSec=600
Restart=always
WorkingDirectory=/home/cloud/vmdriver
ExecStart=/bin/bash -c "source /etc/profile; workon vmdriver; exec celery -A vmcelery worker --loglevel=info -n $(/bin/hostname -s).%I"
""" Celery module for libvirt RPC calls. """
from celery import Celery
from kombu import Queue, Exchange
from os import getenv
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-n", "--hostname", dest="hostname",
help="Define the full queue name with"
"with priority", metavar="hostname.queue.priority")
(args, unknwon_args) = parser.parse_known_args()
HOSTNAME = vars(args).pop("hostname")
if HOSTNAME is None:
raise Exception("You must define hostname as -n <hostname> or "
"--hostname=<hostname>.\n"
"Hostname format must be hostname.module.priority.")
AMQP_URI = getenv('AMQP_URI')
def to_bool(value):
return value.lower() in ("true", "yes", "y", "t")
lib_connection = None
native_ovs = False
if to_bool(getenv('LIBVIRT_KEEPALIVE', "False")):
import libvirt
lib_connection = libvirt.open(getenv('LIBVIRT_URI'))
if to_bool(getenv('NATIVE_OVS', "False")):
native_ovs = True
celery = Celery('netcelery',
broker=AMQP_URI,
include=['netdriver'])
celery.conf.update(
CELERY_RESULT_BACKEND='amqp',
CELERY_TASK_RESULT_EXPIRES=300,
CELERY_QUEUES=(
Queue(HOSTNAME, Exchange(
'netdriver', type='direct'), routing_key="netdriver"),
)
)
""" CIRCLE driver for Open vSwitch. """
import subprocess
import logging
from netcelery import celery
from os import getenv
from vm import VMNetwork
from vmcelery import native_ovs
driver = getenv("HYPERVISOR_TYPE", "test")
@celery.task
def create(network):
""" Create a network port. """
port_create(VMNetwork.deserialize(network))
@celery.task
def delete(network):
""" Delete a network port. """
port_delete(VMNetwork.deserialize(network))
def add_tuntap_interface(if_name):
""" For testing purpose only adding tuntap interface. """
subprocess.call(['sudo', 'ip', 'tuntap', 'add', 'mode', 'tap', if_name])
def del_tuntap_interface(if_name):
""" For testing purpose only deleting tuntap interface. """
subprocess.call(['sudo', 'ip', 'tuntap', 'del', 'mode', 'tap', if_name])
def ovs_command_execute(command):
""" Execute OpenVSwitch commands.
command - List of strings
return - Command output
"""
command = ['sudo', 'ovs-vsctl'] + command
return_val = subprocess.call(command)
logging.info('OVS command: %s executed.', command)
return return_val
def ofctl_command_execute(command):
""" Execute OpenVSwitch flow commands.
command - List of strings
return - Command output
"""
command = ['sudo', 'ovs-ofctl'] + command
return_val = subprocess.call(command)
logging.info('OVS flow command: %s executed.', command)
return return_val
def build_flow_rule(
in_port=None,
dl_src=None,
protocol=None,
nw_src=None,
ipv6_src=None,
icmp_type=None,
nd_target=None,
tp_dst=None,
priority=None,
actions=None):
"""
Generate flow rule from the parameters.
in_port - Interface flow-port number
dl_src - Source mac addsress (virtual interface)
protocol - Protocol for the rule like ip,ipv6,arp,udp,tcp
nw_src - Source network IP(v4)
ipv6_src - Source network IP(v6)
icmp_type - ICMP/ICMPv6 type
nd_target - IPv6 Neighbor Discovery target IP(v6)
tp_dst - Destination port
priority - Rule priority
actions - Action for the matching rule
return - Open vSwitch compatible flow rule.
"""
flow_rule = ""
if in_port is None:
raise AttributeError("Parameter in_port is mandantory")
parameters = [('in_port=%s', in_port),
('dl_src=%s', dl_src),
('%s', protocol),
('nw_src=%s', nw_src),
('ipv6_src=%s', ipv6_src),
('icmp_type=%s', icmp_type),
('nd_target=%s', nd_target),
('tp_dst=%s', tp_dst),
('priority=%s', priority),
('actions=%s', actions)]
# Checking for values if not None making up rule list
rule = [p1 % p2 for (p1, p2) in parameters if p2 is not None]
# Generate rule string with comas, except the last item
for i in rule[:-1]:
flow_rule += i + ","
flow_rule += rule[-1]
return flow_rule
def set_port_vlan(network_name, vlan):
""" Setting vlan for interface named net_name. """
cmd_list = ['set', 'Port', network_name, 'tag=' + str(vlan)]
ovs_command_execute(cmd_list)
def add_port_to_bridge(network_name, bridge):
""" Add bridge to network_name. """
cmd_list = ['add-port', bridge, network_name]
ovs_command_execute(cmd_list)
def del_port_from_bridge(network_name):
""" Delete network_name port. """
ovs_command_execute(['del-port', network_name])
def mac_filter(network, port_number, remove=False):
""" Apply/Remove mac filtering rule for network. """
if not remove:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
priority="40000", actions="normal")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def ban_dhcp_server(network, port_number, remove=False):
""" Apply/Remove dhcp-server ban rule to network. """
if not remove:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="udp", tp_dst="68",
priority="43000", actions="drop")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="udp", tp_dst="68")
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def ipv4_filter(network, port_number, remove=False):
""" Apply/Remove ipv4 filter rule to network. """
if not remove:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="ip", nw_src=network.ipv4,
priority=42000, actions="normal")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="ip", nw_src=network.ipv4)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def ipv6_filter(network, port_number, remove=False):
""" Apply/Remove ipv6 filter rule to network. """
LINKLOCAL_SUBNET = "FE80::/64"
ICMPv6_NA = "136" # The type of IPv6 Neighbor Advertisement
if not remove:
# Enable Neighbor Advertisement from linklocal address
# if target ip same as network.ipv6
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="icmp6", ipv6_src=LINKLOCAL_SUBNET,
icmp_type=ICMPv6_NA,
nd_target=network.ipv6,
priority=42001, actions="normal")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
# Enable traffic from valid source
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="ipv6", ipv6_src=network.ipv6,
priority=42000, actions="normal")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="icmp6", ipv6_src=LINKLOCAL_SUBNET,
icmp_type=ICMPv6_NA,
nd_target=network.ipv6)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="ipv6", ipv6_src=network.ipv6)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def arp_filter(network, port_number, remove=False):
""" Apply/Remove arp filter rule to network. """
if not remove:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="arp", nw_src=network.ipv4,
priority=41000, actions="normal")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="arp", nw_src=network.ipv4)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def enable_dhcp_client(network, port_number, remove=False):
""" Apply/Remove allow dhcp-client rule to network. """
if not remove:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="udp", tp_dst="67",
priority="40000", actions="normal")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number, dl_src=network.mac,
protocol="udp", tp_dst="67")
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def disable_all_not_allowed_trafic(network, port_number, remove=False):
""" Apply/Remove explicit deny all not allowed network. """
if not remove:
flow_cmd = build_flow_rule(in_port=port_number,
priority="30000", actions="drop")
ofctl_command_execute(["add-flow", network.bridge, flow_cmd])
else:
flow_cmd = build_flow_rule(in_port=port_number)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def port_create(network):
""" Adding port to bridge apply rules and pull up interface. """
# For testing purpose create tuntap iface
if driver == "test":
add_tuntap_interface(network.name)
if not native_ovs:
try:
del_port_from_bridge(network.name)
except:
pass
# Create the port for virtual network
add_port_to_bridge(network.name, network.bridge)
# Set VLAN parameter for tap interface
set_port_vlan(network.name, network.vlan)
# Clear all old rules
clear_port_rules(network)
# Getting network FlowPortNumber
port_number = get_fport_for_network(network)
# Set Flow rules to avoid mac or IP spoofing
if network.managed:
# Allow traffic from fource MAC and IP
ban_dhcp_server(network, port_number)
if network.ipv4 != "None":
ipv4_filter(network, port_number)
if network.ipv6 != "None":
ipv6_filter(network, port_number)
arp_filter(network, port_number)
enable_dhcp_client(network, port_number)
else:
# Allow all traffic from source MAC address
mac_filter(network, port_number)
# Explicit deny all other traffic
disable_all_not_allowed_trafic(network, port_number)
pull_up_interface(network)
def port_delete(network):
""" Remove port from bridge and remove rules from flow database. """
# Clear all port rules
try:
clear_port_rules(network)
except:
pass # Missing port (deleted already)
if not native_ovs:
# Delete port
del_port_from_bridge(network.name)
# For testing purpose dele tuntap iface
if driver == "test":
del_tuntap_interface(network.name)
def clear_port_rules(network):
""" Clear all rules for a port. """
port_number = get_fport_for_network(network)
flow_cmd = build_flow_rule(in_port=port_number)
ofctl_command_execute(["del-flows", network.bridge, flow_cmd])
def pull_up_interface(network):
""" Pull up interface named network.
return command output
"""
command = ['sudo', 'ip', 'link', 'set', 'up', network.name]
return_val = subprocess.call(command)
logging.info('IP command: %s executed.', command)
return return_val
def get_fport_for_network(network):
""" Return the OpenFlow port number for a given network.
Example: ovs-vsctl get Interface vm-88 ofport
return stripped output string
"""
output = subprocess.check_output(
['sudo', 'ovs-vsctl', 'get', 'Interface', network.name, 'ofport'])
return str(output).strip()
celery==3.1.17
decorator==3.4.0
lxml==3.4.2
psutil==1.1.3
Pillow==2.3.0
GitPython==0.3.6
#!/usr/bin/env python
import vm
import vmdriver
# import logging
from nose.tools import raises
graphics = {'type': 'vnc', 'listen':
'0.0.0.0', 'port': '6300', 'passwd': 'asd'}
a = vm.VMNetwork(name="vm-88", mac="02:00:00:00:00:00")
b = vm.VMDisk(name="asd", source='/asdasd/adasds/asd')
testvm = vm.VMInstance(name="Thisthename", vcpu="1",
memory_max="2048",
disk_list=[a],
network_list=[b],
graphics=graphics)
netdict = {'name': "vm-88", 'mac': "02:00:00:00:00:00"}
diskdict = {'name': "asd", 'source': '/asdasd/adasds/asd'}
vmdict = {
'name': "Thisthename",
'vcpu': 1,
'memory_max': 2048,
'disk_list': [diskdict],
'network_list': [netdict],
'graphics': graphics
}
print vm.VMNetwork.deserialize(netdict).dump_xml()
print vm.VMDisk.deserialize(diskdict).dump_xml()
asd = vm.VMInstance.deserialize(vmdict)
print asd.dump_xml()
# Enable logging
# logging.basicConfig(filename='example.log', level=logging.DEBUG)
# print testvm.dump_xml()
# vm_driver = vmdriver.VMDriver()
# vm_driver.connect()
# vm_driver.vm_define(testvm)
# print '%(name)s defined.' % {'name': testvm.name}
# for i in vm_driver.list_domains():
# print i
# #vm_driver.vm_start(i)
# vm_driver.vm_undefine(i)
# print '%(name)s undefined.' % {'name': i}
# vm_driver.disconnect()
@raises(AttributeError)
def test_vm_create_with_None():
vm_driver = vmdriver.VMDriver()
vm_driver.connect()
vm_driver.vm_create(None)
import lxml.etree as ET
from vmcelery import native_ovs
# VM Instance class
class VMInstance:
name = None
arch = None
vm_type = None
os_boot = None
vcpu = None
cpu_share = None
memory_max = None
network_list = list()
disk_list = list()
graphics = dict
raw_data = None
def __init__(self,
name,
vcpu,
memory_max,
memory=None,
emulator='/usr/bin/kvm',
cpu_share=100,
arch="x86_64",
boot_menu=False,
vm_type="test",
network_list=None,
disk_list=None,
graphics=None,
acpi=True,
raw_data="",
boot_token="",
seclabel_type="dynamic",
seclabel_mode="apparmor"):
'''Default Virtual Machine constructor
name - unique name for the instance
vcpu - nubmer of processors
memory_max - maximum virtual memory (actual memory maybe add late)
memory
cpu_share - KVM process priority (0-100)
arch - libvirt arch parameter default x86_64
os_boot - boot device default hd
vm_type - hypervisor type default kvm
network_list - VMNetwork list
disk_list - VMDIsk list
graphics - Dict that keys are: type, listen, port, passwd
acpi - True/False to enable acpi
seclabel_type - libvirt security label type
seclabel_mode - libvirt security mode (selinux, apparmor)
'''
self.name = name
self.emulator = emulator
self.vcpu = vcpu
self.cpu_share = cpu_share
self.memory_max = memory_max
if memory is None:
self.memory = memory_max
else:
self.memory = memory
self.arch = arch
self.boot_menu = boot_menu
self.vm_type = vm_type
self.network_list = network_list
self.disk_list = disk_list
self.graphics = graphics
self.acpi = acpi
self.raw_data = raw_data
self.seclabel_type = seclabel_type
self.seclabel_mode = seclabel_mode
self.boot_token = boot_token
@classmethod
def deserialize(cls, desc):
desc['disk_list'] = [VMDisk.deserialize(d) for d in desc['disk_list']]
desc['network_list'] = [VMNetwork.deserialize(
n) for n in desc['network_list']]
return cls(**desc)
def build_xml(self):
'''Return the root Element Tree object
'''
ET.register_namespace(
'qemu', 'http://libvirt.org/schemas/domain/qemu/1.0')
xml_top = ET.Element(
'domain',
attrib={
'type': self.vm_type
})
# Building raw data into xml
if self.raw_data:
xml_top.append(ET.fromstring(self.raw_data))
# Basic virtual machine paramaters
ET.SubElement(xml_top, 'name').text = self.name
ET.SubElement(xml_top, 'vcpu').text = str(self.vcpu)
cpu = ET.SubElement(xml_top, 'cpu')
ET.SubElement(cpu, 'topology',
attrib={
'sockets': str(1),
'cores': str(self.vcpu),
'threads': str(1)})
ET.SubElement(xml_top, 'memory').text = str(self.memory_max)
ET.SubElement(xml_top, 'currentMemory').text = str(self.memory)
# Cpu tune
cputune = ET.SubElement(xml_top, 'cputune')
ET.SubElement(cputune, 'shares').text = str(self.cpu_share)
# Os specific options
os = ET.SubElement(xml_top, 'os')
ET.SubElement(os, 'type', attrib={'arch': self.arch}).text = "hvm"
ET.SubElement(os, 'bootmenu', attrib={
'enable': "yes" if self.boot_menu else "no"})
# Devices
devices = ET.SubElement(xml_top, 'devices')
ET.SubElement(devices, 'emulator').text = self.emulator
for disk in self.disk_list:
devices.append(disk.build_xml())
for network in self.network_list:
devices.append(network.build_xml())
# Serial console
serial = ET.SubElement(devices,
'console',
attrib={'type': 'unix'})
ET.SubElement(serial,
'target',
attrib={'port': '0'})
ET.SubElement(serial,
'source',
attrib={'mode': 'bind',
'path': '/var/lib/libvirt/serial/%s'
% self.name})
# Virtio console
virtio = ET.SubElement(devices,
'channel',
attrib={'type': 'unix'})
ET.SubElement(virtio,
'target',
attrib={'type': 'virtio', 'name': 'agent'})
ET.SubElement(virtio,
'source',
attrib={'mode': 'bind',
'path': '/var/lib/libvirt/serial/vio-%s'
% self.name})
# Console/graphics section
if self.graphics is not None:
ET.SubElement(devices,
'graphics',
attrib={
'type': self.graphics['type'],
'listen': self.graphics['listen'],
'port': str(self.graphics['port']),
# 'passwd': self.graphics['passwd'],
# TODO: Add this as option
})
ET.SubElement(devices,
'input',
attrib={
'type': 'tablet',
'bus': 'usb', })
# Features (TODO: features as list)
features = ET.SubElement(xml_top, 'features')
if self.acpi:
ET.SubElement(features, 'acpi')
# Security label
ET.SubElement(xml_top, 'seclabel', attrib={
'type': self.seclabel_type,
'mode': self.seclabel_mode
})
return xml_top
def dump_xml(self):
return ET.tostring(self.build_xml(),
encoding='utf8',
method='xml',
pretty_print=True)
class VMDisk:
'''Virtual MAchine disk representing class
'''
name = None
source = None
disk_type = None
disk_device = None
driver_name = None
driver_type = None
driver_cache = None
target_device = None
def __init__(self,
source,
disk_type="file",
disk_device="disk",
driver_name="qemu",
driver_type="qcow2",
driver_cache="none",
target_device="vda",
target_bus="virtio"):
self.source = source
self.disk_type = disk_type
self.disk_device = disk_device
self.driver_name = driver_name
self.driver_type = driver_type
self.driver_cache = driver_cache
self.target_device = target_device
self.target_bus = target_bus
@classmethod
def deserialize(cls, desc):
return cls(**desc)
def build_xml(self):
xml_top = ET.Element('disk',
attrib={'type': self.disk_type,
'device': self.disk_device})
ET.SubElement(xml_top, 'source',
attrib={self.disk_type: self.source})
ET.SubElement(xml_top, 'target',
attrib={'dev': self.target_device,
'bus': self.target_bus})
ET.SubElement(xml_top, 'driver',
attrib={
'name': self.driver_name,
'type': self.driver_type,
'cache': self.driver_cache})
return xml_top
def dump_xml(self):
return ET.tostring(self.build_xml(),
encoding='utf8',
method='xml',
pretty_print=True)
class VMNetwork:
''' Virtual Machine network representing class
name -- network device name
bridge -- bridg for the port
mac -- the MAC address of the quest interface
ipv4 -- the IPv4 address of virtual machine (Flow control)
ipv6 -- the IPv6 address of virtual machine (Flow controlo)
vlan -- Port VLAN configuration
network_type -- need to be "ethernet" by default
model -- available models in libvirt
QoS -- CIRCLE QoS class?
comment -- Any comment
managed -- Apply managed flow rules for spoofing prevent
script -- Executable network script /bin/true by default
'''
# Class attributes
name = None
bridge = None
network_type = None
mac = None
model = None
QoS = None
script_exec = '/bin/true'
comment = None
vlan = 0
ipv4 = None
ipv6 = None
managed = False
def __init__(self,
name,
mac,
bridge="cloud",
ipv4=None,
ipv6=None,
network_type=None,
virtual_port=None,
model='virtio',
QoS=None,
vlan=0,
managed=False):
self.name = name
self.bridge = bridge
self.mac = mac
self.ipv4 = ipv4
self.ipv6 = ipv6
self.model = model
if not network_type:
if native_ovs:
self.network_type = 'bridge'
self.virtual_port = 'openvswitch'
else:
self.network_type = 'ethernet'
self.virtual_port = virtual_port
else:
self.network_type = network_type
self.virtual_port = virtual_port
self.QoS = QoS
self.vlan = vlan
self.managed = managed
@classmethod
def deserialize(cls, desc):
return cls(**desc)
# XML dump
def build_xml(self):
xml_top = ET.Element('interface', attrib={'type': self.network_type})
if self.vlan > 0 and self.network_type == "bridge":
xml_vlan = ET.SubElement(xml_top, 'vlan')
ET.SubElement(xml_vlan, 'tag', attrib={'id': str(self.vlan)})
if self.network_type == "bridge":
ET.SubElement(xml_top, 'source', attrib={'bridge': self.bridge})
if self.network_type == "ethernet":
ET.SubElement(xml_top, 'script', attrib={'path': self.script_exec})
if self.virtual_port is not None:
ET.SubElement(xml_top, 'virtualport',
attrib={'type': self.virtual_port})
ET.SubElement(xml_top, 'target', attrib={'dev': self.name})
ET.SubElement(xml_top, 'mac', attrib={'address': self.mac})
ET.SubElement(xml_top, 'model', attrib={'type': self.model})
# ET.SubElement(xml_top, 'rom', attrib={'bar': 'off'}) Bugged (hot-plug
# failure)
return xml_top
def dump_xml(self):
return ET.tostring(self.build_xml(), encoding='utf8',
method='xml',
pretty_print=True)
""" Celery module for libvirt RPC calls. """
from celery import Celery
from kombu import Queue, Exchange
from os import getenv
from argparse import ArgumentParser
def to_bool(value):
return value.lower() in ("true", "yes", "y", "t")
if to_bool(getenv("LIBVIRT_TEST", "False")):
HOSTNAME = "vmdriver.test"
else:
parser = ArgumentParser()
parser.add_argument("-n", "--hostname", dest="hostname",
help="Define the full queue name with"
"with priority", metavar="hostname.queue.priority")
(args, unknwon_args) = parser.parse_known_args()
HOSTNAME = vars(args).pop("hostname")
if HOSTNAME is None:
raise Exception("You must define hostname as -n <hostname> or "
"--hostname=<hostname>.\n"
"Hostname format must be hostname.module.priority.")
AMQP_URI = getenv('AMQP_URI')
# Global configuration parameters declaration
lib_connection = None
native_ovs = False
if to_bool(getenv('LIBVIRT_KEEPALIVE', "False")):
import libvirt
lib_connection = libvirt.open(getenv('LIBVIRT_URI'))
if to_bool(getenv('NATIVE_OVS', "False")):
native_ovs = True
celery = Celery('vmcelery',
broker=AMQP_URI,
include=['vmdriver'])
celery.conf.update(
CELERY_RESULT_BACKEND='amqp',
CELERY_TASK_RESULT_EXPIRES=300,
CELERY_QUEUES=(
Queue(HOSTNAME, Exchange(
'vmdriver', type='direct'), routing_key="vmdriver"),
)
)
""" Driver for libvirt. """
import libvirt
import logging
import os
import sys
import socket
import json
from decorator import decorator
import lxml.etree as ET
from psutil import NUM_CPUS, virtual_memory, cpu_percent
from celery.contrib.abortable import AbortableTask
from vm import VMInstance, VMDisk, VMNetwork
from vmcelery import celery, lib_connection, to_bool
sys.path.append(os.path.dirname(os.path.basename(__file__)))
vm_xml_dump = None
state_dict = {0: 'NOSTATE',
1: 'RUNNING',
2: 'BLOCKED',
3: 'PAUSED',
4: 'SHUTDOWN',
5: 'SHUTOFF',
6: 'CRASHED',
7: 'PMSUSPENDED'
}
# class Singleton(type):
#
# """ Singleton class."""
#
# _instances = {}
#
# def __call__(cls, *args, **kwargs):
# if cls not in cls._instances:
# cls._instances[cls] = super(Singleton, cls).__call__(*args,
# **kwargs)
# return cls._instances[cls]
class Connection(object):
""" Singleton class to handle connection."""
# __metaclass__ = Singleton
connection = None
@classmethod
def get(cls):
""" Return the libvirt connection."""
return cls.connection
@classmethod
def set(cls, connection):
""" Set the libvirt connection."""
cls.connection = connection
@decorator
def req_connection(original_function, *args, **kw):
"""Connection checking decorator for libvirt.
If envrionment variable LIBVIRT_KEEPALIVE is set
it will use the connection from the celery worker.
Return the decorateed function
"""
logging.debug("Decorator running")
if Connection.get() is None:
connect()
try:
logging.debug("Decorator calling original function")
return_value = original_function(*args, **kw)
finally:
logging.debug("Finally part of decorator")
disconnect()
return return_value
else:
logging.debug("Decorator calling original \
function with active connection")
return_value = original_function(*args, **kw)
return return_value
@decorator
def wrap_libvirtError(original_function, *args, **kw):
""" Decorator to wrap libvirt error in simple Exception.
Return decorated function
"""
try:
return original_function(*args, **kw)
except libvirt.libvirtError as e:
logging.error(e.get_error_message())
e_msg = e.get_error_message()
if vm_xml_dump is not None:
e_msg += "\n"
e_msg += vm_xml_dump
new_e = Exception(e.get_error_message())
new_e.libvirtError = True
raise new_e
@wrap_libvirtError
def connect(connection_string='qemu:///system'):
""" Connect to the libvirt daemon.
String is specified in the connection_string parameter
the default is the local root.
"""
if not to_bool(os.getenv('LIBVIRT_KEEPALIVE', "False")):
if Connection.get() is None:
Connection.set(libvirt.open(connection_string))
logging.debug("Connection estabilished to libvirt.")
else:
logging.debug("There is already an active connection to libvirt.")
else:
Connection.set(lib_connection)
logging.debug("Using celery libvirt connection connection.")
@wrap_libvirtError
def disconnect():
""" Disconnect from the active libvirt daemon connection."""
if os.getenv('LIBVIRT_KEEPALIVE') is None:
if Connection.get() is None:
logging.debug('There is no available libvirt conection.')
else:
Connection.get().close()
logging.debug('Connection closed to libvirt.')
Connection.set(None)
else:
logging.debug('Keepalive connection should not close.')
@celery.task
@req_connection
@wrap_libvirtError
def define(vm):
""" Define permanent virtual machine from xml. """
Connection.get().defineXML(vm.dump_xml())
logging.info("Virtual machine %s is defined from xml", vm.name)
@celery.task
@req_connection
@wrap_libvirtError
def create(vm_desc):
""" Create and start non-permanent virtual machine from xml.
Return the domain info dict.
flags can be:
VIR_DOMAIN_NONE = 0
VIR_DOMAIN_START_PAUSED = 1
VIR_DOMAIN_START_AUTODESTROY = 2
VIR_DOMAIN_START_BYPASS_CACHE = 4
VIR_DOMAIN_START_FORCE_BOOT = 8
"""
vm = VMInstance.deserialize(vm_desc)
# Setting proper hypervisor
vm.vm_type = os.getenv("HYPERVISOR_TYPE", "test")
if vm.vm_type == "test":
vm.arch = "i686"
vm_xml_dump = vm.dump_xml()
logging.info(vm_xml_dump)
# Emulating DOMAIN_START_PAUSED FLAG behaviour on test driver
if vm.vm_type == "test":
Connection.get().createXML(
vm_xml_dump, libvirt.VIR_DOMAIN_NONE)
domain = lookupByName(vm.name)
domain.suspend()
# Real driver create
else:
Connection.get().createXML(
vm_xml_dump, libvirt.VIR_DOMAIN_START_PAUSED)
logging.info("Virtual machine %s is created from xml", vm.name)
# context
try:
sock = socket.create_connection(('127.0.0.1', 1235), 3)
data = {'boot_token': vm.boot_token,
'socket': '/var/lib/libvirt/serial/%s' % vm.name}
sock.sendall(json.dumps(data))
sock.close()
except socket.error:
logging.error('Unable to connect to context server')
return vm_xml_dump
class shutdown(AbortableTask):
""" Shutdown virtual machine (need ACPI support).
Return When domain is missiing.
This job is abortable:
AbortableAsyncResult(id="<<jobid>>").abort()
"""
time_limit = 120
@req_connection
def run(self, args):
from time import sleep
name, = args
logging.info("Shutdown started for vm: %s", name)
try:
domain = lookupByName(name)
logging.info("%s domain found in shutdown", name)
domain.shutdown()
logging.info("Domain shutdown called for vm: %s", name)
while True:
try:
Connection.get().lookupByName(name)
except libvirt.libvirtError as e:
if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
return
else:
raise
else:
if self.is_aborted():
logging.info("Shutdown aborted on vm: %s", name)
return
sleep(5)
except libvirt.libvirtError as e:
new_e = Exception(e.get_error_message())
new_e.libvirtError = True
raise new_e
@celery.task
@req_connection
@wrap_libvirtError
def delete(name):
""" Destroy the running called 'name' virtual machine. """
domain = lookupByName(name)
domain.destroy()
@celery.task
@req_connection
@wrap_libvirtError
def list_domains():
""" List the running domains.
:return list: List of domains name in host.
"""
domain_list = []
for i in Connection.get().listDomainsID():
dom = Connection.get().lookupByID(i)
domain_list.append(dom.name())
return domain_list
@celery.task
@req_connection
@wrap_libvirtError
def list_domains_info():
""" List the running domains.
:return list: List of domains info dict.
"""
domain_list = []
for i in Connection.get().listDomainsID():
dom = Connection.get().lookupByID(i)
domain_dict = _parse_info(dom.info())
domain_dict['name'] = dom.name()
domain_list.append(domain_dict)
return domain_list
@celery.task
@req_connection
@wrap_libvirtError
def lookupByName(name):
""" Return with the requested Domain. """
return Connection.get().lookupByName(name)
@celery.task
@req_connection
@wrap_libvirtError
def undefine(name):
""" Undefine an already defined virtual machine.
If it's running it becomes transient (lost on reboot)
"""
domain = lookupByName(name)
domain.undefine()
@celery.task
@req_connection
@wrap_libvirtError
def start(name):
""" Start an already defined virtual machine."""
domain = lookupByName(name)
domain.create()
@celery.task
@req_connection
@wrap_libvirtError
def suspend(name):
""" Stop virtual machine and keep memory in RAM.
Return the domain info dict.
"""
domain = lookupByName(name)
domain.suspend()
return _parse_info(domain.info())
@celery.task
@req_connection
@wrap_libvirtError
def save(name, path):
""" Stop virtual machine and save its memory to path. """
domain = lookupByName(name)
domain.save(path)
@celery.task
@req_connection
@wrap_libvirtError
def restore(name, path):
""" Restore a saved virtual machine.
Restores the virtual machine from the memory image
stored at path.
Return the domain info dict.
"""
Connection.get().restore(path)
return domain_info(name)
@celery.task
@req_connection
@wrap_libvirtError
def resume(name):
""" Resume stopped virtual machines.
Return the domain info dict.
"""
domain = lookupByName(name)
domain.resume()
return _parse_info(domain.info())
@celery.task
@req_connection
@wrap_libvirtError
def reset(name):
""" Reset (power reset) virtual machine.
Return the domain info dict.
"""
domain = lookupByName(name)
domain.reset(0)
return _parse_info(domain.info())
@celery.task
@req_connection
@wrap_libvirtError
def reboot(name):
""" Reboot (with guest acpi support) virtual machine.
Return the domain info dict.
"""
domain = lookupByName(name)
domain.reboot(0)
return _parse_info(domain.info())
@celery.task
@req_connection
@wrap_libvirtError
def node_info():
""" Get info from Host as dict.
Return dict:
model string indicating the CPU model
memory memory size in kilobytes
cpus the number of active CPUs
mhz expected CPU frequency
nodes the number of NUMA cell, 1 for unusual NUMA
topologies or uniform memory access;
check capabilities XML for the actual NUMA topology
sockets number of CPU sockets per node if nodes > 1,
1 in case of unusual NUMA topology
cores number of cores per socket, total number of
processors in case of unusual NUMA topolog
threads number of threads per core, 1 in case of unusual numa topology
"""
keys = ['model', 'memory', 'cpus', 'mhz',
'nodes', 'sockets', 'cores', 'threads']
values = Connection.get().getInfo()
return dict(zip(keys, values))
def _parse_info(values):
""" Parse libvirt domain info into dict.
Return the info dict.
"""
keys = ['state', 'maxmem', 'memory', 'virtcpunum', 'cputime']
info = dict(zip(keys, values))
# Change state to proper ENUM
info['state'] = state_dict[info['state']]
return info
@celery.task
@req_connection
@wrap_libvirtError
def domain_info(name):
""" Get the domain info from libvirt.
Return the domain info dict:
state the running state, one of virDomainState
maxmem the maximum memory in KBytes allowed
memory the memory in KBytes used by the domain
virtcpunum the number of virtual CPUs for the domain
cputime the CPU time used in nanoseconds
"""
dom = lookupByName(name)
return _parse_info(dom.info())
@celery.task
@req_connection
@wrap_libvirtError
def network_info(name, network):
""" Return the network info dict.
rx_bytes
rx_packets
rx_errs
rx_drop
tx_bytes
tx_packets
tx_errs
tx_drop
"""
keys = ['rx_bytes', 'rx_packets', 'rx_errs', 'rx_drop',
'tx_bytes', 'tx_packets', 'tx_errs', 'tx_drop']
dom = lookupByName(name)
values = dom.interfaceStats(network)
info = dict(zip(keys, values))
return info
@celery.task
@req_connection
@wrap_libvirtError
def send_key(name, key_code):
""" Sending linux key_code to the name vm.
key_code can be optained from linux_keys.py
e.x: linuxkeys.KEY_RIGHTCTRL
"""
domain = lookupByName(name)
domain.sendKey(libvirt.VIR_KEYCODE_SET_LINUX, 100, [key_code], 1, 0)
def _stream_handler(stream, buf, opaque):
opaque.write(buf)
@celery.task
@req_connection
@wrap_libvirtError
def screenshot(name):
"""Save screenshot of virtual machine.
Returns a ByteIO object that contains the screenshot in png format.
"""
from io import BytesIO
from PIL import Image
# Import linuxkeys to get defines
import linuxkeys
# Connection need for the stream object
domain = lookupByName(name)
# Send key to wake up console
domain.sendKey(libvirt.VIR_KEYCODE_SET_LINUX,
100, [linuxkeys.KEY_RIGHTCTRL], 1, 0)
# Create Stream to get data
stream = Connection.get().newStream(0)
# Take screenshot accessible by stream (return mimetype)
domain.screenshot(stream, 0, 0)
# Get file to save data (send on AMQP?)
fd = BytesIO()
try:
# Save data with handler
stream.recvAll(_stream_handler, fd)
finally:
stream.finish()
# Convert ppm to png
# Seek to the beginning of the stream
fd.seek(0)
# Get the image
image = BytesIO()
ppm = Image.open(fd)
ppm.save(image, format='PNG')
return image
@celery.task
@req_connection
@wrap_libvirtError
def migrate(name, host, live=False):
""" Migrate domain to host. """
flags = libvirt.VIR_MIGRATE_PEER2PEER
if live:
flags = flags | libvirt.VIR_MIGRATE_LIVE
domain = lookupByName(name)
domain.migrateToURI(
duri="qemu+tcp://" + host + "/system",
flags=flags,
dname=name,
bandwidth=0)
# return _parse_info(domain.info())
@celery.task
@req_connection
@wrap_libvirtError
def attach_disk(name, disk):
""" Attach Disk to a running virtual machine. """
domain = lookupByName(name)
disk = VMDisk.deserialize(disk)
domain.attachDevice(disk.dump_xml())
@celery.task
@req_connection
@wrap_libvirtError
def detach_disk(name, disk):
""" Detach disk from a running virtual machine. """
domain = lookupByName(name)
disk = VMDisk.deserialize(disk)
domain.detachDevice(disk.dump_xml())
# Libvirt does NOT report failed detach so test it.
__check_detach(domain, disk.source)
def __check_detach(domain, disk):
""" Test if detach was successfull by searching
for disk in the XML"""
xml = domain.XMLDesc()
root = ET.fromstring(xml)
devices = root.find('devices')
for d in devices.findall("disk"):
if disk in d.find('source').attrib.values()[0]:
raise Exception("Disk could not been detached. "
"Check if hot plug support is "
"enabled (acpiphp module on Linux).")
@celery.task
@req_connection
@wrap_libvirtError
def attach_network(name, net):
domain = lookupByName(name)
net = VMNetwork.deserialize(net)
logging.error(net.dump_xml())
domain.attachDevice(net.dump_xml())
@celery.task
@req_connection
@wrap_libvirtError
def detach_network(name, net):
domain = lookupByName(name)
net = VMNetwork.deserialize(net)
domain.detachDevice(net.dump_xml())
@celery.task
@req_connection
@wrap_libvirtError
def resize_disk(name, path, size):
domain = lookupByName(name)
# domain.blockResize(path, int(size),
# flags=libvirt.VIR_DOMAIN_BLOCK_RESIZE_BYTES)
# To be compatible with libvirt < 0.9.11
domain.blockResize(path, int(size)/1024, 0)
@celery.task
def ping():
return True
@celery.task
@req_connection
@wrap_libvirtError
def get_architecture():
xml = Connection.get().getCapabilities()
return ET.fromstring(xml).getchildren()[0].getchildren(
)[1].getchildren()[0].text
@celery.task
def get_core_num():
return NUM_CPUS
@celery.task
def get_ram_size():
return virtual_memory().total
@celery.task
def get_driver_version():
from git import Repo
try:
repo = Repo(path=os.getcwd())
lc = repo.head.commit
return {'branch': repo.active_branch.name,
'commit': lc.hexsha,
'commit_text': lc.summary,
'is_dirty': repo.is_dirty()}
except Exception as e:
logging.exception("Unhandled exception: %s", e)
return None
@celery.task
def get_info():
return {'core_num': get_core_num(),
'ram_size': get_ram_size(),
'architecture': get_architecture(),
'driver_version': get_driver_version()}
@celery.task
def get_node_metrics():
result = {}
result['cpu.usage'] = cpu_percent(0)
result['memory.usage'] = virtual_memory().percent
return result
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment