############################################################################## # # Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved. # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsibility of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # guarantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 3 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## import subprocess from slapos.recipe.librecipe import GenericBaseRecipe import socket import struct import os, stat import string, random import json import traceback from slapos import slap class Recipe(GenericBaseRecipe): def __init__(self, buildout, name, options): """Default initialisation""" self.slap = slap.slap() # SLAP related information slap_connection = buildout['slap-connection'] self.computer_id = slap_connection['computer-id'] self.computer_partition_id = slap_connection['partition-id'] self.server_url = slap_connection['server-url'] self.software_release_url = slap_connection['software-release-url'] self.key_file = slap_connection.get('key-file') self.cert_file = slap_connection.get('cert-file') self.slave_list = json.loads(options['slave-instance-list']) options['slave-amount'] = '%s' % len(self.slave_list) return GenericBaseRecipe.__init__(self, buildout, name, options) def getSerialFromIpv6(self, ipv6): prefix = ipv6.split('/')[0].lower() hi, lo = struct.unpack('!QQ', socket.inet_pton(socket.AF_INET6, prefix)) ipv6_int = (hi << 64) | lo serial = '0x1%x' % ipv6_int # delete non significant part for part in prefix.split(':')[::-1]: if part: for i in ['0']*(4 - len(part)): part = i + part serial = serial.split(part)[0] + part break return serial def generateCertificate(self): key_file = self.options['key-file'].strip() cert_file = self.options['cert-file'].strip() dh_file = self.options['dh-file'].strip() if not os.path.exists(dh_file): dh_command = [self.options['openssl-bin'], 'dhparam', '-out', '%s' % dh_file, self.options['key-size']] try: subprocess.check_call(dh_command) except Exception: if os.path.exists(dh_file): os.unlink(dh_file) raise if not os.path.exists(cert_file): serial = self.getSerialFromIpv6(self.options['ipv6-prefix'].strip()) key_command = [self.options['openssl-bin'], 'genrsa', '-out', '%s' % key_file, self.options['key-size']] #'-config', openssl_configuration cert_command = [self.options['openssl-bin'], 'req', '-nodes', '-new', '-x509', '-batch', '-key', '%s' % key_file, '-set_serial', '%s' % serial, '-days', '3650', '-out', '%s' % cert_file] try: subprocess.check_call(key_command) except Exception: if os.path.exists(key_file): os.unlink(key_file) raise try: subprocess.check_call(cert_command) except Exception: if os.path.exists(cert_file): os.unlink(cert_file) raise def generateSlaveTokenList(self, slave_instance_list, token_file): to_remove_dict = {} to_add_dict = {} token_dict = self.loadJsonFile(token_file) reference_list = [slave_instance.get('slave_reference') for slave_instance in slave_instance_list] for reference in reference_list: if not reference in token_dict: # we generate new token number = reference.split('-')[1] new_token = number + ''.join(random.sample(string.ascii_lowercase, 20)) token_dict[reference] = new_token to_add_dict[reference] = new_token for reference in token_dict.keys(): if not reference in reference_list: # This slave instance is destroyed ? to_remove_dict[reference] = token_dict.pop(reference) return token_dict, to_add_dict, to_remove_dict def loadJsonFile(self, path): if os.path.exists(path): with open(path, 'r') as f: content = f.read() return json.loads(content) else: return {} def writeFile(self, path, data): with open(path, 'w') as f: f.write(data) return path def readFile(self, path): if os.path.exists(path): with open(path, 'r') as f: content = f.read() return content return '' def genHash(self, length): hash_path = os.path.join(self.options['conf-dir'], '%s-hash' % length) if not os.path.exists(hash_path): pool = string.letters + string.digits hash_string = ''.join(random.choice(pool) for i in xrange(length)) self.writeFile(hash_path, hash_string) else: hash_string = self.readFile(hash_path) return hash_string def install(self): path_list = [] token_save_path = os.path.join(self.options['conf-dir'], 'token.json') token_list_path = self.options['token-dir'] self.generateCertificate() wrapper = self.createFile(self.options['wrapper'], self.substituteTemplate( self.getTemplateFilename('registry-run.in'), dict( parameter='@%s' % self.options['config-file'], pid_file=self.options['pid-file'], command=self.options['command'] ) ) ) os.chmod(self.options['wrapper'], stat.S_IRWXU) path_list.append(wrapper) registry_url = 'http://%s:%s/' % (self.options['ipv4'], self.options['port']) token_dict, add_token_dict, rm_token_dict = self.generateSlaveTokenList( self.slave_list, token_save_path) # write request add token for reference in add_token_dict: path = os.path.join(token_list_path, '%s.add' % reference) if not os.path.exists(path): self.createFile(path, add_token_dict[reference]) # write request remove token for reference in rm_token_dict: path = os.path.join(token_list_path, '%s.remove' % reference) if not os.path.exists(path): self.createFile(path, rm_token_dict[reference]) # remove request add token if exists add_path = os.path.join(token_list_path, '%s.add' % reference) if os.path.exists(add_path): os.unlink(add_path) self.createFile(token_save_path, json.dumps(token_dict)) service_dict = dict(token_base_path=token_list_path, token_json=token_save_path, db=self.options['db-path'], partition_id=self.computer_partition_id, computer_id=self.computer_id, registry_url=registry_url) service_dict['server_url'] = self.server_url service_dict['cert_file'] = self.cert_file service_dict['key_file'] = self.key_file request_add = self.createPythonScript( self.options['manager-wrapper'].strip(), '%s.re6stnet.manage' % __name__, service_dict ) path_list.append(request_add) request_drop = self.createPythonScript( self.options['drop-service-wrapper'].strip(), '%s.re6stnet.requestRemoveToken' % __name__, service_dict ) path_list.append(request_drop) request_check = self.createPythonScript( self.options['check-service-wrapper'].strip(), '%s.re6stnet.checkService' % __name__, service_dict ) path_list.append(request_check) revoke_check = self.createPythonScript( self.options['revoke-service-wrapper'].strip(), '%s.re6stnet.requestRevoqueCertificate' % __name__, service_dict ) path_list.append(revoke_check) # Send connection parameters of slave instances if token_dict: self.slap.initializeConnection(self.server_url, self.key_file, self.cert_file) computer_partition = self.slap.registerComputerPartition( self.computer_id, self.computer_partition_id) for slave_reference, token in token_dict.iteritems(): try: status_file = os.path.join(token_list_path, '%s.status' % slave_reference) status = self.readFile(status_file) or 'New token requested' msg = status if status == 'TOKEN_ADDED': msg = 'Token is ready for use' elif status == 'TOKEN_USED': msg = 'Token not available, it has been used to generate re6stnet certificate.' computer_partition.setConnectionDict( {'token':token, '1_info':msg}, slave_reference) except Exception: self.logger.fatal("Error while sending slave %s informations: %s", slave_reference, traceback.format_exc()) return path_list