diff --git a/slapos/recipe/kvm/template/kvm_run.in b/slapos/recipe/kvm/template/kvm_run.in index 81116c5fad6c315abd8ae19c42ae336ded62d4f9..88eb0f0f3452620fbf4f438ee1af63032d8e3011 100644 --- a/slapos/recipe/kvm/template/kvm_run.in +++ b/slapos/recipe/kvm/template/kvm_run.in @@ -24,10 +24,10 @@ default_disk_image = '%(default-disk-image)s' disk_path = '%(disk-path)s' virtual_hard_drive_url = '%(virtual-hard-drive-url)s'.strip() virtual_hard_drive_md5sum = '%(virtual-hard-drive-md5sum)s'.strip() -virtual_hard_drive_gzipped = '%(virtual-hard-drive-gzipped)s'.strip() +virtual_hard_drive_gzipped = '%(virtual-hard-drive-gzipped)s'.strip().lower() nat_rules = '%(nat-rules)s'.strip() -use_tap = '%(use-tap)s' -use_nat = '%(use-nat)s' +use_tap = '%(use-tap)s'.lower() +use_nat = '%(use-nat)s'.lower() tap_interface = '%(tap-interface)s' listen_ip = '%(ipv4)s' mac_address = '%(mac-address)s' @@ -119,7 +119,7 @@ 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': + if virtual_hard_drive_gzipped == 'true': downloaded_disk = '%%s.gz' %% disk_path urllib.urlretrieve(virtual_hard_drive_url, downloaded_disk) except: @@ -162,7 +162,7 @@ for storage in disk_storage_list: key, val = storage.split(' ') disk_storage_dict[key.strip()] = val.strip() -if not external_disk_format in ['qcow2', 'raw', 'vdi', 'vmdk', 'cloop']: +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, @@ -197,7 +197,7 @@ tap_network_parameter = [] nat_network_parameter = [] numa_parameter = [] number = -1 -if use_nat == 'True': +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()) @@ -209,7 +209,7 @@ if use_nat == 'True': cluster_doc_host, cluster_doc_port) nat_network_parameter = ['-netdev', rules, '-device', 'e1000,netdev=lan%%s,mac=%%s' %% (number, mac_address)] -if use_tap == 'True': +if use_tap == 'true': number += 1 tap_network_parameter = ['-netdev', 'tap,id=lan%%s,ifname=%%s,script=no,downscript=no' %% (number, diff --git a/slapos/recipe/simplehttpserver/__init__.py b/slapos/recipe/simplehttpserver/__init__.py index f5d76cf8dad3654bfa06edd558754b0561e2401c..9f7413417903e60e081eb47393df85467d6faeab 100644 --- a/slapos/recipe/simplehttpserver/__init__.py +++ b/slapos/recipe/simplehttpserver/__init__.py @@ -33,20 +33,24 @@ class Recipe(GenericBaseRecipe): def __init__(self, buildout, name, options): base_path = options['base-path'] - pool = string.letters + string.digits - hash_string = ''.join(random.choice(pool) for i in xrange(64)) - path = os.path.join(base_path, hash_string) - - if os.path.exists(base_path): - path_list = os.listdir(base_path) - if len(path_list) == 1: - hash_string = path_list[0] - path = os.path.join(base_path, hash_string) - elif len(path_list) > 1: - raise ValueError("Folder %s should contain 0 or 1 element." % base_path) - - options['root-dir'] = path - options['path'] = hash_string + if options.get('use-hash-url', 'True') in ['true', 'True']: + pool = string.letters + string.digits + hash_string = ''.join(random.choice(pool) for i in xrange(64)) + path = os.path.join(base_path, hash_string) + + if os.path.exists(base_path): + path_list = os.listdir(base_path) + if len(path_list) == 1: + hash_string = path_list[0] + path = os.path.join(base_path, hash_string) + elif len(path_list) > 1: + raise ValueError("Folder %s should contain 0 or 1 element." % base_path) + + options['root-dir'] = path + options['path'] = hash_string + else: + options['root-dir'] = base_path + options['path'] = '' return GenericBaseRecipe.__init__(self, buildout, name, options) @@ -59,8 +63,9 @@ class Recipe(GenericBaseRecipe): 'port': int(self.options['port']), 'cwd': self.options['base-path'], 'log-file': self.options['log-file'], - 'cert-file': self.options['cert-file'], - 'key-file': self.options['key-file'] + 'cert-file': self.options.get('cert-file', ''), + 'key-file': self.options.get('key-file', ''), + 'root-dir': self.options['root-dir'] } server = self.createPythonScript( diff --git a/slapos/recipe/simplehttpserver/simplehttpserver.py b/slapos/recipe/simplehttpserver/simplehttpserver.py index a11a087a8ec36566b9e92644c1324d3caff4a3ef..db8f2bfe5980f45844a0a95f3c81a713332ee1e8 100644 --- a/slapos/recipe/simplehttpserver/simplehttpserver.py +++ b/slapos/recipe/simplehttpserver/simplehttpserver.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from SimpleHTTPServer import SimpleHTTPRequestHandler from BaseHTTPServer import HTTPServer import ssl @@ -5,26 +6,74 @@ import os import logging from netaddr import valid_ipv4, valid_ipv6 import socket +import cgi, errno class ServerHandler(SimpleHTTPRequestHandler): - def respond(self, code=200, type='text/plain'): + document_path = '' + restrict_root_folder = True + + def respond(self, code=200, type='text/html'): self.send_response(code) self.send_header("Content-type", type) self.end_headers() - def do_GET(self): - logging.info('%s - GET: %s \n%s' % (self.client_address[0], self.path, self.headers)) - if not self.path or self.path == '/': + def restrictedRootAccess(self): + if self.restrict_root_folder and self.path and self.path == '/': # no access to root path self.respond(403) self.wfile.write("Forbidden") + return True + return False + + def do_GET(self): + logging.info('%s - GET: %s \n%s' % (self.client_address[0], self.path, self.headers)) + if self.restrictedRootAccess(): return SimpleHTTPRequestHandler.do_GET(self) + def do_POST(self): + logging.info('%s - POST: %s \n%s' % (self.client_address[0], self.path, self.headers)) + if self.restrictedRootAccess(): + return + + form = cgi.FieldStorage( + fp=self.rfile, + headers=self.headers, + environ={'REQUEST_METHOD':'POST', + 'CONTENT_TYPE':self.headers['Content-Type']} + ) + name = form['path'].value + content = form['content'].value + method = 'a' + if form.has_key('clear') and form['clear'].value == '1': + method = 'w' + self.writeFile(name, content, method) + self.respond(200, type=self.headers['Content-Type']) + self.wfile.write("Content written to %s" % name) + + def writeFile(self, filename, content, method='a'): + file_path = os.path.join(self.document_path, filename) + + try: + os.makedirs(os.path.dirname(file_path)) + except OSError as exception: + if exception.errno != errno.EEXIST: + logging.error('Failed to create file in %s. The error is \n%s' % ( + file_path, str(exception))) + + logging.info('Writing recieved content to file %s' % file_path) + try: + with open(file_path, method) as myfile: + myfile.write(content.decode('utf-8')) + logging.info('Done.') + except IOError as e: + logging.error('Something happened while processing \'writeFile\'. The message is %s' % + str(e)) + class HTTPServerV6(HTTPServer): address_family = socket.AF_INET6 - + def run(args): @@ -38,6 +87,8 @@ def run(args): os.chdir(args['cwd']) Handler = ServerHandler + Handler.document_path = args['root-dir'] + Handler.restrict_root_folder = (args['root-dir'] != args['cwd']) if valid_ipv6(host): server = HTTPServerV6 @@ -45,11 +96,14 @@ def run(args): server = HTTPServer httpd = server((host, port), Handler) - if args.has_key('cert-file') and args.has_key('key-file'): + scheme = 'http' + if args.has_key('cert-file') and args.has_key('key-file') and \ + os.path.exists(args['cert-file']) and os.path.exists(args['key-file']): + scheme = 'https' httpd.socket = ssl.wrap_socket (httpd.socket, server_side=True, certfile=args['cert-file'], keyfile=args['key-file']) - logging.info("Starting simple http server at https://%s:%s" % (host, port)) + logging.info("Starting simple http server at %s://%s:%s" % (scheme, host, port)) httpd.serve_forever() diff --git a/software/kvm/common.cfg b/software/kvm/common.cfg index 1cb707feb78b5d81bbbaa8db46fe954d5240bc84..2e38452010b674c03e6330687e01567ba051d9d7 100644 --- a/software/kvm/common.cfg +++ b/software/kvm/common.cfg @@ -87,7 +87,7 @@ command = [template] recipe = slapos.recipe.template url = ${:_profile_base_location_}/instance.cfg.in -md5sum = cf67212d3155767d0d0d8a6d75d2d8ad +md5sum = 3bca2c959d19881270c64f94ad1ebba8 output = ${buildout:directory}/template.cfg mode = 0644 @@ -95,7 +95,7 @@ mode = 0644 recipe = hexagonit.recipe.download url = ${:_profile_base_location_}/instance-kvm.cfg.jinja2 mode = 644 -md5sum = 3e3354844b2052609e3c49eca03b607e +md5sum = ea1e8f4a7c1878beec83267fd40728c2 download-only = true on-update = true @@ -103,7 +103,7 @@ on-update = true recipe = hexagonit.recipe.download url = ${:_profile_base_location_}/instance-kvm-cluster.cfg.jinja2.in mode = 644 -md5sum = 1f7dc7b7f2740cf416927b144e93ccb1 +md5sum = 5a864099760e3a37fa4604044d708657 download-only = true on-update = true @@ -173,7 +173,7 @@ recipe = hexagonit.recipe.download url = ${:_profile_base_location_}/template/apache.conf.in mode = 644 filename = apache.conf.in -md5sum = 91f05377aff35ffbac7f2687e90b5dcc +md5sum = e9c9fd88d71e9dc7416149af5bcfb951 download-only = true on-update = true @@ -182,7 +182,7 @@ recipe = hexagonit.recipe.download url = ${:_profile_base_location_}/template/template-content.in mode = 644 filename = template-content.in -md5sum = 47d492dafe5cb314bdc49bf013d21ead +md5sum = 822737e483864bf255ad1259237bef2a download-only = true on-update = true @@ -191,9 +191,10 @@ recipe = slapos.recipe.template:jinja2 filename = template-httpd.cfg template = ${:_profile_base_location_}/instance-kvm-http.cfg.in rendered = ${buildout:parts-directory}/${:_buildout_section_name_}/instance-kvm-http.cfg -md5sum = 84b96dfc78e8d2611bf7210b8b6bb9c5 +md5sum = fc8b3259942d6dedbc01065358a00d71 context = key apache_location apache:location + raw openssl_executable_location ${openssl:location}/bin/openssl raw template_apache_conf ${template-apache-conf:location}/${template-apache-conf:filename} diff --git a/software/kvm/instance-kvm-cluster-input-schema.json b/software/kvm/instance-kvm-cluster-input-schema.json index 3c8b1bfa1d2363121ac53e306215719c0388a84c..25e2d8197fb9d15827ec2f86bd1b2eac5f4ed059 100644 --- a/software/kvm/instance-kvm-cluster-input-schema.json +++ b/software/kvm/instance-kvm-cluster-input-schema.json @@ -122,11 +122,16 @@ }, "type": "object" }, - "authorized-key": { + "authorized-keys": { "title": "Public keys for virtual machines.", "description": "Set the list of public keys to add in your virtual machine. The public key file will be available in the VM via url http://10.0.2.100/authorized_keys if you keep the NAT interface enabled", "type": "array" }, + "cluster-data": { + "title": "Text content to share with virtual machines.", + "description": "Text content which will be written in a file data of cluster http server. All VM will be able to download that file via the static URL of cluster HTTP server: https://10.0.2.101/FOLDER_HASH/data.", + "type": "string" + }, "kvm-partition-dict": { "title": "kvm instances definition", "description": "kvm instances definition", @@ -261,7 +266,7 @@ "description": "Type of QEMU disk drive, to create.", "type": "string", "default": "qcow2", - "enum": ["qcow2", "raw", "vdi", "vmdk", "cloop"] + "enum": ["qcow2", "raw", "vdi", "vmdk", "cloop", "qed"] }, "use-tap": { "title": "Use QEMU TAP network interface", @@ -284,6 +289,11 @@ 80, 443 ] + }, + "data-to-vm": { + "title": "Text content to send to this virtual machine.", + "description": "Text content which will be written in a file 'data' of http server of this virtual machine instance. The file will be available via URL: http://10.0.2.100/data in the VM.", + "type": "string" } }, "type": "object" diff --git a/software/kvm/instance-kvm-cluster.cfg.jinja2.in b/software/kvm/instance-kvm-cluster.cfg.jinja2.in index c649ab9e99cea685d5e214bbe85757ddde0e8736..e521b82b9e084028adfc410bf79071743a99ae65 100644 --- a/software/kvm/instance-kvm-cluster.cfg.jinja2.in +++ b/software/kvm/instance-kvm-cluster.cfg.jinja2.in @@ -22,7 +22,7 @@ config-use-ipv6 = {{ dumps(slapparameter_dict.get('use-ipv6', False)) }} # Request kvm instances {% for instance_name, kvm_parameter_dict in slapparameter_dict.get('kvm-partition-dict', {'kvm-default': {}}).items() -%} {% set section = 'request-' ~ instance_name -%} -{% set use_nat = kvm_parameter_dict.get('use-nat', True) -%} +{% set use_nat = kvm_parameter_dict.get('use-nat', 'True') -%} [{{ section }}] <= request-common software-type = kvm @@ -34,8 +34,8 @@ config-frontend-software-type = {{ dumps(frontend_dict.get('software-type', 'fro config-frontend-software-url = {{ dumps(frontend_dict.get('software-url', 'http://git.erp5.org/gitweb/slapos.git/blob_plain/refs/tags/slapos-0.92:/software/kvm/software.cfg')) }} config-frontend-instance-guid = {{ dumps(frontend_dict.get('instance-guid', '')) }} config-name = {{ instance_name }} -{% if slapparameter_dict.get('authorized-key', []) -%} -config-authorized-key = {{ slapparameter_dict.get('authorized-key') | join('##') }} +{% if slapparameter_dict.get('authorized-keys', []) -%} +config-authorized-key = {{ dumps(slapparameter_dict.get('authorized-keys') | join('\n')) }} {% endif -%} config-nbd-port = {{ dumps(kvm_parameter_dict.get('nbd-port', 1024)) }} config-nbd-host = {{ dumps(kvm_parameter_dict.get('nbd-host', '')) }} @@ -61,21 +61,24 @@ config-external-disk-size = {{ dumps(kvm_parameter_dict.get('external-disk-size' config-external-disk-format = {{ dumps(kvm_parameter_dict.get('external-disk-format', 'qcow2')) }} config-enable-http-server = {{ dumps(kvm_parameter_dict.get('enable-http-server', True)) }} config-httpd-port = {{ dumps(kvm_parameter_dict.get('httpd-port', 8081)) }} +{% if kvm_parameter_dict.get('data-to-vm', '') -%} +config-data-to-vm = {{ dumps(kvm_parameter_dict.get('data-to-vm', '')) }} +{% endif -%} # Enable simple http server on ipv6 so all VMs will access it -config-document-host = ${http-server:host} -config-document-port = ${http-server:port} -config-document-path = ${http-server:path} +config-document-host = ${apache-conf:ip} +config-document-port = ${apache-conf:port} +config-document-path = ${hash-code:passwd} return = backend-url url -{% if use_nat -%} +{% if str(use_nat).lower() -%} {% for port in nat_rules_list -%} {{ ' ' }}nat-rule-url-{{ port }} {% endfor -%} {% endif -%} -{% if kvm_parameter_dict.get('use-tap', True) -%} +{% if str(kvm_parameter_dict.get('use-tap', 'True')).lower() == 'true' -%} {{ ' ' }}tap-ipv4 {% do publish_dict.__setitem__('lan-' ~ instance_name, '${' ~ section ~ ':connection-tap-ipv4}') -%} @@ -83,7 +86,7 @@ return = {% endif -%} {% do publish_dict.__setitem__(instance_name ~ '-backend-url', '${' ~ section ~ ':connection-backend-url}') -%} {% do publish_dict.__setitem__(instance_name ~ '-url', '${' ~ section ~ ':connection-url}') -%} -{% do kvm_instance_dict.__setitem__(instance_name, (kvm_parameter_dict.get('use-nat', True), nat_rules_list)) -%} +{% do kvm_instance_dict.__setitem__(instance_name, (use_nat, nat_rules_list)) -%} {% endfor %} @@ -132,6 +135,11 @@ sla-instance_guid = {{ slave_frontend_iguid }} {% endfor %} # Enable simple http server on ipv6 so all VMs will access it +[hash-code] +recipe = slapos.cookbook:generate.password +storage-path = ${directory:etc}/code +bytes = 24 + [directory] recipe = slapos.cookbook:mkdirectory etc = ${buildout:directory}/etc @@ -141,35 +149,47 @@ var = ${buildout:directory}/var log = ${:var}/log scripts = ${:etc}/run services = ${:etc}/service -document = ${:srv}/document +webroot = ${:srv}/document +promises = ${:etc}/promise ssl = ${:etc}/ssl -[http-ssl] -recipe = plone.recipe.command -command = "{{ openssl_executable_location }}" req -newkey rsa -batch -new -x509 -days 3650 -nodes -keyout "${:key}" -out "${:cert}" -key = ${directory:ssl}/key -cert = ${directory:ssl}/cert -update-command = -stop-on-error = true - -[http-server] -recipe = slapos.cookbook:simplehttpserver -host = {{ ipv6 }} +[directory-doc] +recipe = slapos.cookbook:mkdirectory +document = ${directory:webroot}/${hash-code:passwd} + +[apache-conf] +denied-root-access = true +root = ${directory:webroot}/ +index = ${directory:webroot}/${hash-code:passwd} port = 9002 -base-path = ${directory:document} -wrapper = ${directory:services}/simple-http-server -log-file = ${directory:log}/http.log -cert-file = ${http-ssl:cert} -key-file = ${http-ssl:key} +{% if len(kvm_hostname_list) -%} +{% do part_list.append('write-vm-hostname') -%} [write-vm-hostname] recipe = slapos.recipe.template:jinja2 template = {{ template_content }} filename = hosts -rendered = ${http-server:root-dir}/${:filename} +rendered = ${directory:webroot}/${hash-code:passwd}/${:filename} context = raw content_list {{ kvm_hostname_list | join('#') }} raw sep # +{% endif -%} + +{% macro writefile(section_name, file_path, content, mode='') -%} +{% do part_list.append(section_name) -%} +{% set data_list = content.split('\n') -%} +[{{ section_name }}] +recipe = collective.recipe.template +input = inline: + {{ data_list | join('\n ') }} +output = {{ file_path }} +mode = {{ mode }} +{% endmacro -%} + +# write cluster-data into file public/data +{% if slapparameter_dict.get('cluster-data', '') -%} +{{ writefile('cluster-data-content', '${http-server:root-dir}/data', slapparameter_dict.get('cluster-data', ''), '700') }} +{% endif -%} [publish] recipe = slapos.cookbook:publish @@ -177,10 +197,14 @@ recipe = slapos.cookbook:publish {{ name }} = {{ value }} {% endfor %} [buildout] +extends = + {{ template_httpd_cfg }} + parts = - http-server - write-vm-hostname + httpd + httpd-promise publish + directory-doc # Complete parts with sections {{ part_list | join('\n ') }} diff --git a/software/kvm/instance-kvm-http.cfg.in b/software/kvm/instance-kvm-http.cfg.in index 6c295db7047a48acaf7cdbee4aa0fc762de46e78..5261c3c160ca4a10d0727d8073919e5d4a09f89d 100644 --- a/software/kvm/instance-kvm-http.cfg.in +++ b/software/kvm/instance-kvm-http.cfg.in @@ -14,33 +14,48 @@ log = ${:var}/log services = ${:etc}/service promises = ${:etc}/promise run = ${:var}/run +document = ${:srv}/document +ssl = ${:etc}/ssl [apache-conf] recipe = slapos.recipe.template:jinja2 template = {{ template_apache_conf }} rendered = ${directory:etc}/apache.conf -#ipv6 = ${slap-network-information:global-ipv6} -ipv4 = ${slap-network-information:local-ipv4} +ip = ${slap-network-information:global-ipv6} +#ipv4 = ${slap-network-information:local-ipv4} port = ${slap-parameter:httpd-port} error-log = ${directory:log}/apache-error.log access-log = ${directory:log}/apache-access.log pid-file = ${directory:run}/apache.pid index = ${directory:public} +root = {:index} +denied-root-access = false context = key port :port - key ip :ipv4 + key ip :ip key access_log :access-log key error_log :error-log key pid_file :pid-file key index_folder :index + key cert httpd-ssl:cert + key key httpd-ssl:key + key document_root :root [httpd] recipe = slapos.cookbook:wrapper wrapper-path = ${directory:services}/httpd command-line = "{{ apache_location }}/bin/httpd" -f "${apache-conf:rendered}" -DFOREGROUND +[httpd-ssl] +recipe = plone.recipe.command +command = "{{ openssl_executable_location }}" req -newkey rsa -batch -new -x509 -days 3650 -nodes -keyout "${:key}" -out "${:cert}" +key = ${directory:ssl}/key +cert = ${directory:ssl}/cert +update-command = +stop-on-error = true + [httpd-promise] recipe = slapos.cookbook:check_port_listening path = ${directory:promises}/apache-httpd -hostname = ${apache-conf:ipv4} +hostname = ${apache-conf:ip} port = ${apache-conf:port} \ No newline at end of file diff --git a/software/kvm/instance-kvm-input-schema.json b/software/kvm/instance-kvm-input-schema.json index 90c257e3e3bca65ba549cdb80d33d2e212cd237c..abc78e17ec5d930cd1f9ed8b4204882187b47cc1 100644 --- a/software/kvm/instance-kvm-input-schema.json +++ b/software/kvm/instance-kvm-input-schema.json @@ -115,7 +115,7 @@ "description": "Type of QEMU disk drive, to create.", "type": "string", "default": "qcow2", - "enum": ["qcow2", "raw", "vdi", "vmdk", "cloop"] + "enum": ["qcow2", "raw", "vdi", "vmdk", "cloop", "qed"] }, "use-tap": { @@ -135,9 +135,28 @@ "description": "List of rules for NAT of QEMU user mode network stack, as comma-separated list of ports. For each port specified, it will redirect port x of the VM (example: 80) to the port x + 10000 of the public IPv6 (example: 10080). Defaults to \"22 80 443\". Ignored if \"use-tap\" parameter is enabled.", "type": "string" }, + "enable-http-server": { + "title": "Enable local http server", + "description": "Set if local http server which serve files to the vm should be deployed. If set to true, get file into the vm with URL: http://10.0.2.100/FILE.", + "type": "boolean", + "default": false + }, + "httpd-port": { + "title": "Local http server port", + "description": "Port of the local http server used to share files.", + "type": "integer", + "default": 8081, + "minimum": 1, + "maximum": 65535 + }, "authorized-key": { "title": "Public keys to get from all virtual machines.", - "description": "Set the public keys to add in your virtual machine. Keys are separated with '##'. The public key file will be available in the VM via url http://10.0.2.100/authorized_keys if you keep the NAT interface enabled", + "description": "Set the public keys to add in your virtual machine. The public key file will be available in the VM via url http://10.0.2.100/authorized_keys if you keep the NAT interface enabled", + "type": "string" + }, + "data-to-vm": { + "title": "Text content to send to this virtual machine.", + "description": "Text content which will be written in a file 'data' of http server of this virtual machine instance. The file will be available via URL: http://10.0.2.100/data in the VM.", "type": "string" }, "frontend-instance-guid": { diff --git a/software/kvm/instance-kvm.cfg.jinja2 b/software/kvm/instance-kvm.cfg.jinja2 index 3cc361b49dab9f67fc3577af22a0f73b8b316b01..f8bd5602995d46effe5267065ebf77f91afeff45 100644 --- a/software/kvm/instance-kvm.cfg.jinja2 +++ b/software/kvm/instance-kvm.cfg.jinja2 @@ -1,4 +1,6 @@ -{% set enable_http = slapparameter_dict.get('enable-http-server', 'False') -%} +{% set enable_http = slapparameter_dict.get('enable-http-server', 'False').lower() -%} +{% set use_tap = slapparameter_dict.get('use-tap', 'False').lower() -%} +{% set use_nat = slapparameter_dict.get('use-nat', 'True').lower() -%} ############################# # # Instanciate kvm @@ -19,13 +21,19 @@ parts = {% if slapparameter_dict.get('document-host', '') %} cluster-url-path {% endif -%} -{% if slapparameter_dict.get('enable-http-server', 'False') == 'True' %} +{% if enable_http == 'true' %} httpd httpd-promise publish-host-config +{% if slapparameter_dict.get('data-to-vm', '') %} + vm-data-content +{% endif -%} +{% if slapparameter_dict.get('authorized-key', '') %} + get-authorized-key +{% endif -%} -extends = - {{ template_httpd_cfg }} +# extends = +# {{ template_httpd_cfg }} {% endif -%} eggs-directory = {{ eggs_directory }} @@ -127,7 +135,7 @@ external-disk-number = ${slap-parameter:external-disk-number} external-disk-size = ${slap-parameter:external-disk-size} external-disk-format = ${slap-parameter:external-disk-format} -{% if slapparameter_dict.get('enable-http-server', 'False') == 'True' or ( slapparameter_dict.get('use-tap', 'False') == 'True' and tap_network_dict.has_key('ipv4') ) -%} +{% if enable_http == 'true' or ( use_tap == 'true' and tap_network_dict.has_key('ipv4') ) -%} httpd-port = ${slap-parameter:httpd-port} {% else -%} httpd-port = 0 @@ -264,6 +272,23 @@ dash_path = {{ dash_executable_location }} curl_path = {{ curl_executable_location }} +{% if enable_http == 'true' %} +[httpd] +recipe = slapos.cookbook:simplehttpserver +host = ${slap-network-information:local-ipv4} +port = ${slap-parameter:httpd-port} +base-path = ${directory:public} +wrapper = ${directory:services}/http-server +log-file = ${directory:log}/httpd.log +use-hash-url = false + +[httpd-promise] +recipe = slapos.cookbook:check_port_listening +path = ${directory:promises}/httpd +hostname = ${httpd:host} +port = ${httpd:port} +{% endif %} + [publish-connection-information] recipe = slapos.cookbook:publish ipv6 = ${slap-network-information:global-ipv6} @@ -272,7 +297,7 @@ url = ${request-slave-frontend:connection-url}/vnc_auto.html?host=${request-slav {% set disk_number = len(storage_dict) -%} maximum-extra-disk-amount = {{ disk_number }} {% set iface = 'eth0' -%} -{% if slapparameter_dict.get('use-nat', 'True') == 'True' -%} +{% if use_nat == 'true' -%} {% set iface = 'eth1' -%} # Publish NAT port mapping status # XXX: hardcoded value from [slap-parameter] @@ -285,27 +310,27 @@ nat-rule-url-{{port}} = [${slap-network-information:global-ipv6}]:{{external_por {% endif -%} {% endfor -%} {% endif -%} -{% if slapparameter_dict.get('use-tap', 'False') == 'True' -%} +{% if use_tap == 'true' -%} tap-ipv4 = ${slap-network-information:tap-ipv4} {% endif -%} {% set kvm_http = 'http://${slap-network-information:local-ipv4}:' ~ slapparameter_dict.get('httpd-port', 8081) -%} {% if enable_http == 'True' %} -{% if slapparameter_dict.get('use-nat', 'True') == 'True' -%} +{% if use_nat == 'true' -%} {% set kvm_http = 'http://10.0.2.100' -%} {% endif %} {% if slapparameter_dict.get('authorized-key', '') -%} -7_info = Get the publick key file in your VM with the command: wget {{ kvm_http }}/${get-authorized-key:filename} +7_info = Get the publick key file in your VM with the command: wget {{ kvm_http }}/authorized_keys {% endif %} {% endif %} -{% if slapparameter_dict.get('use-tap', 'False') == 'True' and tap_network_dict.has_key('ipv4') -%} +{% if use_tap == 'true' and tap_network_dict.has_key('ipv4') -%} 1_info = Use these configurations below to configure interface {{ iface }} in your VM. 2_info = ${network-config:ifconfig} 3_info = ${network-config:route-iface} 4_info = ${network-config:route-network} 5_info = ${network-config:route-default} -{% if enable_http == 'True' %} +{% if enable_http == 'true' %} 6_info = Or run in your VM the command: wget -O- {{ kvm_http }}/netconfig.sh | /bin/sh - {% endif %} @@ -332,20 +357,26 @@ command = update-command = ${:command} {% endif -%} -[get-authorized-key] -recipe = slapos.recipe.template:jinja2 -template = {{ template_content }} -filename = authorized_keys -rendered = ${directory:public}/${:filename} -public-key = {{ slapparameter_dict.get('authorized-key', '') }} -context = - key content_list :public-key - raw sep ## +{% macro writefile(section_name, file_path, content, mode='') -%} +{% set data_list = content.split('\n') -%} +[{{ section_name }}] +recipe = collective.recipe.template +input = inline: + {{ data_list | join('\n ') }} +output = {{ file_path }} +mode = {{ mode }} +{% endmacro -%} + +# write vm-data into file public/data +{{ writefile('vm-data-content', '${directory:public}/data', slapparameter_dict.get('data-to-vm', ''), '700') }} + +# write public key for vms to public/authorized_keys +{{ writefile('get-authorized-key', '${directory:public}/authorized_keys', slapparameter_dict.get('authorized-key', ''), '700') }} [publish-host-config] recipe = plone.recipe.command name = {{ slapparameter_dict.get('name', 'localhost') }} -{% if slapparameter_dict.get('use-tap', 'False') == 'True' and tap_network_dict.has_key('ipv4') -%} +{% if use_tap == 'true' and tap_network_dict.has_key('ipv4') -%} local-ipv4 = ${slap-network-information:tap-ipv4} {% else -%} local-ipv4 = 127.0.0.1 @@ -419,3 +450,6 @@ enable-http-server = False httpd-port = 8081 # for auto config, the public key file will be available in the VM via url http://10.0.2.100/authorized_key if use-nat = True authorized-key = + +# send some content which will be accessible to the vm through static url: http://10.0.2.100/data +data-to-vm = diff --git a/software/kvm/instance.cfg.in b/software/kvm/instance.cfg.in index 742d7d4abd70f9998d243bbb542697307feca41e..9041021eb3603f307e68b2139e1b4d608b63f060 100644 --- a/software/kvm/instance.cfg.in +++ b/software/kvm/instance.cfg.in @@ -67,6 +67,7 @@ filename = template-kvm-cluster.cfg extra-context = section parameter_dict dynamic-template-kvm-cluster-parameters raw template_content ${template-content:location}/${template-content:filename} + raw template_httpd_cfg ${template-httpd:rendered} [dynamic-template-kvm] recipe = slapos.recipe.template:jinja2 diff --git a/software/kvm/template/apache.conf.in b/software/kvm/template/apache.conf.in index 1431b79c2ac901b27352f85c12bb860e66f929f0..4e3f144387ebb5d9f65ac32e6c8b0b8e3d4f4347 100644 --- a/software/kvm/template/apache.conf.in +++ b/software/kvm/template/apache.conf.in @@ -27,6 +27,17 @@ ServerTokens Prod ServerSignature Off TraceEnable Off +SSLEngine on + +SSLCertificateFile {{ cert }} +SSLCertificateKeyFile {{ key }} +SSLRandomSeed startup builtin +SSLRandomSeed connect builtin +SSLProtocol All -SSLv2 +SSLProxyEngine On + +DocumentRoot {{ document_root }} + ErrorLog "{{ error_log }}" # Default apache log format with request time in microsecond at the end LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined @@ -40,10 +51,11 @@ SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded Require all denied </Directory> -DocumentRoot {{ index_folder }} <Directory {{ index_folder }}> Options Indexes FollowSymLinks - Require ip {{ ip }} + # Require ip {{ ip }} # Require env forwarded '{{ ip }}' - Require all denied + # Require all denied + AllowOverride None + Require all granted </Directory> \ No newline at end of file diff --git a/software/kvm/template/template-content.in b/software/kvm/template/template-content.in index eb9efd6a248164653b022584414bd2ca79ea5a29..22557669c7a73e3d99933eb108e786a48bed0ee2 100644 --- a/software/kvm/template/template-content.in +++ b/software/kvm/template/template-content.in @@ -1,3 +1,7 @@ -{% for content in content_list.split(sep) -%} +{% if not sep -%} +{{ content_list }} +{% else -%} +{% for content in content_list.split(sep) -%} {{ content }} -{% endfor -%} \ No newline at end of file +{% endfor -%} +{% endif -%} \ No newline at end of file