Commit 79e8f0a2 authored by Antoine Catton's avatar Antoine Catton Committed by Łukasz Nowak

Merge Antoine Catton work on promises.

Squashed commit of the following:

commit 29caaa556d1d641f6b2241db84d2d8b588689a82
Merge: 32d5fef 13d6e1d3
Author: Antoine Catton <acatton@tiolive.com>
Date:   Mon Sep 19 11:49:27 2011 +0200

    Merge branch 'master' into promises

    Conflicts:
    	slapos/recipe/kvm/__init__.py

commit 32d5fef49f22e3a47ca0b0728e502d177979b214
Author: Antoine Catton <acatton@tiolive.com>
Date:   Wed Aug 24 16:45:31 2011 +0200

    Minor: Use pkg_resources the right way

    http://peak.telecommunity.com/DevCenter/PkgResources

commit e2fae493ab6a96fa10cbe6531ee1a1ba9c16bb4c
Author: Antoine Catton <acatton@tiolive.com>
Date:   Wed Aug 24 16:00:02 2011 +0200

    Minor: some typo fix to get the recipe working

commit 3764ab93da5031f9b5ea59072090d2b961a791cf
Author: Antoine Catton <acatton@tiolive.com>
Date:   Wed Aug 24 15:53:17 2011 +0200

    Minor: typo fix

commit 259026f718e0e37c5c151fb40dbf85866f6ab87f
Author: Antoine Catton <acatton@tiolive.com>
Date:   Wed Aug 24 15:33:04 2011 +0200

    Cleanup: use createPromiseWrapper instead of easy_install.script

    - Install the socket_connection_attempt script into bin/ directory
    - Create promises running this script specifying the right command line
      arguments

commit a870a4d77a3b72d79354e557ee0b8571da9a7459
Author: Antoine Catton <acatton@tiolive.com>
Date:   Wed Aug 24 13:44:50 2011 +0200

    Minor: remove appending promise path to path_list

commit 260df1be27ac382061809aa8851130cd26f74be0
Author: Łukasz Nowak <luke@nexedi.com>
Date:   Thu Jul 28 14:40:29 2011 +0200

    Do not expose python's shabang.

commit 7db31e9b6759183a54390480a77032009bf2e9ae
Author: Antoine Catton <acatton@tiolive.com>
Date:   Thu Jul 28 14:13:03 2011 +0200

    Replaced the usage python script as template by the easy_install
    capability usage.

commit 3f62cda3a2a9e64cc92cd4c01585b422bb30ff0e
Author: Antoine Catton <acatton@tiolive.com>
Date:   Tue Jul 26 15:34:13 2011 +0200

    Adding promises to kvm software release.

    Not tested yet

    Minor: removed magic number 5900

commit 2e36426bd11e4833ec3be6a8e2b664e343760417
Author: Antoine Catton <acatton@tiolive.com>
Date:   Tue Jul 26 13:38:14 2011 +0200

    Trailing spaces on KVM recipe

commit 02ed180bd0002cbfe387875ec917e4d6e214e40e
Author: Antoine Catton <acatton@tiolive.com>
Date:   Mon Jul 25 16:57:43 2011 +0200

    Method createPromise in slapos.librecipe

    This method create a promise script which should return 0 whether the
    promise was kept.
parent 13d6e1d3
...@@ -37,6 +37,9 @@ import hashlib ...@@ -37,6 +37,9 @@ import hashlib
class Recipe(BaseSlapRecipe): class Recipe(BaseSlapRecipe):
# To avoid magic numbers
VNC_BASE_PORT = 5900
def _install(self): def _install(self):
""" """
Set the connection dictionnary for the computer partition and create a list Set the connection dictionnary for the computer partition and create a list
...@@ -49,42 +52,58 @@ class Recipe(BaseSlapRecipe): ...@@ -49,42 +52,58 @@ class Recipe(BaseSlapRecipe):
self.path_list = [] self.path_list = []
self.requirements, self.ws = self.egg.working_set() self.requirements, self.ws = self.egg.working_set()
self.cron_d = self.installCrond() self.cron_d = self.installCrond()
self.ca_conf = self.installCertificateAuthority() self.ca_conf = self.installCertificateAuthority()
self.key_path, self.certificate_path = self.requestCertificate('noVNC') self.key_path, self.certificate_path = self.requestCertificate('noVNC')
# Install the socket_connection_attempt script
catcher = zc.buildout.easy_install.scripts(
[('check_port_listening', __name__ + 'socket_connection_attempt', 'connection_attempt')],
self.ws,
sys.executable,
self.bin_directory,
)
# Save the check_port_listening script path
check_port_listening_script = catcher[0]
# Get the port_listening_promise template path, and save it
self.port_listening_promise_path = pkg_resources.resource_filename(
__name__, 'template/port_listening_promise.in')
self.port_listening_promise_conf = dict(
check_port_listening_script=check_port_listening_script,
)
kvm_conf = self.installKvm(vnc_ip = self.getLocalIPv4Address()) kvm_conf = self.installKvm(vnc_ip = self.getLocalIPv4Address())
vnc_port = 5900 + kvm_conf['vnc_display'] vnc_port = Recipe.VNC_BASE_PORT + kvm_conf['vnc_display']
noVNC_conf = self.installNoVnc(source_ip = self.getGlobalIPv6Address(), noVNC_conf = self.installNoVnc(source_ip = self.getGlobalIPv6Address(),
source_port = 6080, source_port = 6080,
target_ip = kvm_conf['vnc_ip'], target_ip = kvm_conf['vnc_ip'],
target_port = vnc_port) target_port = vnc_port)
self.linkBinary() self.linkBinary()
self.computer_partition.setConnectionDict(dict( self.computer_partition.setConnectionDict(dict(
url = "https://[%s]:%s/vnc.html?host=[%s]&port=%s&encrypt=1" % (noVNC_conf['source_ip'], url = "https://[%s]:%s/vnc.html?host=[%s]&port=%s&encrypt=1" % (noVNC_conf['source_ip'],
noVNC_conf['source_port'], noVNC_conf['source_port'],
noVNC_conf['source_ip'], noVNC_conf['source_ip'],
noVNC_conf['source_port'] noVNC_conf['source_port']
), ),
password = kvm_conf['vnc_passwd'])) password = kvm_conf['vnc_passwd']))
return self.path_list return self.path_list
def installKvm(self, vnc_ip): def installKvm(self, vnc_ip):
""" """
Create kvm configuration dictionnary and instanciate a wrapper for kvm and Create kvm configuration dictionnary and instanciate a wrapper for kvm and
kvm controller kvm controller
Parameters : IP the vnc server is listening on Parameters : IP the vnc server is listening on
Returns : Dictionnary kvm_conf Returns : Dictionnary kvm_conf
""" """
kvm_conf = dict(vnc_ip = vnc_ip) kvm_conf = dict(vnc_ip = vnc_ip)
connection_found = False connection_found = False
for tap_interface, dummy in self.parameter_dict['ip_list']: for tap_interface, dummy in self.parameter_dict['ip_list']:
# Get an ip associated to a tap interface # Get an ip associated to a tap interface
...@@ -94,13 +113,13 @@ class Recipe(BaseSlapRecipe): ...@@ -94,13 +113,13 @@ class Recipe(BaseSlapRecipe):
raise NotImplementedError("Do not support ip without tap interface") raise NotImplementedError("Do not support ip without tap interface")
kvm_conf['tap_interface'] = tap_interface kvm_conf['tap_interface'] = tap_interface
# Disk path # Disk path
kvm_conf['disk_path'] = os.path.join(self.data_root_directory, kvm_conf['disk_path'] = os.path.join(self.data_root_directory,
'virtual.qcow2') 'virtual.qcow2')
kvm_conf['socket_path'] = os.path.join(self.var_directory, 'qmp_socket') kvm_conf['socket_path'] = os.path.join(self.var_directory, 'qmp_socket')
# XXX Weak password # XXX Weak password
##XXX -Vivien: add an option to generate one password for all instances ##XXX -Vivien: add an option to generate one password for all instances
# and/or to input it yourself # and/or to input it yourself
kvm_conf['vnc_passwd'] = binascii.hexlify(os.urandom(4)) kvm_conf['vnc_passwd'] = binascii.hexlify(os.urandom(4))
...@@ -119,7 +138,7 @@ class Recipe(BaseSlapRecipe): ...@@ -119,7 +138,7 @@ class Recipe(BaseSlapRecipe):
int(self.options['disk_size']))], shell=True) int(self.options['disk_size']))], shell=True)
if retcode != 0: if retcode != 0:
raise OSError, "Disk creation failed!" raise OSError, "Disk creation failed!"
# Options nbd_ip and nbd_port are provided by slapos master # Options nbd_ip and nbd_port are provided by slapos master
kvm_conf['nbd_ip'] = self.parameter_dict['nbd_ip'] kvm_conf['nbd_ip'] = self.parameter_dict['nbd_ip']
kvm_conf['nbd_port'] = self.parameter_dict['nbd_port'] kvm_conf['nbd_port'] = self.parameter_dict['nbd_port']
...@@ -133,37 +152,45 @@ class Recipe(BaseSlapRecipe): ...@@ -133,37 +152,45 @@ class Recipe(BaseSlapRecipe):
kvm_conf['ram_size'] = self.options['ram_size'] kvm_conf['ram_size'] = self.options['ram_size']
kvm_conf['vnc_display'] = 1 kvm_conf['vnc_display'] = 1
# Instanciate KVM # Instanciate KVM
kvm_template_location = pkg_resources.resource_filename( kvm_template_location = pkg_resources.resource_filename(
__name__, os.path.join( __name__, 'template/kvm_run.in')
'template', 'kvm_run.in'))
kvm_runner_path = self.createRunningWrapper("kvm",
kvm_runner_path = self.createRunningWrapper("kvm",
self.substituteTemplate(kvm_template_location, self.substituteTemplate(kvm_template_location,
kvm_conf)) kvm_conf))
self.path_list.append(kvm_runner_path) self.path_list.append(kvm_runner_path)
# Instanciate KVM controller # Instanciate KVM controller
kvm_controller_template_location = pkg_resources.resource_filename( kvm_controller_template_location = pkg_resources.resource_filename(
__name__, os.path.join( __name__, 'template/kvm_controller_run.in')
'template',
'kvm_controller_run.in' )) kvm_controller_runner_path = self.createRunningWrapper("kvm_controller",
kvm_controller_runner_path = self.createRunningWrapper("kvm_controller",
self.substituteTemplate(kvm_controller_template_location, self.substituteTemplate(kvm_controller_template_location,
kvm_conf)) kvm_conf))
self.path_list.append(kvm_controller_runner_path) self.path_list.append(kvm_controller_runner_path)
# Instanciate Slapmonitor # Instanciate Slapmonitor
##slapmonitor_runner_path = self.instanciate_wrapper("slapmonitor", ##slapmonitor_runner_path = self.instanciate_wrapper("slapmonitor",
# [database_path, pid_file_path, python_path]) # [database_path, pid_file_path, python_path])
# Instanciate Slapreport # Instanciate Slapreport
##slapreport_runner_path = self.instanciate_wrapper("slapreport", ##slapreport_runner_path = self.instanciate_wrapper("slapreport",
# [database_path, python_path]) # [database_path, python_path])
# Add VNC promise
self.port_listening_promise_conf.update(
hostname=kvm_conf['vnc_ip'],
port=Recipe.VNC_BASE_PORT + kvm_conf['vnc_display'],
)
self.createPromiseWrapper("vnc_promise",
self.substituteTemplate(self.port_listening_promise_path,
self.port_listening_promise_conf,
)
)
return kvm_conf return kvm_conf
def installNoVnc(self, source_ip, source_port, target_ip, target_port): def installNoVnc(self, source_ip, source_port, target_ip, target_port):
...@@ -204,9 +231,19 @@ class Recipe(BaseSlapRecipe): ...@@ -204,9 +231,19 @@ class Recipe(BaseSlapRecipe):
[self.certificate_path, self.key_path], [self.certificate_path, self.key_path],
environment] environment]
)[0] )[0]
self.path_list.append(websockify_runner_path) self.path_list.append(websockify_runner_path)
# Add noVNC promise
self.port_listening_promise_conf.update(hostname=noVNC_conf['source_ip'],
port=noVNC_conf['source_port'],
)
self.createPromiseWrapper("novnc_promise",
self.substituteTemplate(self.port_listening_promise_path,
self.port_listening_promise_conf,
)
)
return noVNC_conf return noVNC_conf
def linkBinary(self): def linkBinary(self):
...@@ -230,7 +267,7 @@ class Recipe(BaseSlapRecipe): ...@@ -230,7 +267,7 @@ class Recipe(BaseSlapRecipe):
os.symlink(target, link) os.symlink(target, link)
self.logger.debug('Created link %r -> %r' % (link, target)) self.logger.debug('Created link %r -> %r' % (link, target))
self.path_list.append(link) self.path_list.append(link)
def installCertificateAuthority(self, ca_country_code='XX', def installCertificateAuthority(self, ca_country_code='XX',
ca_email='xx@example.com', ca_state='State', ca_city='City', ca_email='xx@example.com', ca_state='State', ca_city='City',
ca_company='Company'): ca_company='Company'):
...@@ -289,7 +326,7 @@ class Recipe(BaseSlapRecipe): ...@@ -289,7 +326,7 @@ class Recipe(BaseSlapRecipe):
ca_crl=os.path.join(config['ca_dir'], 'crl'), ca_crl=os.path.join(config['ca_dir'], 'crl'),
certificate_authority_path=config['ca_dir'] certificate_authority_path=config['ca_dir']
) )
def requestCertificate(self, name): def requestCertificate(self, name):
hash = hashlib.sha512(name).hexdigest() hash = hashlib.sha512(name).hexdigest()
key = os.path.join(self.ca_private, hash + self.ca_key_ext) key = os.path.join(self.ca_private, hash + self.ca_key_ext)
...@@ -301,7 +338,7 @@ class Recipe(BaseSlapRecipe): ...@@ -301,7 +338,7 @@ class Recipe(BaseSlapRecipe):
parser.set('certificate', 'certificate_file', certificate) parser.set('certificate', 'certificate_file', certificate)
parser.write(open(os.path.join(self.ca_request_dir, hash), 'w')) parser.write(open(os.path.join(self.ca_request_dir, hash), 'w'))
return key, certificate return key, certificate
def installCrond(self): def installCrond(self):
timestamps = self.createDataDirectory('cronstamps') timestamps = self.createDataDirectory('cronstamps')
cron_output = os.path.join(self.log_directory, 'cron-output') cron_output = os.path.join(self.log_directory, 'cron-output')
......
import socket
import sys
def connection_attempt():
try:
hostname, port = sys.argv[1:3]
except ValueError:
print >> sys.stderr, """Bad command line.
Usage: %s hostname|ip port""" % sys.argv[0]
sys.exit(1)
connection_okay = False
try:
s = socket.create_connection((hostname, port))
connection_okay = True
s.close()
except (socket.error, socket.timeout):
connection_okay = False
if not connection_okay:
print >> sys.stderr, "%(port)s on %(ip)s isn't listening" % {
'port': port, 'ip': hostname
}
sys.exit(127)
#!/usr/bin/env sh
"%(check_port_listening_script)s" "%(hostname)s" "%(port)s"
exit $?
...@@ -60,6 +60,7 @@ class BaseSlapRecipe: ...@@ -60,6 +60,7 @@ class BaseSlapRecipe:
'xml_report') 'xml_report')
self.destroy_script_location = os.path.join(self, self.work_directory, self.destroy_script_location = os.path.join(self, self.work_directory,
'sbin', 'destroy') 'sbin', 'destroy')
self.promise_directory = os.path.join(self.etc_directory, 'promise')
# default directory structure information # default directory structure information
self.default_directory_list = [ self.default_directory_list = [
...@@ -71,6 +72,7 @@ class BaseSlapRecipe: ...@@ -71,6 +72,7 @@ class BaseSlapRecipe:
self.etc_directory, # CP/etc - configuration container self.etc_directory, # CP/etc - configuration container
self.wrapper_directory, # CP/etc/run - for wrappers self.wrapper_directory, # CP/etc/run - for wrappers
self.wrapper_report_directory, # CP/etc/report - for report wrappers self.wrapper_report_directory, # CP/etc/report - for report wrappers
self.promise_directory, # CP/etc/promise - for promise checking scripts
self.var_directory, # CP/var - partition "internal" container for logs, self.var_directory, # CP/var - partition "internal" container for logs,
# and another metadata # and another metadata
self.wrapper_xml_report_directory, # CP/var/xml_report - for xml_report wrappers self.wrapper_xml_report_directory, # CP/var/xml_report - for xml_report wrappers
...@@ -243,3 +245,14 @@ class BaseSlapRecipe: ...@@ -243,3 +245,14 @@ class BaseSlapRecipe:
def _install(self): def _install(self):
"""Hook which shall be implemented in children class""" """Hook which shall be implemented in children class"""
raise NotImplementedError('Shall be implemented by subclass') raise NotImplementedError('Shall be implemented by subclass')
def createPromiseWrapper(self, promise_name, file_content):
"""Create a promise wrapper.
This wrapper aim to check if the software release is doing its job.
Return the promise file path.
"""
promise_path = os.path.join(self.promise_directory, promise_name)
self._writeExecutable(promise_path, file_content)
return promise_path
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