Commit 39b619db authored by Cédric de Saint Martin's avatar Cédric de Saint Martin

Merge branch 'kvm'

parents f1c3a7ff 5c9da475
# -*- coding: utf-8 -*-
import logging
import time
import slapos
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
class Renamer(object):
def __init__(self, server_url, key_file, cert_file, computer_guid,
partition_id, software_release, namebase):
self.server_url = server_url
self.key_file = key_file
self.cert_file = cert_file
self.computer_guid = computer_guid
self.partition_id = partition_id
self.software_release = software_release
self.namebase = namebase
def _failover(self):
"""\
This method does
- retrieve the broken computer partition
- change its reference to 'broken-...' and its software type to 'frozen'
- retrieve the winner computer partition (attached to this process)
- change its reference to replace the broken one.
later, slapgrid will change its software_type as well.
Then, after running slapgrid-cp a few times, the winner takes over and
a new cp is created to replace it as an importer.
"""
slap = slapos.slap.slap()
slap.initializeConnection(self.server_url, self.key_file, self.cert_file)
# partition that will take over.
cp_winner = slap.registerComputerPartition(computer_guid=self.computer_guid,
partition_id=self.partition_id)
# XXX although we can already rename cp_winner, to change its software type we need to
# get hold of the root cp as well
cp_exporter_ref = self.namebase + '0' # this is ok. the boss is always number zero.
# partition to be deactivated
cp_broken = cp_winner.request(software_release=self.software_release,
software_type='frozen',
state='stopped',
partition_reference=cp_exporter_ref)
broken_new_ref = 'broken-{}'.format(time.strftime("%d-%b_%H:%M:%S", time.gmtime()))
log.debug("Renaming {}: {}".format(cp_broken.getId(), broken_new_ref))
cp_broken.rename(new_name=broken_new_ref)
log.debug("Renaming {}: {}".format(cp_winner.getId(), cp_exporter_ref))
# update name (and later, software type) for the partition that will take over
cp_winner.rename(new_name=cp_exporter_ref)
cp_winner.bang(message='partitions have been renamed!')
def failover(self):
try:
self._failover()
log.info('Renaming done')
except slapos.slap.ServerError:
log.info('Internal server error')
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
import time
import slapos.recipe.addresiliency.renamer import slapos
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
def takeover(server_url, key_file, cert_file, computer_guid,
partition_id, software_release, namebase,
winner_instance_suffix = None):
"""
This function does
- retrieve the broken computer partition
- change its reference to 'broken-...' and its software type to 'frozen'
- retrieve the winner computer partition (attached to this process)
- change its reference to replace the broken one.
later, slapgrid will change its software_type as well.
Then, after running slapgrid-cp a few times, the winner takes over and
a new cp is created to replace it as an importer.
"""
slap = slapos.slap.slap()
slap.initializeConnection(server_url, key_file, cert_file)
current_partition = slap.registerComputerPartition(computer_guid=computer_guid,
partition_id=partition_id)
# partition that will take over.
if winner_instance_suffix:
winner_instance_name = namebase + winner_instance_suffix
# XXX: we hardcode a lot of values here, because request is a settergetter, all at once.
cp_winner = current_partition.request(software_release=software_release,
software_type='%s-import' % namebase,
partition_reference=winner_instance_name)
else:
# This script is run in the winning partition: use this one as winner
cp_winner = current_partition
# XXX although we can already rename cp_winner, to change its software type we need to
# get hold of the root cp as well
cp_exporter_ref = namebase + '0' # this is ok. the boss is always number zero.
# partition to be deactivated
cp_broken = cp_winner.request(software_release=software_release,
software_type='frozen',
state='stopped',
partition_reference=cp_exporter_ref)
broken_new_ref = 'broken-{}'.format(time.strftime("%d-%b_%H:%M:%S", time.gmtime()))
log.debug("Renaming {}: {}".format(cp_broken.getId(), broken_new_ref))
cp_broken.rename(new_name=broken_new_ref)
log.debug("Renaming {}: {}".format(cp_winner.getId(), cp_exporter_ref))
# update name (and later, software type) for the partition that will take over
cp_winner.rename(new_name=cp_exporter_ref)
cp_winner.bang(message='partitions have been renamed!')
# Note: Root instance will reconfigure itself the winning instance (software_type
# and parameters.)
def run(args): def run(args):
renamer = slapos.recipe.addresiliency.renamer.Renamer(server_url = args.pop('server_url'), slapos.recipe.addresiliency.takeover.takeover(server_url = args.pop('server_url'),
key_file = args.pop('key_file'), key_file = args.pop('key_file'),
cert_file = args.pop('cert_file'), cert_file = args.pop('cert_file'),
computer_guid = args.pop('computer_id'), computer_guid = args.pop('computer_id'),
...@@ -11,6 +73,3 @@ def run(args): ...@@ -11,6 +73,3 @@ def run(args):
software_release = args.pop('software'), software_release = args.pop('software'),
namebase = args.pop('namebase')) namebase = args.pop('namebase'))
renamer.failover()
...@@ -48,16 +48,16 @@ class Recipe(GenericBaseRecipe): ...@@ -48,16 +48,16 @@ class Recipe(GenericBaseRecipe):
if not self.isTrueValue(self.options.get('use-tap')): if not self.isTrueValue(self.options.get('use-tap')):
# XXX This could be done using Jinja. # XXX This could be done using Jinja.
for port in self.options['nat-rules'].split(): for port in self.options['nat-rules'].split():
ipv6_port = int(port) + 10000 tunnel_port = int(port) + 10000
tunnel_path = self.createExecutable( tunnel_path = self.createExecutable(
'%s-%sto%s' % (self.options['6tunnel-wrapper-path'], port, ipv6_port), '%s-%s' % (self.options['6tunnel-wrapper-path'], tunnel_port),
self.substituteTemplate( self.substituteTemplate(
self.getTemplateFilename('6to4.in'), self.getTemplateFilename('6to4.in'),
{ {
'ipv6': self.options['ipv6'], 'ipv6': self.options['ipv6'],
'ipv6_port': ipv6_port, 'ipv6_port': tunnel_port,
'ipv4': self.options['ipv4'], 'ipv4': self.options['ipv4'],
'ipv4_port': port, 'ipv4_port': tunnel_port,
'shell_path': self.options['shell-path'], 'shell_path': self.options['shell-path'],
'6tunnel_path': self.options['6tunnel-path'], '6tunnel_path': self.options['6tunnel-path'],
}, },
......
...@@ -18,7 +18,7 @@ nbd_list = (('%(nbd-host)s', %(nbd-port)s), ('%(nbd2-host)s', %(nbd2-port)s)) ...@@ -18,7 +18,7 @@ nbd_list = (('%(nbd-host)s', %(nbd-port)s), ('%(nbd2-host)s', %(nbd2-port)s))
default_disk_image = '%(default-disk-image)s' default_disk_image = '%(default-disk-image)s'
disk_path = '%(disk-path)s' disk_path = '%(disk-path)s'
virtual_hard_drive_url = '%(virtual-hard-drive-url)s'.strip() virtual_hard_drive_url = '%(virtual-hard-drive-url)s'.strip()
virtual_hard_drive_md5_url = '%(virtual-hard-drive-md5-url)s'.strip() virtual_hard_drive_md5sum = '%(virtual-hard-drive-md5sum)s'.strip()
nat_rules = '%(nat-rules)s'.strip() nat_rules = '%(nat-rules)s'.strip()
use_tap = '%(use-tap)s' use_tap = '%(use-tap)s'
tap_interface = '%(tap-interface)s' tap_interface = '%(tap-interface)s'
...@@ -59,18 +59,27 @@ def getSocketStatus(host, port): ...@@ -59,18 +59,27 @@ def getSocketStatus(host, port):
# Download existing hard drive if needed at first boot # Download existing hard drive if needed at first boot
if not os.path.exists(disk_path) and virtual_hard_drive_url != '': if not os.path.exists(disk_path) and virtual_hard_drive_url != '':
print('Downloading virtual hard drive...')
urllib.urlretrieve(virtual_hard_drive_url, disk_path) urllib.urlretrieve(virtual_hard_drive_url, disk_path)
md5sum = virtual_hard_drive_md5sum.strip()
if md5sum:
print('Checking MD5 checksum...')
local_md5sum = md5Checksum(disk_path) local_md5sum = md5Checksum(disk_path)
md5sum = urllib.urlopen(virtual_hard_drive_md5_url).read().strip()
if local_md5sum != md5sum: if local_md5sum != md5sum:
os.remove(disk_path) os.remove(disk_path)
raise Exception('MD5 mismatch.') 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.')
# Create disk if doesn't exist # Create disk if doesn't exist
# XXX: move to Buildout profile # XXX: move to Buildout profile
if not os.path.exists(disk_path): if not os.path.exists(disk_path):
print('Creating virtual hard drive...')
subprocess.Popen([qemu_img_path, 'create' ,'-f', 'qcow2', subprocess.Popen([qemu_img_path, 'create' ,'-f', 'qcow2',
disk_path, '%%sG' %% disk_size]) disk_path, '%%sG' %% disk_size])
print('Done.')
# Generate network parameters # Generate network parameters
# XXX: use_tap should be a boolean # XXX: use_tap should be a boolean
......
...@@ -42,6 +42,7 @@ eggs = ...@@ -42,6 +42,7 @@ eggs =
websockify websockify
slapos.cookbook slapos.cookbook
slapos.toolbox slapos.toolbox
erp5.util
[http-proxy] [http-proxy]
# https://github.com/nodejitsu/node-http-proxy # https://github.com/nodejitsu/node-http-proxy
...@@ -78,7 +79,7 @@ command = ...@@ -78,7 +79,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 = 0e84223169661462f439c164d62c2a6a #md5sum = 461187060d12a6bea4d5b9699687c0e9
output = ${buildout:directory}/template.cfg output = ${buildout:directory}/template.cfg
mode = 0644 mode = 0644
...@@ -91,9 +92,17 @@ mode = 0644 ...@@ -91,9 +92,17 @@ mode = 0644
[template-kvm-resilient] [template-kvm-resilient]
recipe = hexagonit.recipe.download recipe = hexagonit.recipe.download
url = ${:_profile_base_location_}/instance-kvm-resilient.cfg.jinja url = ${:_profile_base_location_}/instance-kvm-resilient.cfg.jinja2
mode = 644 mode = 644
md5sum = 3ee64c654aae503b93b39e9ccd6d3643 md5sum = 6753004b582c0470bd028253ce1964ad
download-only = true
[template-kvm-resilient-test]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance-kvm-resilient-test.cfg.in
#md5sum = 9dead7572072307b6c38cdf47052225b
output = ${buildout:directory}/template-kvm-resilient-test.cfg
mode = 0644
[template-kvm-import] [template-kvm-import]
recipe = slapos.recipe.template recipe = slapos.recipe.template
...@@ -138,3 +147,4 @@ url = ${:_profile_base_location_}/instance-frontend.cfg.in ...@@ -138,3 +147,4 @@ url = ${:_profile_base_location_}/instance-frontend.cfg.in
md5sum = cdb690495e9eb007d2b7d2f8e12f5c59 md5sum = cdb690495e9eb007d2b7d2f8e12f5c59
output = ${buildout:directory}/template-frontend.cfg output = ${buildout:directory}/template-frontend.cfg
mode = 0644 mode = 0644
...@@ -6,11 +6,13 @@ extends = ...@@ -6,11 +6,13 @@ extends =
parts += parts +=
slapos.cookbook-repository slapos.cookbook-repository
slapos.toolbox-repository slapos.toolbox-repository
erp5.util-repository
check-recipe check-recipe
develop = develop =
${:parts-directory}/slapos.cookbook-repository ${:parts-directory}/slapos.cookbook-repository
${:parts-directory}/slapos.toolbox-repository ${:parts-directory}/slapos.toolbox-repository
${:parts-directory}/erp5.util-repository
[slapos.cookbook-repository] [slapos.cookbook-repository]
recipe = slapos.recipe.build:gitclone recipe = slapos.recipe.build:gitclone
...@@ -24,13 +26,18 @@ repository = http://git.erp5.org/repos/slapos.toolbox.git ...@@ -24,13 +26,18 @@ repository = http://git.erp5.org/repos/slapos.toolbox.git
branch = master branch = master
git-executable = ${git:location}/bin/git git-executable = ${git:location}/bin/git
[erp5.util-repository]
recipe = slapos.recipe.build:gitclone
repository = http://git.erp5.org/repos/erp5.git
branch = scalability-master2
git-executable = ${git:location}/bin/git
[check-recipe] [check-recipe]
recipe = plone.recipe.command recipe = plone.recipe.command
stop-on-error = true stop-on-error = true
update-command = ${:command} update-command = ${:command}
command = command =
grep parts ${buildout:develop-eggs-directory}/slapos.cookbook.egg-link && grep parts ${buildout:develop-eggs-directory}/slapos.cookbook.egg-link &&
grep parts ${buildout:develop-eggs-directory}/slapos.toolbox.egg-link grep parts ${buildout:develop-eggs-directory}/slapos.toolbox.egg-link &&
grep parts ${buildout:develop-eggs-directory}/erp5.util.egg-link
[versions]
websockify = 0.3.0
\ No newline at end of file
...@@ -73,6 +73,12 @@ ...@@ -73,6 +73,12 @@
"format": "uri", "format": "uri",
"type": "string", "type": "string",
}, },
"virtual-hard-drive-md5sum": {
"title": "Checksum of virtual hard drive",
"description": "MD5 checksum of virtual hard drive, used if virtual-hard-drive-url is specified.",
"type": "string",
},
virtual-hard-drive-md5sum
"use-tap": { "use-tap": {
"title": "Use QEMU TAP network interface", "title": "Use QEMU TAP network interface",
......
[buildout]
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
offline = true
parts =
deploy-resiliency-test
request-resilient-kvm
[directory]
recipe = slapos.cookbook:mkdirectory
etc = $${buildout:directory}/etc/
var = $${buildout:directory}/var/
srv = $${buildout:directory}/srv/
bin = $${buildout:directory}/bin/
tmp = $${buildout:directory}/tmp/
services = $${:etc}/service/
scripts = $${:etc}/run/
[deploy-resiliency-test]
recipe = slapos.cookbook:wrapper
#log-path = {directory:log}
wrapper-path = $${directory:bin}/runKVMResiliencyTestSuite
#output = $${binary-wrap-launcher:binary-path}
#site-id = "{site_id}"
# Suppose that there is a user zope family in the configuration
#erp5-url = $${erp5-cluster:connection-family-user}{:site-id}
#parameters-extra = $*
command-line = ${buildout:bin-directory}/runKVMResiliencyTestSuite --server-url=$${slap-connection:server-url} --key-file=$${slap-connection:key-file} --cert-file=$${slap-connection:cert-file} --computer-id=$${slap-connection:computer-id} --partition-id=$${slap-connection:partition-id} --software=$${slap-connection:software-release-url} --namebase=kvm --kvm-rootinstance-name='$${request-resilient-kvm:name}'
#return = url
[request-resilient-kvm]
<= slap-connection
recipe = slapos.cookbook:request
software-url = $${slap-connection:software-release-url}
software-type = kvm-resilient
name = Resilient KVM (Root Instance)
config = virtual-hard-drive-url virtual-hard-drive-md5sum resiliency-backup-periodicity
config-virtual-hard-drive-url = $${slap-parameter:virtual-hard-drive-url}
config-virtual-hard-drive-md5sum = $${slap-parameter:virtual-hard-drive-md5sum}
config-resiliency-backup-periodicity = */5
# We don't use url parameter, but we want it to be there to make sure root instance is ready.
return = url
# XXX What to do?
#sla = instance_guid
#sla-instance_guid = $${slap-parameter:frontend-instance-guid}
[slap-parameter]
virtual-hard-drive-url = https://softinst43236.host.vifib.net/data/public/8e2138.php?dl=true
virtual-hard-drive-md5sum = de0f10c7c6538e9928879332afd9be7a
# XXX: what to do about pbs-kvm1-computer-guid and kvm1-computer-guid ?
...@@ -10,10 +10,10 @@ offline = true ...@@ -10,10 +10,10 @@ 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", "2") }} {{ parts.replicate("kvm", "3") }}
publish-connection-informations publish-connection-informations
{{ replicated.replicate("kvm", "2", "kvm-export", "kvm-import") }} {{ replicated.replicate("kvm", "3", "kvm-export", "kvm-import") }}
# Bubble down the parameters of the requested instance to the user # Bubble down the parameters of the requested instance to the user
[request-kvm] [request-kvm]
...@@ -22,10 +22,10 @@ return = ...@@ -22,10 +22,10 @@ return =
# Resilient related parameters # Resilient related parameters
url ssh-public-key ssh-url notification-id ip url ssh-public-key ssh-url notification-id ip
# KVM related parameters # KVM related parameters
backend-url url backend-url url ipv6
[publish-connection-informations] [publish-connection-informations]
recipe = slapos.cookbook:publish recipe = slapos.cookbook:publish
backend-url = ${request-kvm:connection-backend-url} backend-url = ${request-kvm:connection-backend-url}
url = ${request-kvm:connection-url} url = ${request-kvm:connection-url}
ipv6 = ${request-kvm:connection-ipv6}
...@@ -88,7 +88,7 @@ nat-rules = $${slap-parameter:nat-rules} ...@@ -88,7 +88,7 @@ nat-rules = $${slap-parameter:nat-rules}
6tunnel-wrapper-path = $${directory:services}/6tunnel 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-md5-url = $${slap-parameter:virtual-hard-drive-md5-url} virtual-hard-drive-md5sum = $${slap-parameter:virtual-hard-drive-md5sum}
shell-path = ${dash:location}/bin/dash shell-path = ${dash:location}/bin/dash
qemu-path = ${kvm:location}/bin/qemu-system-x86_64 qemu-path = ${kvm:location}/bin/qemu-system-x86_64
...@@ -214,7 +214,7 @@ sla-instance_guid = $${slap-parameter:frontend-instance-guid} ...@@ -214,7 +214,7 @@ sla-instance_guid = $${slap-parameter:frontend-instance-guid}
recipe = slapos.cookbook:publish recipe = slapos.cookbook:publish
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-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-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-instance:vnc-passwd}
ipv6 = $${slap-network-information:global-ipv6}
[frontend-promise] [frontend-promise]
recipe = slapos.cookbook:check_url_available recipe = slapos.cookbook:check_url_available
...@@ -243,4 +243,4 @@ nat-rules = 22 80 443 ...@@ -243,4 +243,4 @@ nat-rules = 22 80 443
use-tap = False use-tap = False
virtual-hard-drive-url = virtual-hard-drive-url =
virtual-hard-drive-md5-url = virtual-hard-drive-md5sum =
...@@ -16,6 +16,9 @@ kvm-resilient = $${dynamic-template-kvm-resilient:rendered} ...@@ -16,6 +16,9 @@ kvm-resilient = $${dynamic-template-kvm-resilient:rendered}
kvm-import = ${template-kvm-import:output} kvm-import = ${template-kvm-import:output}
kvm-export = ${template-kvm-export:output} kvm-export = ${template-kvm-export:output}
# Used for the test of resiliency. The system wants a "test" software_type.
test = ${template-kvm-resilient-test:output}
frozen = ${instance-frozen:output} frozen = ${instance-frozen:output}
pull-backup = ${template-pull-backup:output} pull-backup = ${template-pull-backup:output}
...@@ -29,8 +32,7 @@ cert = $${slap-connection:cert-file} ...@@ -29,8 +32,7 @@ cert = $${slap-connection:cert-file}
[dynamic-template-kvm-resilient] [dynamic-template-kvm-resilient]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
template = ${:_profile_base_location_}/instance-kvm-resilient.cfg.jinja2 template = ${template-kvm-resilient:location}/instance-kvm-resilient.cfg.jinja2
md5sum = 1b7a2fcc884649b4d08f238e828899c1
rendered = $${buildout:directory}/template-kvm-resilient.cfg rendered = $${buildout:directory}/template-kvm-resilient.cfg
context = key buildout buildout:bin-directory context = key buildout buildout:bin-directory
key develop_eggs_directory buildout:develop-eggs-directory key develop_eggs_directory buildout:develop-eggs-directory
......
...@@ -48,7 +48,7 @@ mode = 0644 ...@@ -48,7 +48,7 @@ mode = 0644
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${:_profile_base_location_}/pbsready-export.cfg.in url = ${:_profile_base_location_}/pbsready-export.cfg.in
output = ${buildout:directory}/pbsready-export.cfg output = ${buildout:directory}/pbsready-export.cfg
md5sum = a0e22a5de727544c5767d6bee059a77a md5sum = ef3861861746d3574f39f1aa3200d74e
mode = 0644 mode = 0644
[template-pull-backup] [template-pull-backup]
......
...@@ -39,5 +39,10 @@ notify = $${slap-parameter:notify} ...@@ -39,5 +39,10 @@ notify = $${slap-parameter:notify}
<= cron <= cron
recipe = slapos.cookbook:cron.d recipe = slapos.cookbook:cron.d
name = backup name = backup
frequency = 0 * * * * frequency = $${slap-parameter:resiliency-backup-periodicity} * * * *
command = $${notifier-exporter:wrapper} command = $${notifier-exporter:wrapper}
[slap-parameter]
# in minutes, modulo 60, in cron.d format (i.e */15 is accepted).
resiliency-backup-periodicity = 0
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