diff --git a/slapos/proxy/schema.sql b/slapos/proxy/schema.sql index 9f1bd1674ae18653a600e86be7dbb86e61ee6666..30f30df3f5d2b8035c21cece7abd7831a33f78f5 100644 --- a/slapos/proxy/schema.sql +++ b/slapos/proxy/schema.sql @@ -1,4 +1,4 @@ ---version:7 +--version:9 CREATE TABLE IF NOT EXISTS software%(version)s (url VARCHAR(255) UNIQUE); CREATE TABLE IF NOT EXISTS computer%(version)s ( address VARCHAR(255), @@ -11,11 +11,19 @@ CREATE TABLE IF NOT EXISTS partition%(version)s ( software_release VARCHAR(255), xml TEXT, connection_xml TEXT, + slave_instance_list TEXT, software_type VARCHAR(255), partition_reference VARCHAR(255), requested_by VARCHAR(255) ); +CREATE TABLE IF NOT EXISTS slave%(version)s ( + reference VARCHAR(255) UNIQUE, + connection_xml TEXT, + hosted_by VARCHAR(255), + asked_by VARCHAR(255) +); + CREATE TABLE IF NOT EXISTS partition_network%(version)s ( partition_reference VARCHAR(255), reference VARCHAR(255), diff --git a/slapos/proxy/views.py b/slapos/proxy/views.py index 7880af71ba507c069a3ed5ff7fb59d52e76d14e5..e9afb409ffc39fbce040fa771a0cde43bf580361 100644 --- a/slapos/proxy/views.py +++ b/slapos/proxy/views.py @@ -30,15 +30,18 @@ from flask import g, Flask, request, abort import xml_marshaller from lxml import etree -from slapos.slap.slap import Computer, ComputerPartition, SoftwareRelease, SoftwareInstance +from slapos.slap.slap import Computer, ComputerPartition, \ + SoftwareRelease, SoftwareInstance import sqlite3 app = Flask(__name__) DB_VERSION = app.open_resource('schema.sql').readline().strip().split(':')[1] + class UnauthorizedError(Exception): pass + def xml2dict(xml): result_dict = {} if xml is not None and xml != '': @@ -54,16 +57,18 @@ def xml2dict(xml): result_dict[key] = value return result_dict + def dict2xml(dictionnary): instance = etree.Element('instance') for parameter_id, parameter_value in dictionnary.iteritems(): # cast everything to string parameter_value = str(parameter_value) etree.SubElement(instance, "parameter", - attrib={'id':parameter_id}).text = parameter_value + attrib={'id': parameter_id}).text = parameter_value return etree.tostring(instance, pretty_print=True, xml_declaration=True, encoding='utf-8') + def partitiondict2partition(partition): slap_partition = ComputerPartition(app.config['computer_id'], partition['reference']) @@ -74,35 +79,43 @@ def partitiondict2partition(partition): slap_partition._need_modification = 0 slap_partition._parameter_dict = xml2dict(partition['xml']) address_list = [] - for address in execute_db('partition_network', 'SELECT * FROM %s WHERE partition_reference=?', [partition['reference']]): + for address in execute_db('partition_network', + 'SELECT * FROM %s WHERE partition_reference=?', + [partition['reference']]): address_list.append((address['reference'], address['address'])) + if not partition['slave_instance_list'] == None: + slap_partition._parameter_dict['slave_instance_list'] = \ + xml_marshaller.xml_marshaller.loads(partition['slave_instance_list']) slap_partition._parameter_dict['ip_list'] = address_list - slap_partition._parameter_dict['slap_software_type'] = partition['software_type'] + slap_partition._parameter_dict['slap_software_type'] = \ + partition['software_type'] slap_partition._connection_dict = xml2dict(partition['connection_xml']) slap_partition._software_release_document = SoftwareRelease( software_release=partition['software_release'], computer_guid=app.config['computer_id']) return slap_partition + def execute_db(table, query, args=(), one=False): try: cur = g.db.execute(query % (table + DB_VERSION,), args) except: app.logger.error('There was some issue during processing query %r on table %r with args %r' % (query, table, args)) raise - rv = [dict((cur.description[idx][0], value) for idx, value in enumerate(row)) for row in cur.fetchall()] return (rv[0] if rv else None) if one else rv + def connect_db(): return sqlite3.connect(app.config['DATABASE_URI']) + @app.before_request def before_request(): g.db = connect_db() schema = app.open_resource('schema.sql') - schema = schema.read() % dict(version = DB_VERSION) + schema = schema.read() % dict(version=DB_VERSION) g.db.cursor().executescript(schema) g.db.commit() @@ -150,16 +163,23 @@ def getFullComputerInformation(): @app.route('/setComputerPartitionConnectionXml', methods=['POST']) def setComputerPartitionConnectionXml(): - computer_id = request.form['computer_id'] + slave_reference = request.form['slave_reference'].encode() computer_partition_id = request.form['computer_partition_id'] connection_xml = request.form['connection_xml'] connection_dict = xml_marshaller.xml_marshaller.loads( - connection_xml.encode()) + connection_xml.encode()) connection_xml = dict2xml(connection_dict) - query = 'UPDATE %s SET connection_xml=? WHERE reference=?' - argument_list = [connection_xml, computer_partition_id.encode()] - execute_db('partition', query, argument_list) - return 'done' + if slave_reference == 'None': + query = 'UPDATE %s SET connection_xml=? WHERE reference=?' + argument_list = [connection_xml, computer_partition_id.encode()] + execute_db('partition', query, argument_list) + return 'done' + else: + query = 'UPDATE %s SET connection_xml=? , hosted_by=? WHERE reference=?' + argument_list = [connection_xml, computer_partition_id.encode(), + slave_reference] + execute_db('slave', query, argument_list) + return 'done' @app.route('/buildingSoftwareRelease', methods=['POST']) def buildingSoftwareRelease(): @@ -167,8 +187,6 @@ def buildingSoftwareRelease(): @app.route('/availableSoftwareRelease', methods=['POST']) def availableSoftwareRelease(): - computer_id = request.form['computer_id'] - url = request.form['url'] return 'Ignored' @app.route('/softwareReleaseError', methods=['POST']) @@ -177,43 +195,90 @@ def softwareReleaseError(): @app.route('/buildingComputerPartition', methods=['POST']) def buildingComputerPartition(): - computer_id = request.form['computer_id'] - computer_partition_id = request.form['computer_partition_id'] return 'Ignored' @app.route('/availableComputerPartition', methods=['POST']) def availableComputerPartition(): - computer_id = request.form['computer_id'] - computer_partition_id = request.form['computer_partition_id'] return 'Ignored' @app.route('/softwareInstanceError', methods=['POST']) def softwareInstanceError(): - computer_id = request.form['computer_id'] - computer_partition_id = request.form['computer_partition_id'] - error_log = request.form['error_log'] return 'Ignored' @app.route('/startedComputerPartition', methods=['POST']) def startedComputerPartition(): - computer_id = request.form['computer_id'] - computer_partition_id = request.form['computer_partition_id'] return 'Ignored' @app.route('/stoppedComputerPartition', methods=['POST']) def stoppedComputerPartition(): - computer_id = request.form['computer_id'] - computer_partition_id = request.form['computer_partition_id'] return 'Ignored' @app.route('/destroyedComputerPartition', methods=['POST']) def destroyedComputerPartition(): - computer_id = request.form['computer_id'] - computer_partition_id = request.form['computer_partition_id'] return 'Ignored' +@app.route('/useComputer', methods=['POST']) +def useComputer(): + return 'Ignored' + +@app.route('/loadComputerConfigurationFromXML', methods=['POST']) +def loadComputerConfigurationFromXML(): + xml = request.form['xml'] + computer_dict = xml_marshaller.xml_marshaller.loads(str(xml)) + if app.config['computer_id'] == computer_dict['reference']: + execute_db('computer', 'INSERT OR REPLACE INTO %s values(:address, :netmask)', + computer_dict) + for partition in computer_dict['partition_list']: + + execute_db('partition', 'INSERT OR IGNORE INTO %s (reference) values(:reference)', partition) + execute_db('partition_network', 'DELETE FROM %s WHERE partition_reference = ?', [partition['reference']]) + for address in partition['address_list']: + address['reference'] = partition['tap']['name'] + address['partition_reference'] = partition['reference'] + execute_db('partition_network', 'INSERT OR REPLACE INTO %s (reference, partition_reference, address, netmask) values(:reference, :partition_reference, :addr, :netmask)', address) + + return 'done' + else: + raise UnauthorizedError, "Only accept request for: %s" % \ + app.config['computer_id'] + +@app.route('/registerComputerPartition', methods=['GET']) +def registerComputerPartition(): + computer_reference = request.args['computer_reference'] + computer_partition_reference = request.args['computer_partition_reference'] + if app.config['computer_id'] == computer_reference: + partition = execute_db('partition', 'SELECT * FROM %s WHERE reference=?', + [computer_partition_reference.encode()], one=True) + if partition is None: + raise UnauthorizedError + return xml_marshaller.xml_marshaller.dumps( + partitiondict2partition(partition)) + else: + raise UnauthorizedError, "Only accept request for: %s" % \ + app.config['computer_id'] + +@app.route('/supplySupply', methods=['POST']) +def supplySupply(): + url = request.form['url'] + computer_id = request.form['computer_id'] + if app.config['computer_id'] == computer_id: + execute_db('software', 'INSERT OR REPLACE INTO %s VALUES(?)', [url]) + else: + raise UnauthorizedError, "Only accept request for: %s" % \ + app.config['computer_id'] + return '%r added' % url + + @app.route('/requestComputerPartition', methods=['POST']) def requestComputerPartition(): + shared_xml = request.form.get('shared_xml') + share = xml_marshaller.xml_marshaller.loads(shared_xml) + if not share: + return request_not_shared() + else: + return request_slave() + +def request_not_shared(): software_release = request.form['software_release'].encode() # some supported parameters software_type = request.form.get('software_type', 'RootSoftwareInstance' @@ -261,7 +326,7 @@ def requestComputerPartition(): q += ' ,requested_by=?' a(partition_id) if instance_xml: - q+= ' ,xml=?' + q += ' ,xml=?' a(instance_xml) q += ' WHERE reference=?' a(partition['reference'].encode()) @@ -280,70 +345,91 @@ def requestComputerPartition(): slap_software_release_url=partition['software_release'], slap_server_url='slap_server_url', slap_software_type=partition['software_type'], - slave_id_list=[], + slave_instance_list=partition['slave_instance_list'], ip_list=address_list ))) abort(408) - computer_id = request.form.get('computer_id') - computer_partition_id = request.form.get('computer_partition_id') - software_type = request.form.get('software_type') - partition_reference = request.form.get('partition_reference') - shared_xml = request.form.get('shared_xml') - partition_parameter_xml = request.form.get('partition_parameter_xml') - filter_xml = request.form.get('filter_xml') raise NotImplementedError -@app.route('/useComputer', methods=['POST']) -def useComputer(): - computer_id = request.form['computer_id'] - use_string = request.form['use_string'] - return 'Ignored' - -@app.route('/loadComputerConfigurationFromXML', methods=['POST']) -def loadComputerConfigurationFromXML(): - xml = request.form['xml'] - computer_dict = xml_marshaller.xml_marshaller.loads(str(xml)) - if app.config['computer_id'] == computer_dict['reference']: - args = [] - a = args.append - execute_db('computer', 'INSERT OR REPLACE INTO %s values(:address, :netmask)', - computer_dict) - for partition in computer_dict['partition_list']: - - execute_db('partition', 'INSERT OR IGNORE INTO %s (reference) values(:reference)', partition) - execute_db('partition_network', 'DELETE FROM %s WHERE partition_reference = ?', [partition['reference']]) - for address in partition['address_list']: - address['reference'] = partition['tap']['name'] - address['partition_reference'] = partition['reference'] - execute_db('partition_network', 'INSERT OR REPLACE INTO %s (reference, partition_reference, address, netmask) values(:reference, :partition_reference, :addr, :netmask)', address) - return 'done' +def request_slave(): + """ + Function to organise link between slave and master + """ + software_release = request.form['software_release'].encode() + # some supported parameters + software_type = request.form.get('software_type').encode() + partition_reference = request.form.get('partition_reference', '').encode() + partition_id = request.form.get('computer_partition_id', '').encode() + partition_parameter_kw = request.form.get('partition_parameter_xml', None) + if partition_parameter_kw : + partition_parameter_kw = xml_marshaller.xml_marshaller.loads( + partition_parameter_kw.encode()) else: - raise UnauthorizedError, "Only accept request for: %s" % \ - app.config['computer_id'] + partition_parameter_kw = {} + instance_xml = dict2xml(partition_parameter_kw) + args = [] + a = args.append + q = 'SELECT * FROM %s WHERE software_release=?' + a(software_release) + if software_type: + q += ' AND software_type=?' + a(software_type) + partition = execute_db('partition', q, args, one=True) + if partition is None: + app.logger.warning('No partition corresponding to slave request') + abort(408) + # Define Slave + new_slave = {} + slave_reference = partition_id + '_' + partition_reference + new_slave['slave_title'] = slave_reference + new_slave['slap_software_type'] = software_type + new_slave['slave_reference'] = slave_reference + for key in partition_parameter_kw : + new_slave[key] = partition_parameter_kw[key] -@app.route('/registerComputerPartition', methods=['GET']) -def registerComputerPartition(): - computer_reference = request.args['computer_reference'] - computer_partition_reference = request.args['computer_partition_reference'] - if app.config['computer_id'] == computer_reference: - partition = execute_db('partition', 'SELECT * FROM %s WHERE reference=?', - [computer_partition_reference.encode()], one=True) - if partition is None: - raise UnauthorizedError - return xml_marshaller.xml_marshaller.dumps( - partitiondict2partition(partition)) + # Add slave to partition slave_list if not present + slave_instance_list = partition['slave_instance_list'] + if slave_instance_list == None: + slave_instance_list = [] else: - raise UnauthorizedError, "Only accept request for: %s" % \ - app.config['computer_id'] + slave_instance_list = xml_marshaller.xml_marshaller.loads(slave_instance_list) + if not slave_reference in (x['slave_reference'] for x in slave_instance_list): + slave_instance_list.append(new_slave) -@app.route('/supplySupply', methods=['POST']) -def supplySupply(): - url = request.form['url'] - computer_id = request.form['computer_id'] - if app.config['computer_id'] == computer_id: - execute_db('software', 'INSERT OR REPLACE INTO %s VALUES(?)', [url]) - else: - raise UnauthorizedError, "Only accept request for: %s" % \ - app.config['computer_id'] - return '%r added' % url + args = [] + a = args.append + q = 'UPDATE %s SET slave_instance_list=?' + a(xml_marshaller.xml_marshaller.dumps(slave_instance_list)) + q += ' WHERE reference=?' + a(partition['reference'].encode()) + execute_db('partition', q, args) + args = [] + partition = execute_db('partition', 'SELECT * FROM %s WHERE reference=?', + [partition['reference'].encode()], one=True) + + # Add slave to slave table if not there + slave = execute_db('slave', 'SELECT * FROM %s WHERE reference=?', + [slave_reference], one=True) + if slave is None : + execute_db('slave', + 'INSERT OR IGNORE INTO %s (reference,asked_by) values(:reference,:asked_by)', + [slave_reference,partition_id]) + slave = execute_db('slave','SELECT * FROM %s WHERE reference=?', + [slave_reference], one = True) + + address_list = [] + for address in execute_db('partition_network', + 'SELECT * FROM %s WHERE partition_reference=?', + [partition['reference']]): + address_list.append((address['reference'], address['address'])) + return xml_marshaller.xml_marshaller.dumps(SoftwareInstance(**dict( + _connection_dict=xml2dict(slave['connection_xml']), + xml = instance_xml, + slap_computer_id=app.config['computer_id'], + slap_computer_partition_id=slave['hosted_by'], + slap_software_release_url=partition['software_release'], + slap_server_url='slap_server_url', + slap_software_type=partition['software_type'], + ip_list=address_list + )))