Commit 69950ba5 authored by Alain Takoudjou's avatar Alain Takoudjou

kvm: move slapos.cookbook:kvm recipe to buildout+jinja

parent 4edaea13
...@@ -13,10 +13,6 @@ from random import shuffle ...@@ -13,10 +13,6 @@ from random import shuffle
import glob import glob
import re import re
import ssl
opener = urllib.FancyURLopener(context=ssl._create_unverified_context())
# XXX: give all of this through parameter, don't use this as template, but as module # XXX: give all of this through parameter, don't use this as template, but as module
qemu_img_path = '%(qemu-img-path)s' qemu_img_path = '%(qemu-img-path)s'
qemu_path = '%(qemu-path)s' qemu_path = '%(qemu-path)s'
...@@ -131,7 +127,7 @@ if not os.path.exists(disk_path) and virtual_hard_drive_url != '': ...@@ -131,7 +127,7 @@ if not os.path.exists(disk_path) and virtual_hard_drive_url != '':
downloaded_disk = disk_path downloaded_disk = disk_path
if virtual_hard_drive_gzipped == 'true': if virtual_hard_drive_gzipped == 'true':
downloaded_disk = '%%s.gz' %% disk_path downloaded_disk = '%%s.gz' %% disk_path
opener.retrieve(virtual_hard_drive_url, downloaded_disk) urllib.urlretrieve(virtual_hard_drive_url, downloaded_disk)
except: except:
if os.path.exists(downloaded_disk): if os.path.exists(downloaded_disk):
os.remove(downloaded_disk) os.remove(downloaded_disk)
......
...@@ -90,7 +90,7 @@ command = ...@@ -90,7 +90,7 @@ command =
[template] [template]
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance.cfg.in url = ${:_profile_base_location_}/instance.cfg.in
md5sum = 41eb12b66bd1af86b1b98b2166e86417 md5sum = ac94fdcf8e3db4bdb2dff4478426595d
output = ${buildout:directory}/template.cfg output = ${buildout:directory}/template.cfg
mode = 0644 mode = 0644
...@@ -98,7 +98,7 @@ mode = 0644 ...@@ -98,7 +98,7 @@ mode = 0644
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2 url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2
mode = 644 mode = 644
md5sum = 90309e9a1efe6d6b981a27f503c86277 md5sum = 4056df213786fd87b60efd3d6f1f2bec
download-only = true download-only = true
on-update = true on-update = true
...@@ -106,7 +106,7 @@ on-update = true ...@@ -106,7 +106,7 @@ on-update = true
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in
mode = 644 mode = 644
md5sum = 0e06ef2879454eee140fe00e167a01d5 md5sum = 8e84c7a4e7be009021243c14707e0a1e
download-only = true download-only = true
on-update = true on-update = true
...@@ -114,7 +114,7 @@ on-update = true ...@@ -114,7 +114,7 @@ on-update = true
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/instance-kvm-resilient.cfg.jinja2 url = ${:_profile_base_location_}/instance-kvm-resilient.cfg.jinja2
mode = 644 mode = 644
md5sum = 450a50069f5617993ac8de47d533d653 md5sum = 7564bfbb74e6557e1041e9d6d1bc5d14
download-only = true download-only = true
on-update = true on-update = true
...@@ -179,6 +179,24 @@ mode = 0644 ...@@ -179,6 +179,24 @@ mode = 0644
download-only = true download-only = true
filename = ansible-promise.in filename = ansible-promise.in
[template-kvm-run]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/template/template-kvm-run.in
mode = 644
filename = template-kvm-run.in
md5sum = 38265d52fdc03589081cc7dd13999020
download-only = true
on-update = true
[template-kvm-controller]
recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/template/kvm-controller-run.in
mode = 644
filename = kvm-controller-run.in
md5sum = b61ef9c54d912fdbfed3899fa985f79c
download-only = true
on-update = true
[template-apache-conf] [template-apache-conf]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/template/apache.conf.in url = ${:_profile_base_location_}/template/apache.conf.in
......
...@@ -242,23 +242,6 @@ ...@@ -242,23 +242,6 @@
"minimum": 1, "minimum": 1,
"maximum": 65535 "maximum": 65535
}, },
"nbd2-host": {
"title": "Second NBD hostname or IP",
"description": "hostname (or IP) of the second NBD server (containing drivers for example).",
"type": "string",
"format": [
"host-name",
"ip-address",
"ipv6"
]
},
"nbd2-port": {
"title": "Second NBD port",
"description": "Port of the second NBD server containing the boot image.",
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"virtual-hard-drive-url": { "virtual-hard-drive-url": {
"title": "Existing disk image URL", "title": "Existing disk image URL",
"description": "If specified, will download an existing disk image (qcow2, raw, ...), and will use it as main virtual hard drive. Can be used to download and use an already installed and customized virtual hard drive.", "description": "If specified, will download an existing disk image (qcow2, raw, ...), and will use it as main virtual hard drive. Can be used to download and use an already installed and customized virtual hard drive.",
...@@ -276,6 +259,12 @@ ...@@ -276,6 +259,12 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"hard-drive-url-check-certificate": {
"title": "Check certificate when downloading virtual hard drive from https.",
"description": "Define if certificate should be checked when downloading virtual hard drive from https url.",
"type": "boolean",
"default": true
},
"external-disk-number": { "external-disk-number": {
"title": "Number of additional disk to create for virtual machine", "title": "Number of additional disk to create for virtual machine",
"description": "Specify the number of additional disk to create for virtual machine in data folder of SlapOS Node. Requires instance_storage_home to be configured on SlapOS Node.", "description": "Specify the number of additional disk to create for virtual machine in data folder of SlapOS Node. Requires instance_storage_home to be configured on SlapOS Node.",
......
...@@ -60,6 +60,7 @@ config-enable-vhost = {{ dumps(kvm_parameter_dict.get('enable-vhost', False)) }} ...@@ -60,6 +60,7 @@ config-enable-vhost = {{ dumps(kvm_parameter_dict.get('enable-vhost', False)) }}
config-virtual-hard-drive-url = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-url', '')) }} config-virtual-hard-drive-url = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-url', '')) }}
config-virtual-hard-drive-md5sum = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-md5sum', '')) }} config-virtual-hard-drive-md5sum = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-md5sum', '')) }}
config-virtual-hard-drive-gzipped = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-gzipped', False)) }} config-virtual-hard-drive-gzipped = {{ dumps(kvm_parameter_dict.get('virtual-hard-drive-gzipped', False)) }}
config-hard-drive-url-check-certificate = {{ dumps(kvm_parameter_dict.get('hard-drive-url-check-certificate', True)) }}
config-external-disk-number = {{ dumps(kvm_parameter_dict.get('external-disk-number', 0)) }} config-external-disk-number = {{ dumps(kvm_parameter_dict.get('external-disk-number', 0)) }}
config-external-disk-size = {{ dumps(kvm_parameter_dict.get('external-disk-size', 20)) }} config-external-disk-size = {{ dumps(kvm_parameter_dict.get('external-disk-size', 20)) }}
config-external-disk-format = {{ dumps(kvm_parameter_dict.get('external-disk-format', 'qcow2')) }} config-external-disk-format = {{ dumps(kvm_parameter_dict.get('external-disk-format', 'qcow2')) }}
......
...@@ -98,6 +98,12 @@ ...@@ -98,6 +98,12 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"hard-drive-url-check-certificate": {
"title": "Check certificate when downloading virtual hard drive from https.",
"description": "Define if certificate should be checked when downloading virtual hard drive from https url.",
"type": "boolean",
"default": true
},
"external-disk-number": { "external-disk-number": {
"title": "Number of additional disk to create for virtual machine", "title": "Number of additional disk to create for virtual machine",
......
...@@ -13,7 +13,7 @@ offline = true ...@@ -13,7 +13,7 @@ offline = true
# += because we need to take up parts (like instance-custom, slapmonitor etc) from the profile we extended # += because we need to take up parts (like instance-custom, slapmonitor etc) from the profile we extended
parts += parts +=
{{ parts.replicate("kvm", backup_amount) }} {{ parts.replicate("kvm", backup_amount) }}
publish-connection-informations publish-connection-information
kvm-frontend-url-promise kvm-frontend-url-promise
kvm-backend-url-promise kvm-backend-url-promise
...@@ -55,6 +55,6 @@ mode = 700 ...@@ -55,6 +55,6 @@ mode = 700
# Check that backend url is reachable # Check that backend url is reachable
recipe = slapos.cookbook:check_url_available recipe = slapos.cookbook:check_url_available
path = ${directory:promises}/frontend_promise path = ${directory:promises}/frontend_promise
url = ${publish-connection-informations:url} url = ${publish-connection-information:url}
dash_path = /bin/sh dash_path = /bin/sh
curl_path = {{ curl_executable_location }} curl_path = {{ curl_executable_location }}
...@@ -3,77 +3,15 @@ ...@@ -3,77 +3,15 @@
{% set use_nat = slapparameter_dict.get('use-nat', 'True').lower() -%} {% set use_nat = slapparameter_dict.get('use-nat', 'True').lower() -%}
{% set name = slapparameter_dict.get('name', 'localhost') -%} {% set name = slapparameter_dict.get('name', 'localhost') -%}
{% set monitor = slapparameter_dict.get('enable-monitor', 'True').lower() -%} {% set monitor = slapparameter_dict.get('enable-monitor', 'True').lower() -%}
{% set disable_ansible_promise = slapparameter_dict.get('disable-ansible-promise', 'False').lower() -%}
{% set frontend_software_type = 'default' -%} {% set frontend_software_type = 'default' -%}
{% set extends_list = [] -%} {% set extends_list = [] -%}
{% set part_list = [] -%}
{% if monitor -%} {% if monitor -%}
{% do extends_list.append(template_monitor) -%} {% do extends_list.append(template_monitor) -%}
{% endif -%} {% endif -%}
{% do extends_list.append(logrotate_cfg) -%} {% do extends_list.append(logrotate_cfg) -%}
#############################
#
# Instanciate kvm
#
#############################
[buildout]
parts =
certificate-authority
publish-connection-information
kvm-vnc-promise
kvm-disk-image-corruption-promise
websockify-sighandler
novnc-promise
# kvm-monitor
cron
# cron-entry-monitor
frontend-promise
{% if monitor -%}
# monitor parts
cron-entry-monitor
cron-entry-rss
deploy-index
deploy-status-history-cgi
deploy-status-cgi
# deploy-logfile-cgi
# deploy-resource-consumption-monitoring-cgi
setup-static-files
public-symlink
cgi-httpd-wrapper
cgi-httpd-graceful-wrapper
monitor-promise
monitor-instance-log-access
monitor-access-log
monitor-access-public
# monitor-frontend-promise
{% endif -%}
{% if slapparameter_dict.get('document-host', '') %}
cluster-url-path
{% endif -%}
{% if enable_http == 'true' %}
httpd
httpd-promise
publish-host-config
{% if slapparameter_dict.get('data-to-vm', '') %}
vm-data-content
{% endif -%}
{% if use_tap == 'true' and tap_network_dict.has_key('ipv4') %}
ansible-vm-promise
logrotate-vm-bootstrap
{% endif -%}
{% if slapparameter_dict.get('authorized-key', '') %}
get-authorized-key
{% endif -%}
{% endif -%}
extends =
# Add extends list
{{ extends_list | join('\n ') }}
# {{ template_httpd_cfg }}
eggs-directory = {{ eggs_directory }}
develop-eggs-directory = {{ develop_eggs_directory }}
offline = true
[directory] [directory]
recipe = slapos.cookbook:mkdirectory recipe = slapos.cookbook:mkdirectory
...@@ -106,22 +44,19 @@ recipe = slapos.cookbook:generate.password ...@@ -106,22 +44,19 @@ recipe = slapos.cookbook:generate.password
storage-path = ${directory:srv}/passwd storage-path = ${directory:srv}/passwd
bytes = 8 bytes = 8
[kvm-controller-parameter-dict]
[kvm-instance] python-path = {{ python_executable }}
# XXX-Cedric: change "KVM" recipe to simple "create wrappers". No need for this
# Specific code. It needs Jinja.
recipe = slapos.cookbook:kvm
vnc-passwd = ${gen-passwd:passwd} vnc-passwd = ${gen-passwd:passwd}
socket-path = ${directory:var}/qmp_socket
[kvm-parameter-dict]
python-path = {{ python_executable }}
ipv4 = ${slap-network-information:local-ipv4} ipv4 = ${slap-network-information:local-ipv4}
ipv6 = ${slap-network-information:global-ipv6} ipv6 = ${slap-network-information:global-ipv6}
vnc-ip = ${:ipv4} vnc-ip = ${:ipv4}
vnc-port = 5901 vnc-port = 5901
# XXX-Cedric: should be named "default-cdrom-iso" default-cdrom-iso = {{ debian_amd64_netinst_location }}
default-disk-image = {{ debian_amd64_netinst_location }}
nbd-host = ${slap-parameter:nbd-host} nbd-host = ${slap-parameter:nbd-host}
nbd-port = ${slap-parameter:nbd-port} nbd-port = ${slap-parameter:nbd-port}
nbd2-host = ${slap-parameter:nbd2-host} nbd2-host = ${slap-parameter:nbd2-host}
...@@ -133,8 +68,9 @@ disk-path = ${directory:srv}/virtual.qcow2 ...@@ -133,8 +68,9 @@ disk-path = ${directory:srv}/virtual.qcow2
disk-size = ${slap-parameter:disk-size} disk-size = ${slap-parameter:disk-size}
disk-type = ${slap-parameter:disk-type} disk-type = ${slap-parameter:disk-type}
socket-path = ${directory:var}/qmp_socket
pid-file-path = ${directory:run}/pid_file pid-file-path = ${directory:run}/pid_file
socket-path = ${kvm-controller-parameter-dict:socket-path}
smp-count = ${slap-parameter:cpu-count} smp-count = ${slap-parameter:cpu-count}
smp-options = ${slap-parameter:cpu-options} smp-options = ${slap-parameter:cpu-options}
...@@ -143,24 +79,19 @@ numa = ${slap-parameter:numa} ...@@ -143,24 +79,19 @@ numa = ${slap-parameter:numa}
mac-address = ${create-mac:mac-address} mac-address = ${create-mac:mac-address}
tap-mac-address = ${create-tap-mac:mac-address} tap-mac-address = ${create-tap-mac:mac-address}
# XXX-Cedric: should be named runner-wrapper-path and controller-wrapper-path
runner-path = ${directory:services}/kvm
controller-path = ${directory:scripts}/kvm_controller
use-tap = ${slap-parameter:use-tap} use-tap = ${slap-parameter:use-tap}
use-nat = ${slap-parameter:use-nat} use-nat = ${slap-parameter:use-nat}
nat-rules = ${slap-parameter:nat-rules} nat-rules = ${slap-parameter:nat-rules}
enable-vhost = ${slap-parameter:enable-vhost} enable-vhost = ${slap-parameter:enable-vhost}
6tunnel-wrapper-path = ${directory:services}/6tunnel
virtual-hard-drive-url = ${slap-parameter:virtual-hard-drive-url} virtual-hard-drive-url = ${slap-parameter:virtual-hard-drive-url}
virtual-hard-drive-md5sum = ${slap-parameter:virtual-hard-drive-md5sum} virtual-hard-drive-md5sum = ${slap-parameter:virtual-hard-drive-md5sum}
virtual-hard-drive-gzipped = ${slap-parameter:virtual-hard-drive-gzipped} virtual-hard-drive-gzipped = ${slap-parameter:virtual-hard-drive-gzipped}
hard-drive-url-check-certificate = ${slap-parameter:hard-drive-url-check-certificate}
shell-path = {{ dash_executable_location }} shell-path = {{ dash_executable_location }}
qemu-path = {{ qemu_executable_location }} qemu-path = {{ qemu_executable_location }}
qemu-img-path = {{ qemu_img_executable_location }} qemu-img-path = {{ qemu_img_executable_location }}
6tunnel-path = {{ sixtunnel_executable_location }}
etc-directory = ${directory:etc} etc-directory = ${directory:etc}
disk-storage-list = disk-storage-list =
...@@ -188,11 +119,55 @@ cluster-doc-port = 0 ...@@ -188,11 +119,55 @@ cluster-doc-port = 0
netcat-binary = {{ netcat_bin }} netcat-binary = {{ netcat_bin }}
language = ${slap-parameter:keyboard-layout-language} language = ${slap-parameter:keyboard-layout-language}
[kvm-run]
recipe = slapos.recipe.template:jinja2
template = {{ template_kvm_run }}
rendered = ${directory:bin}/kvm_raw
mode = 700
context =
section parameter_dict kvm-parameter-dict
[kvm-contoller]
recipe = slapos.recipe.template:jinja2
template = {{ template_kvm_controller_run }}
rendered = ${directory:scripts}/kvm_controller
mode = 700
context =
section parameter_dict kvm-controller-parameter-dict
[tunnel-6to4-base]
recipe = slapos.cookbook:wrapper
ipv4 = ${slap-network-information:local-ipv4}
ipv6 = ${slap-network-information:global-ipv6}
wrapper-path = ${directory:services}/6tunnel-${:ipv6-port}
command-line = {{ sixtunnel_executable_location }} -6 -4 -d -l ${:ipv6} ${:ipv6-port} ${:ipv4} ${:ipv4-port}
{% if use_nat == 'true' -%}
{% set nat_rule_list = slapparameter_dict.get('nat-rules', '22 80 443') %}
{% for port in nat_rule_list.split(' ') -%}
{% set external_port = 10000 + port|int() -%}
{% set section_name = '6tunnel-' ~ external_port -%}
[{{ section_name }}]
<= tunnel-6to4-base
ipv4-port = {{ external_port }}
ipv6-port = {{ external_port }}
{% do part_list.append(section_name) -%}
{% endfor -%}
{% endif -%}
[kvm-instance]
recipe = slapos.cookbook:wrapper
socket-path = ${kvm-controller-parameter-dict:socket-path}
wrapper-path = ${directory:services}/kvm
command-line = ${kvm-run:rendered}
kvm-controller = ${kvm-contoller:rendered}
[kvm-vnc-promise] [kvm-vnc-promise]
recipe = slapos.cookbook:check_port_listening recipe = slapos.cookbook:check_port_listening
path = ${directory:promises}/vnc_promise path = ${directory:promises}/vnc_promise
hostname = ${kvm-instance:vnc-ip} hostname = ${kvm-parameter-dict:vnc-ip}
port = ${kvm-instance:vnc-port} port = ${kvm-parameter-dict:vnc-port}
[kvm-disk-image-corruption-promise] [kvm-disk-image-corruption-promise]
# Check that disk image is not corrupted # Check that disk image is not corrupted
...@@ -201,7 +176,7 @@ input = inline:#!/bin/sh ...@@ -201,7 +176,7 @@ input = inline:#!/bin/sh
# Return code 0 is "OK" # Return code 0 is "OK"
# Return code 3 is "found leaks, but image is OK" # Return code 3 is "found leaks, but image is OK"
# http://git.qemu.org/?p=qemu.git;a=blob;f=qemu-img.c;h=4e9a7f5741c9cb863d978225829e68fefcae3947;hb=HEAD#l702 # http://git.qemu.org/?p=qemu.git;a=blob;f=qemu-img.c;h=4e9a7f5741c9cb863d978225829e68fefcae3947;hb=HEAD#l702
${kvm-instance:qemu-img-path} check ${kvm-instance:disk-path} ${kvm-parameter-dict:qemu-img-path} check ${kvm-parameter-dict:disk-path}
RETURN_CODE=$? RETURN_CODE=$?
if [ $RETURN_CODE -eq 0 ] || [ $RETURN_CODE -eq 3 ]; then if [ $RETURN_CODE -eq 0 ] || [ $RETURN_CODE -eq 3 ]; then
exit 0 exit 0
...@@ -217,8 +192,8 @@ recipe = slapos.cookbook:novnc ...@@ -217,8 +192,8 @@ recipe = slapos.cookbook:novnc
path = ${ca-novnc:executable} path = ${ca-novnc:executable}
ip = ${slap-network-information:global-ipv6} ip = ${slap-network-information:global-ipv6}
port = 6080 port = 6080
vnc-ip = ${kvm-instance:vnc-ip} vnc-ip = ${kvm-parameter-dict:vnc-ip}
vnc-port = ${kvm-instance:vnc-port} vnc-port = ${kvm-parameter-dict:vnc-port}
novnc-location = {{ novnc_location }} novnc-location = {{ novnc_location }}
websockify-path = {{ websockify_executable_location }} websockify-path = {{ websockify_executable_location }}
ssl-key-path = ${ca-novnc:key-file} ssl-key-path = ${ca-novnc:key-file}
...@@ -366,8 +341,8 @@ check-secure = 1 ...@@ -366,8 +341,8 @@ check-secure = 1
[publish-connection-information] [publish-connection-information]
recipe = slapos.cookbook:publish recipe = slapos.cookbook:publish
ipv6 = ${slap-network-information:global-ipv6} ipv6 = ${slap-network-information:global-ipv6}
backend-url = https://[${novnc-instance:ip}]:${novnc-instance:port}/vnc_auto.html?host=[${novnc-instance:ip}]&port=${novnc-instance:port}&encrypt=1&password=${kvm-instance:vnc-passwd} backend-url = https://[${novnc-instance:ip}]:${novnc-instance:port}/vnc_auto.html?host=[${novnc-instance:ip}]&port=${novnc-instance:port}&encrypt=1&password=${kvm-controller-parameter-dict:vnc-passwd}
url = ${request-slave-frontend:connection-url}/vnc_auto.html?host=${request-slave-frontend:connection-domainname}&port=${request-slave-frontend:connection-port}&encrypt=1&path=${request-slave-frontend:connection-resource}&password=${kvm-instance:vnc-passwd} url = ${request-slave-frontend:connection-url}/vnc_auto.html?host=${request-slave-frontend:connection-domainname}&port=${request-slave-frontend:connection-port}&encrypt=1&path=${request-slave-frontend:connection-resource}&password=${kvm-controller-parameter-dict:vnc-passwd}
{% set disk_number = len(storage_dict) -%} {% set disk_number = len(storage_dict) -%}
maximum-extra-disk-amount = {{ disk_number }} maximum-extra-disk-amount = {{ disk_number }}
{% set iface = 'eth0' -%} {% set iface = 'eth0' -%}
...@@ -378,9 +353,9 @@ maximum-extra-disk-amount = {{ disk_number }} ...@@ -378,9 +353,9 @@ maximum-extra-disk-amount = {{ disk_number }}
{% set nat_rule_list = slapparameter_dict.get('nat-rules', '22 80 443') %} {% set nat_rule_list = slapparameter_dict.get('nat-rules', '22 80 443') %}
{% for port in nat_rule_list.split(' ') -%} {% for port in nat_rule_list.split(' ') -%}
{% set external_port = 10000 + port|int() -%} {% set external_port = 10000 + port|int() -%}
nat-rule-port-{{port}} = ${slap-network-information:global-ipv6} : {{external_port}} nat-rule-port-{{port}} = ${slap-network-information:global-ipv6} : ${6tunnel-{{external_port}}:ipv6-port}
{% if slapparameter_dict.get('publish-nat-url', False) -%} {% if slapparameter_dict.get('publish-nat-url', False) -%}
nat-rule-url-{{port}} = [${slap-network-information:global-ipv6}]:{{external_port}} nat-rule-url-{{port}} = [${slap-network-information:global-ipv6}]:${6tunnel-{{external_port}}:ipv6-port}
{% endif -%} {% endif -%}
{% endfor -%} {% endfor -%}
{% endif -%} {% endif -%}
...@@ -555,6 +530,8 @@ enable-vhost = False ...@@ -555,6 +530,8 @@ enable-vhost = False
virtual-hard-drive-url = virtual-hard-drive-url =
virtual-hard-drive-md5sum = virtual-hard-drive-md5sum =
virtual-hard-drive-gzipped = False virtual-hard-drive-gzipped = False
# if virtual-hard-drive-url use https, then specify if https certificate should be checked or not
hard-drive-url-check-certificate = True
external-disk-number = 0 external-disk-number = 0
external-disk-size = 20 external-disk-size = 20
...@@ -571,3 +548,75 @@ data-to-vm = ...@@ -571,3 +548,75 @@ data-to-vm =
# Change keyboard layout language # Change keyboard layout language
keyboard-layout-language = keyboard-layout-language =
#############################
#
# Instanciate kvm (Buildout Section)
#
#############################
# Set Additionals parts
{% if slapparameter_dict.get('document-host', '') %}
{% do part_list.append('cluster-url-path') -%}
{% endif -%}
{% if enable_http == 'true' %}
{% do part_list.extend(['httpd', 'httpd-promise', 'publish-host-config']) -%}
{% if slapparameter_dict.get('data-to-vm', '') %}
{% do part_list.append('vm-data-content') -%}
{% endif -%}
{% if use_tap == 'true' and tap_network_dict.has_key('ipv4') and disable_ansible_promise == 'false' %}
{% do part_list.extend(['ansible-vm-promise', 'logrotate-vm-bootstrap']) -%}
{% endif -%}
{% if slapparameter_dict.get('authorized-key', '') %}
{% do part_list.append('get-authorized-key') -%}
{% endif -%}
{% endif -%}
[buildout]
parts =
certificate-authority
publish-connection-information
kvm-instance
kvm-contoller
kvm-vnc-promise
kvm-disk-image-corruption-promise
websockify-sighandler
novnc-promise
# kvm-monitor
cron
# cron-entry-monitor
frontend-promise
{% if monitor -%}
# monitor parts
cron-entry-monitor
cron-entry-rss
deploy-index
deploy-status-history-cgi
deploy-status-cgi
# deploy-logfile-cgi
# deploy-resource-consumption-monitoring-cgi
setup-static-files
public-symlink
cgi-httpd-wrapper
cgi-httpd-graceful-wrapper
monitor-promise
monitor-instance-log-access
monitor-access-log
monitor-access-public
# monitor-frontend-promise
{% endif -%}
# Complete parts with sections
{{ part_list | join('\n ') }}
extends =
# Add extends list
{{ extends_list | join('\n ') }}
# {{ template_httpd_cfg }}
eggs-directory = {{ eggs_directory }}
develop-eggs-directory = {{ develop_eggs_directory }}
offline = true
\ No newline at end of file
...@@ -92,11 +92,14 @@ context = ...@@ -92,11 +92,14 @@ context =
raw novnc_location ${noVNC:location} raw novnc_location ${noVNC:location}
raw netcat_bin ${netcat:location}/bin/netcat raw netcat_bin ${netcat:location}/bin/netcat
raw openssl_executable_location ${openssl:location}/bin/openssl raw openssl_executable_location ${openssl:location}/bin/openssl
raw python_executable ${buildout:executable}
raw qemu_executable_location ${kvm:location}/bin/qemu-system-x86_64 raw qemu_executable_location ${kvm:location}/bin/qemu-system-x86_64
raw qemu_img_executable_location ${kvm:location}/bin/qemu-img raw qemu_img_executable_location ${kvm:location}/bin/qemu-img
raw sixtunnel_executable_location ${6tunnel:location}/bin/6tunnel raw sixtunnel_executable_location ${6tunnel:location}/bin/6tunnel
raw template_httpd_cfg ${template-httpd:rendered} raw template_httpd_cfg ${template-httpd:rendered}
raw template_content ${template-content:location}/${template-content:filename} raw template_content ${template-content:location}/${template-content:filename}
raw template_kvm_controller_run ${template-kvm-controller:location}/${template-kvm-controller:filename}
raw template_kvm_run ${template-kvm-run:location}/${template-kvm-run:filename}
raw template_monitor ${monitor-template:output} raw template_monitor ${monitor-template:output}
raw websockify_executable_location ${buildout:directory}/bin/websockify raw websockify_executable_location ${buildout:directory}/bin/websockify
template-parts-destination = ${template-parts:destination} template-parts-destination = ${template-parts:destination}
......
#!{{ parameter_dict.get('python-path') }}
# BEWARE: This file is operated by slapgrid
# BEWARE: It will be overwritten automatically
# Echo client program
import socket
import time
# XXX: to be factored with slapos.toolbox qemu qmp wrapper.
socket_path = '{{ parameter_dict.get("socket-path") }}'
vnc_password = '{{ parameter_dict.get("vnc-passwd") }}'
# Connect to KVM qmp socket
so = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
connected = False
while not connected:
try:
so.connect(socket_path)
except socket.error:
time.sleep(1)
else:
connected = True
data = so.recv(1024)
# Enable qmp
so.send('{ "execute": "qmp_capabilities" }')
data = so.recv(1024)
# Set VNC password
so.send('{ "execute": "change", ' \
'"arguments": { "device": "vnc", "target": "password", ' \
' "arg": "' + vnc_password + '" } }')
data = so.recv(1024)
# Finish
so.close()
#!{{ parameter_dict.get('python-path') }}
# BEWARE: This file is operated by slapgrid
# BEWARE: It will be overwritten automatically
import hashlib
import os
import socket
import subprocess
import urllib
import gzip
import shutil
from random import shuffle
import glob
import re
import ssl
# XXX: give all of this through parameter, don't use this as template, but as module
qemu_img_path = '{{ parameter_dict.get("qemu-img-path") }}'
qemu_path = '{{ parameter_dict.get("qemu-path") }}'
disk_size = '{{ parameter_dict.get("disk-size") }}'
disk_type = '{{ parameter_dict.get("disk-type") }}'
socket_path = '{{ parameter_dict.get("socket-path") }}'
nbd_list = (('{{ parameter_dict.get("nbd-host") }}',
{{ parameter_dict.get("nbd-port") }}),
('{{ parameter_dict.get("nbd2-host") }}',
{{ parameter_dict.get("nbd2-port") }}))
default_cdrom_iso = '{{ parameter_dict.get("default-cdrom-iso") }}'
disk_path = '{{ parameter_dict.get("disk-path") }}'
virtual_hard_drive_url = '{{ parameter_dict.get("virtual-hard-drive-url") }}'.strip()
virtual_hard_drive_md5sum = '{{ parameter_dict.get("virtual-hard-drive-md5sum") }}'.strip()
virtual_hard_drive_gzipped = '{{ parameter_dict.get("virtual-hard-drive-gzipped") }}'.strip().lower()
nat_rules = '{{ parameter_dict.get("nat-rules") }}'.strip()
use_tap = '{{ parameter_dict.get("use-tap") }}'.lower()
use_nat = '{{ parameter_dict.get("use-nat") }}'.lower()
enable_vhost = '{{ parameter_dict.get("enable-vhost") }}'.lower()
tap_interface = '{{ parameter_dict.get("tap-interface") }}'
listen_ip = '{{ parameter_dict.get("ipv4") }}'
mac_address = '{{ parameter_dict.get("mac-address") }}'
tap_mac_address = '{{ parameter_dict.get("tap-mac-address") }}'
smp_count = '{{ parameter_dict.get("smp-count") }}'
smp_options = '{{ parameter_dict.get("smp-options") }}'.strip()
numa_list = '{{ parameter_dict.get("numa") }}'.split()
ram_size = '{{ parameter_dict.get("ram-size") }}'
pid_file_path = '{{ parameter_dict.get("pid-file-path") }}'
external_disk_number = {{ parameter_dict.get("external-disk-number") }}
external_disk_size = '{{ parameter_dict.get("external-disk-size") }}'
external_disk_format = '{{ parameter_dict.get("external-disk-format") }}'
disk_storage_dict = {}
disk_storage_list = """{{ parameter_dict.get("disk-storage-list") }}""".split('\n')
map_storage_list = []
etc_directory = '{{ parameter_dict.get("etc-directory") }}'.strip()
httpd_port = {{ parameter_dict.get("httpd-port") }}
netcat_bin = '{{ parameter_dict.get("netcat-binary") }}'.strip()
cluster_doc_host = '{{ parameter_dict.get("cluster-doc-host") }}'
cluster_doc_port = {{ parameter_dict.get("cluster-doc-port") }}
language = '{{ parameter_dict.get("language") }}'
language_list = ['ar', 'da', 'de', 'de-ch', 'en-gb', 'en-us', 'es', 'et', 'fi',
'fo', 'fr', 'fr-be', 'fr-ca', 'fr-ch', 'hr', 'hu', 'is', 'it', 'ja', 'lt',
'lv', 'mk', 'nl', 'nl-be', 'no', 'pl', 'pt', 'pt-br', 'ru', 'sl', 'sv',
'th', 'tr']
url_check_certificate = '{{ parameter_dict.get("hard-drive-url-check-certificate", "true") }}'.lower()
if hasattr(ssl, '_create_unverified_context') and url_check_certificate == 'false':
opener = urllib.FancyURLopener(context=ssl._create_unverified_context())
else:
opener = urllib.FancyURLopener({})
def md5Checksum(file_path):
with open(file_path, 'rb') as fh:
m = hashlib.md5()
while True:
data = fh.read(8192)
if not data:
break
m.update(data)
return m.hexdigest()
def getSocketStatus(host, port):
s = None
for res in socket.getaddrinfo(host, port,
socket.AF_UNSPEC, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error, msg:
s = None
continue
try:
s.connect(sa)
except socket.error, msg:
s.close()
s = None
continue
break
return s
def getMapStorageList(disk_storage_dict, external_disk_number):
map_disk_file = os.path.join(etc_directory, '.data-disk-ids')
last_disk_num_f = os.path.join(etc_directory, '.data-disk-amount')
id_list = []
last_amount = 0
map_f_exist = os.path.exists(map_disk_file)
if os.path.exists(last_disk_num_f):
with open(last_disk_num_f, 'r') as lf:
last_amount = int(lf.readline())
if map_f_exist:
with open(map_disk_file, 'r') as mf:
# ID are writen in one line: data1 data3 data2 ...
content = mf.readline()
for id in content.split(' '):
if disk_storage_dict.has_key(id):
id_list.append(id)
else:
# Mean that this disk path has been removed (disk unmounted)
last_amount -= 1
for key in disk_storage_dict:
if not key in id_list:
id_list.append(key)
if id_list:
if not map_f_exist:
# shuffle the list to not write disk in data1, data2, ... everytime
shuffle(id_list)
if external_disk_number < last_amount:
# Drop created disk is not allowed
external_disk_number = last_amount
with open(map_disk_file, 'w') as mf:
mf.write(' '.join(id_list))
with open(last_disk_num_f, 'w') as lf:
lf.write('%s' % external_disk_number)
return id_list, external_disk_number
# Download existing hard drive if needed at first boot
if not os.path.exists(disk_path) and virtual_hard_drive_url != '':
print('Downloading virtual hard drive...')
try:
downloaded_disk = disk_path
if virtual_hard_drive_gzipped == 'true':
downloaded_disk = '%s.gz' % disk_path
opener.retrieve(virtual_hard_drive_url, downloaded_disk)
except:
if os.path.exists(downloaded_disk):
os.remove(downloaded_disk)
raise
md5sum = virtual_hard_drive_md5sum.strip()
if md5sum:
print('Checking MD5 checksum...')
local_md5sum = md5Checksum(downloaded_disk)
if local_md5sum != md5sum:
os.remove(downloaded_disk)
raise Exception('MD5 mismatch. MD5 of local file is %s, Specified MD5 is %s.' % (
local_md5sum, md5sum))
print('MD5sum check passed.')
else:
print('Warning: not checksum specified.')
if downloaded_disk.endswith('.gz'):
try:
with open(disk_path, 'w') as disk:
with gzip.open(downloaded_disk, 'rb') as disk_gz:
shutil.copyfileobj(disk_gz, disk)
except Exception:
if os.path.exists(disk_path):
os.remove(disk_path)
raise
os.remove(downloaded_disk)
# Create disk if doesn't exist
# XXX: move to Buildout profile
if not os.path.exists(disk_path):
print('Creating virtual hard drive...')
subprocess.check_call([qemu_img_path, 'create' ,'-f', 'qcow2',
disk_path, '%sG' % disk_size])
print('Done.')
# Check and create external disk
additional_disk_list = []
for storage in disk_storage_list:
if storage:
key, val = storage.split(' ')
disk_storage_dict[key.strip()] = val.strip()
if not external_disk_format in ['qcow2', 'raw', 'vdi', 'vmdk', 'cloop', 'qed']:
external_disk_format = 'qcow2'
map_storage_list, external_disk_number = getMapStorageList(disk_storage_dict,
int(external_disk_number))
assert len(map_storage_list) == len(disk_storage_dict)
if disk_storage_dict:
if external_disk_number > 0:
index = 0
while (index < len(disk_storage_dict)) and (index < external_disk_number):
path = disk_storage_dict[map_storage_list[index]]
if os.path.exists(path):
disk_filepath = os.path.join(path,
'kvm_virtual_disk.%s' % external_disk_format)
disk_list = glob.glob('%s.*' % os.path.join(path, 'kvm_virtual_disk'))
if disk_list == []:
print('Creating one additional virtual hard drive...')
process = subprocess.check_call([qemu_img_path, 'create' ,'-f', '%s' % external_disk_format,
disk_filepath, '%sG' % external_disk_size])
else:
# Cannot change or recreate if disk is exists
disk_filepath = disk_list[0]
additional_disk_list.append(disk_filepath)
else:
print('Data folder %s was not used to create external disk %r' % (index +1))
index += 1
# Generate network parameters
# XXX: use_tap should be a boolean
tap_network_parameter = []
nat_network_parameter = []
numa_parameter = []
number = -1
if use_nat == 'true':
number += 1
rules = 'user,id=lan%s,' % number + ','.join('hostfwd=tcp:%s:%s-:%s' % (listen_ip,
int(port) + 10000, port) for port in nat_rules.split())
if httpd_port > 0:
rules += ',guestfwd=tcp:10.0.2.100:80-cmd:%s %s %s' % (netcat_bin,
listen_ip, httpd_port)
if cluster_doc_host and cluster_doc_port > 0:
rules += ',guestfwd=tcp:10.0.2.101:443-cmd:%s %s %s' % (netcat_bin,
cluster_doc_host, cluster_doc_port)
nat_network_parameter = ['-netdev', rules,
'-device', 'virtio-net-pci,netdev=lan%s,mac=%s' % (number, mac_address)]
if use_tap == 'true':
number += 1
vhost = ''
if enable_vhost == 'true':
vhost = ',vhost=on'
tap_network_parameter = ['-netdev',
'tap,id=lan%s,ifname=%s,script=no,downscript=no%s' % (number,
tap_interface, vhost),
'-device', 'virtio-net-pci,netdev=lan%s,mac=%s' % (number, tap_mac_address)]
smp = smp_count
if smp_options:
for option in smp_options.split(','):
key, val = option.split('=')
if key in ('cores', 'threads', 'sockets', 'maxcpus') and val.isdigit():
smp += ',%s=%s' % (key, val)
kvm_argument_list = [qemu_path,
'-enable-kvm', '-smp', smp,
'-m', ram_size, '-vga', 'std',
'-drive', 'file=%s,if=%s' % (disk_path, disk_type),
'-vnc', '%s:1,ipv4,password' % listen_ip,
'-boot', 'order=cd,menu=on',
'-qmp', 'unix:%s,server' % socket_path,
'-pidfile', pid_file_path,
]
rgx = re.compile('^[\w*\,][\=\d+\-\,\w]*$')
for numa in numa_list:
if rgx.match(numa):
numa_parameter.extend(['-numa', numa])
kvm_argument_list += numa_parameter
if tap_network_parameter == [] and nat_network_parameter == []:
print 'Warning : No network interface defined.'
else:
kvm_argument_list += nat_network_parameter + tap_network_parameter
if language in language_list:
kvm_argument_list.extend(['-k', language])
for disk in additional_disk_list:
kvm_argument_list.extend([
'-drive', 'file=%s,if=%s' % (disk, disk_type)])
# Try to connect to NBD server (and second nbd if defined).
# If not available, don't even specify it in qemu command line parameters.
# Reason: if qemu starts with unavailable NBD drive, it will just crash.
for nbd_ip, nbd_port in nbd_list:
if nbd_ip and nbd_port:
s = getSocketStatus(nbd_ip, nbd_port)
if s is None:
# NBD is not available : launch kvm without it
print 'Warning : Nbd is not available.'
else:
# NBD is available
kvm_argument_list.extend([
'-drive',
'file=nbd:[%s]:%s,media=cdrom' % (nbd_ip, nbd_port)])
# If no NBD is specified/available: use internal disk image
else:
kvm_argument_list.extend([
'-drive', 'file=%s,media=cdrom' % default_cdrom_iso
])
print 'Starting KVM: \n %s' % ' '.join(kvm_argument_list)
os.execv(qemu_path, kvm_argument_list)
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 to comment