pax_global_header 0000666 0000000 0000000 00000000064 12247113034 0014507 g ustar 00root root 0000000 0000000 52 comment=d29efb23e257cb64204ef0fcc7ddb6e727101740 slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/ 0000775 0000000 0000000 00000000000 12247113034 0024355 5 ustar 00root root 0000000 0000000 slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/ 0000775 0000000 0000000 00000000000 12247113034 0025656 5 ustar 00root root 0000000 0000000 slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/ 0000775 0000000 0000000 00000000000 12247113034 0027167 5 ustar 00root root 0000000 0000000 slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/__init__.py 0000775 0000000 0000000 00000011651 12247113034 0031307 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*- # vim: set et sts=2: # pylint: disable-msg=W0311,C0301,C0103,C0111,R0904,R0903 import ConfigParser import datetime import logging import logging.handlers from optparse import OptionParser, Option import os import slapos.runner.process import sys from slapos.runner.utils import (runInstanceWithLock, cloneDefaultGit, setupDefaultSR) class Parser(OptionParser): """ Parse all arguments. """ def __init__(self, usage=None, version=None): """ Initialize all possible options. """ option_list = [ Option("-l", "--log_file", help="The path to the log file used by the script.", type=str), Option("-v", "--verbose", default=False, action="store_true", help="Verbose output."), Option("-c", "--console", default=False, action="store_true", help="Console output."), Option("-d", "--debug", default=False, action="store_true", help="Debug mode."), ] OptionParser.__init__(self, usage=usage, version=version, option_list=option_list) def check_args(self): """ Check arguments """ (options, args) = self.parse_args() if len(args) != 1: self.error("Incorrect number of arguments") return options, args[0] class Config: def __init__(self): self.configuration_file_path = None self.console = None self.log_file = None self.logger = None self.verbose = None def setConfig(self, option_dict, configuration_file_path): """ Set options given by parameters. """ self.configuration_file_path = os.path.abspath(configuration_file_path) # Set options parameters for option, value in option_dict.__dict__.items(): setattr(self, option, value) # Load configuration file configuration_parser = ConfigParser.SafeConfigParser() configuration_parser.read(configuration_file_path) # Merges the arguments and configuration for section in ("slaprunner", "slapos", "slapproxy", "slapformat", "sshkeys_authority", "gitclient", "cloud9_IDE"): configuration_dict = dict(configuration_parser.items(section)) for key in configuration_dict: if not getattr(self, key, None): setattr(self, key, configuration_dict[key]) # set up logging self.logger = logging.getLogger("slaprunner") self.logger.setLevel(logging.INFO) if self.console: self.logger.addHandler(logging.StreamHandler()) if self.log_file: if not os.path.isdir(os.path.dirname(self.log_file)): # fallback to console only if directory for logs does not exists and # continue to run raise ValueError('Please create directory %r to store %r log file' % ( os.path.dirname(self.log_file), self.log_file)) else: file_handler = logging.FileHandler(self.log_file) file_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) self.logger.addHandler(file_handler) self.logger.info('Configured logging to file %r' % self.log_file) self.logger.info("Started.") self.logger.info(os.environ['PATH']) if self.verbose: self.logger.setLevel(logging.DEBUG) self.logger.debug("Verbose mode enabled.") def run(): "Run default configuration." usage = "usage: %s [options] CONFIGURATION_FILE" % sys.argv[0] try: # Parse arguments config = Config() config.setConfig(*Parser(usage=usage).check_args()) if os.getuid() == 0: # avoid mistakes (mainly in development mode) raise Exception('Do not run SlapRunner as root.') serve(config) return_code = 0 except SystemExit as err: # Catch exception raise by optparse return_code = err sys.exit(return_code) def serve(config): from views import app from werkzeug.contrib.fixers import ProxyFix workdir = os.path.join(config.runner_workdir, 'project') software_link = os.path.join(config.runner_workdir, 'softwareLink') app.config.update(**config.__dict__) app.config.update( software_log=config.software_root.rstrip('/') + '.log', instance_log=config.instance_root.rstrip('/') + '.log', workspace=workdir, software_link=software_link, instance_profile='instance.cfg', software_profile='software.cfg', SECRET_KEY=os.urandom(24), PERMANENT_SESSION_LIFETIME=datetime.timedelta(days=31), ) if not os.path.exists(workdir): os.mkdir(workdir) if not os.path.exists(software_link): os.mkdir(software_link) slapos.runner.process.setHandler() config.logger.info('Running slapgrid...') runInstanceWithLock(app.config) cloneDefaultGit(app.config) setupDefaultSR(app.config) config.logger.info('Done.') app.wsgi_app = ProxyFix(app.wsgi_app) app.run(host=config.runner_host, port=int(config.runner_port), debug=config.debug, threaded=True) slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/decorators.py 0000664 0000000 0000000 00000000373 12247113034 0031711 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*- # vim: set et sts=2: #pylint: disable-all import json from flask import Response def as_json(f): def inner(*args, **kwargs): return Response(json.dumps(f(*args, **kwargs)), mimetype='application/json') return inner slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/fileBrowser.py 0000664 0000000 0000000 00000017773 12247113034 0032043 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*- # vim: set et sts=2: # pylint: disable-msg=W0311,C0301,C0103,C0111 import datetime import md5 import os import re import shutil import urllib import zipfile import werkzeug from slapos.runner.utils import realpath, tail, isText class FileBrowser(object): """This class contains all base functions for file browser""" def __init__(self, config): self.config = config def _realdir(self, dir): realdir = realpath(self.config, urllib.unquote(dir)) if not realdir: raise NameError('Could not load directory %s: Permission denied' % dir) return realdir def listDirs(self, dir, all=False): """List elements of directory 'dir' taken""" html = 'var gsdirs = [], gsfiles = [];' dir = urllib.unquote(dir) # XXX-Marco 'dir' and 'all' should not shadow builtin names realdir = realpath(self.config, dir) if not realdir: raise NameError('Could not load directory %s: Permission denied' % dir) ldir = sorted(os.listdir(realdir), key=str.lower) for f in ldir: if f.startswith('.') and not all: # do not display this file/folder continue ff = os.path.join(dir, f) realfile = os.path.join(realdir, f) mdate = datetime.datetime.fromtimestamp(os.path.getmtime(realfile) ).strftime("%Y-%d-%m %I:%M") md5sum = md5.md5(realfile).hexdigest() if not os.path.isdir(realfile): size = os.path.getsize(realfile) regex = re.compile("(^.*)\.(.*)", re.VERBOSE) ext = regex.sub(r'\2', f) if ext == f: ext = "unknow" else: ext = str.lower(ext) html += 'gsfiles.push(new gsItem("1", "%s", "%s", "%s", "%s", "%s", "%s"));' % (f, ff, size, md5sum, ext, mdate) else: html += 'gsdirs.push(new gsItem("2", "%s", "%s", "0", "%s", "dir", "%s"));' % (f, ff, md5sum, mdate) return html def makeDirectory(self, dir, filename): """Create a directory""" realdir = self._realdir(dir) folder = os.path.join(realdir, filename) if not os.path.exists(folder): os.mkdir(folder, 0744) return "{result: '1'}" else: return "{result: '0'}" def makeFile(self, dir, filename): """Create a file in a directory dir taken""" realdir = self._realdir(dir) fout = os.path.join(realdir, filename) if not os.path.exists(fout): open(fout, 'w') return "var responce = {result: '1'}" else: return "{result: '0'}" def deleteItem(self, dir, files): """Delete a list of files or directories""" # XXX-Marco do not shadow 'dir' realdir = self._realdir(dir) lfiles = urllib.unquote(files).split(',,,') try: # XXX-Marco do not shadow 'file' for file in lfiles: file = os.path.join(realdir, file) if not os.path.exists(file): continue # silent skip file.... details = file.split('/') last = details[-1] if last and last.startswith('.'): continue # cannot delete this file/directory, to prevent security if os.path.isdir(file): shutil.rmtree(file) else: os.unlink(file) except Exception as e: return str(e) return "{result: '1'}" def copyItem(self, dir, files, del_source=False): """Copy a list of files or directory to dir""" realdir = self._realdir(dir) lfiles = urllib.unquote(files).split(',,,') try: # XXX-Marco do not shadow 'file' for file in lfiles: realfile = realpath(self.config, file) if not realfile: raise NameError('Could not load file or directory %s: Permission denied' % file) #prepare destination file details = realfile.split('/') dest = os.path.join(realdir, details[-1]) if os.path.exists(dest): raise NameError('NOT ALLOWED OPERATION : File or directory already exists') if os.path.isdir(realfile): shutil.copytree(realfile, dest) if del_source: shutil.rmtree(realfile) else: shutil.copy(realfile, dest) if del_source: os.unlink(realfile) except Exception as e: return str(e) return "{result: '1'}" def rename(self, dir, filename, newfilename): """Rename file or directory to dir/filename""" realdir = self._realdir(dir) realfile = realpath(self.config, urllib.unquote(filename)) if not realfile: raise NameError('Could not load directory %s: Permission denied' % filename) tofile = os.path.join(realdir, newfilename) if not os.path.exists(tofile): os.rename(realfile, tofile) return "{result: '1'}" raise NameError('NOT ALLOWED OPERATION : File or directory already exists') def copyAsFile(self, dir, filename, newfilename): """Copy file or directory to dir/filename""" realdir = self._realdir(dir) fromfile = os.path.join(realdir, filename) tofile = os.path.join(realdir, newfilename) if not os.path.exists(fromfile): raise NameError('NOT ALLOWED OPERATION : File or directory does not exist') if not os.path.exists(tofile): shutil.copy(fromfile, tofile) return "{result: '1'}" raise NameError('NOT ALLOWED OPERATION : File or directory already exists') def uploadFile(self, dir, files): """Upload a list of files in directory dir""" realdir = self._realdir(dir) for file in files: if files[file]: filename = werkzeug.secure_filename(files[file].filename) if not os.path.exists(os.path.join(dir, filename)): files[file].save(os.path.join(realdir, filename)) return "{result: '1'}" def downloadFile(self, dir, filename): """Download file dir/filename""" realdir = self._realdir(dir) file = os.path.join(realdir, urllib.unquote(filename)) if not os.path.exists(file): raise NameError('NOT ALLOWED OPERATION : File or directory does not exist %s' % os.path.join(dir, filename)) return file def zipFile(self, dir, filename, newfilename): """Add filename to archive as newfilename""" realdir = self._realdir(dir) tozip = os.path.join(realdir, newfilename) fromzip = os.path.join(realdir, filename) if not os.path.exists(fromzip): raise NameError('NOT ALLOWED OPERATION : File or directory does not exist') if not os.path.exists(tozip): zip = zipfile.ZipFile(tozip, 'w', zipfile.ZIP_DEFLATED) if os.path.isdir(fromzip): rootlen = len(fromzip) + 1 for base, _, files in os.walk(fromzip): for filename in files: fn = os.path.join(base, filename).encode("utf-8") zip.write(fn, fn[rootlen:]) # XXX can fail if 'fromzip' contains multibyte characters else: zip.write(fromzip) zip.close() return "{result: '1'}" raise NameError('NOT ALLOWED OPERATION : File or directory already exists') def unzipFile(self, dir, filename, newfilename): """Extract a zipped archive""" realdir = self._realdir(dir) target = os.path.join(realdir, newfilename) archive = os.path.join(realdir, filename) if not os.path.exists(archive): raise NameError('NOT ALLOWED OPERATION : File or directory does not exist') if not os.path.exists(target): zip = zipfile.ZipFile(archive) #member = zip.namelist() zip.extractall(target) #if len(member) > 1: # zip.extractall(target) #else: # zip.extract(member[0], newfilename) return "{result: '1'}" raise NameError('NOT ALLOWED OPERATION : File or directory already exists') def readFile(self, dir, filename, truncate=False): """Read file dir/filename and return content""" realfile = realpath(self.config, os.path.join(urllib.unquote(dir), urllib.unquote(filename))) if not realfile: raise NameError('Could not load directory %s: Permission denied' % dir) if not isText(realfile): return "FILE ERROR: Cannot display binary file, please open a text file only!" if not truncate: return open(realfile).read() else: return tail(open(realfile), 0) slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/gittools.py 0000664 0000000 0000000 00000010746 12247113034 0031415 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*- # vim: set et sts=2: # pylint: disable-msg=W0311,C0301,C0103,C0111 import os import re import shutil from git import Repo from flask import jsonify def cloneRepo(data): """Clone a repository Args: data: a dictionary of parameters to use: data['path'] is the path of the new project data['repo'] is the url of the repository to be cloned data['email'] is the user's email data['user'] is the name of the user Returns: a jsonify data""" workDir = data['path'] if not workDir: return jsonify(code=0, result="Can not create project folder.") code = 0 json = "" try: if os.path.exists(workDir) and len(os.listdir(workDir)) < 2: shutil.rmtree(workDir) # delete useless files repo = Repo.clone_from(data["repo"], workDir) config_writer = repo.config_writer() config_writer.add_section("user") if data["user"] != "": config_writer.set_value("user", "name", data["user"].encode("utf-8")) if data["email"] != "": config_writer.set_value("user", "email", data["email"]) code = 1 except Exception as e: json = safeResult(str(e)) return jsonify(code=code, result=json) def gitStatus(project): """Run git status and return status of specified project folder Args: project: path of the projet to get status Returns: a parsed string that contains the result of git status""" code = 0 json = "" try: repo = Repo(project) git = repo.git json = git.status().replace('#', '') branch = git.branch().replace(' ', '').split('\n') isdirty = repo.is_dirty(untracked_files=True) code = 1 except Exception as e: json = safeResult(str(e)) return jsonify(code=code, result=json, branch=branch, dirty=isdirty) def switchBranch(project, name): """Switch a git branch Args: project: directory of the local git repository name: switch from current branch to `name` branch Returns: a jsonify data""" code = 0 json = "" try: repo = Repo(project) current_branch = repo.active_branch.name if name == current_branch: json = "This is already your active branch for this project" else: git = repo.git git.checkout(name) code = 1 except Exception as e: json = safeResult(str(e)) return jsonify(code=code, result=json) def addBranch(project, name, onlyCheckout=False): """Add new git branch to the repository Args: project: directory of the local git repository name: name of the new branch onlyCheckout: if True then the branch `name` is created before checkout Returns: a jsonify data""" code = 0 json = "" try: repo = Repo(project) git = repo.git if not onlyCheckout: git.checkout('-b', name) else: git.checkout(name) code = 1 except Exception as e: json = safeResult(str(e)) return jsonify(code=code, result=json) def getDiff(project): """Get git diff for the specified project directory""" result = "" try: repo = Repo(project) git = repo.git current_branch = repo.active_branch.name result = git.diff(current_branch) except Exception as e: result = safeResult(str(e)) return result def gitCommit(project, msg): """Commit changes for the specified repository Args: project: directory of the local repository msg: commit message""" code = 0 json = "" repo = Repo(project) if repo.is_dirty: git = repo.git #add file to be commited files = repo.untracked_files for f in files: git.add(f) #Commit all modified and untracked files git.commit('-a', '-m', msg) else: code = 1 json = "Nothing to be commited" return jsonify(code=code, result=json) def gitPush(project): """Push changes for the specified repository Args: project: directory of the local repository msg: commit message""" code = 0 json = "" try: #push changes to repo current_branch = repo.active_branch.name git.push('origin', current_branch) code = 1 except Exception as e: json = safeResult(str(e)) return jsonify(code=code, result=json) def gitPull(project): result = "" code = 0 try: repo = Repo(project) git = repo.git git.pull() code = 1 except Exception as e: result = safeResult(str(e)) return jsonify(code=code, result=result) def safeResult(result): """Parse string and remove credential of the user""" regex = re.compile("(https:\/\/)([\w\d\._-]+:[\w\d\._-]+)\@([\S]+\s)", re.VERBOSE) return regex.sub(r'\1\3', result) slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/process.py 0000664 0000000 0000000 00000004574 12247113034 0031231 0 ustar 00root root 0000000 0000000 import os import psutil import signal import subprocess import sys SLAPRUNNER_PROCESS_LIST = [] class Popen(subprocess.Popen): """ Extension of Popen to launch and kill processes in a clean way """ def __init__(self, *args, **kwargs): """ Launch process and add object to process list for handler """ self.name = kwargs.pop('name', None) kwargs['stdin'] = subprocess.PIPE kwargs['stderr'] = subprocess.STDOUT kwargs.setdefault('stdout', subprocess.PIPE) kwargs.setdefault('close_fds', True) subprocess.Popen.__init__(self, *args, **kwargs) SLAPRUNNER_PROCESS_LIST.append(self) self.stdin.flush() self.stdin.close() self.stdin = None def kill(self, sig=signal.SIGTERM, recursive=False): """ Kill process and all its descendants if recursive """ if self.poll() is None: if recursive: childs_pids = pidppid(self.pid) for pid in childs_pids: killNoFail(pid, sig) killNoFail(self.pid, sig) if self.poll() is not None: SLAPRUNNER_PROCESS_LIST.remove(self) def __del__(self): """ Del function, try to kill process group and process if its group does not exist """ for pid in (-self.pid, self.pid): try: os.kill(-self.pid, 15) except OSError: pass subprocess.Popen.__del__(self) def pidppid(pid, recursive=True): """ Return a list of all children of pid """ return [p.pid for p in psutil.Process(pid).get_children(recursive=recursive)] def killNoFail(pid, sig): """ function to kill without failing. Return True if kill do not fail """ try: os.kill(pid, sig) return True except OSError: return False def isRunning(name): """ Return True if a process with this name is running """ for process in SLAPRUNNER_PROCESS_LIST: if process.name == name: if process.poll() is None: return True return False def killRunningProcess(name, recursive=False): """ Kill all processes with a given name """ for process in SLAPRUNNER_PROCESS_LIST: if process.name == name: process.kill(recursive=recursive) def handler(sig, frame): """ Signal handler to kill all processes """ pid = os.getpid() os.kill(-pid, sig) sys.exit() def setHandler(sig_list=None): if sig_list is None: sig_list = [signal.SIGTERM] for sig in sig_list: signal.signal(sig, handler) slapos.toolbox-d29efb23e257cb64204ef0fcc7ddb6e727101740-slapos-runner/slapos/runner/runnertest.py 0000664 0000000 0000000 00000045747 12247113034 0031773 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*- # vim: set et sts=2: # pylint: disable-msg=W0311,C0301,C0103,C0111,R0904 import argparse import ConfigParser import datetime import hashlib import json import os import shutil import time import unittest from slapos.runner.utils import (getProfilePath, getSession, isInstanceRunning, isSoftwareRunning, startProxy) from slapos.runner.process import killRunningProcess, isRunning from slapos.runner import views import slapos.slap #Helpers def loadJson(response): return json.loads(response.data) class Config: def __init__(self): self.runner_workdir = None self.software_root = None self.instance_root = None self.configuration_file_path = None def setConfig(self): """ Set options given by parameters. """ self.configuration_file_path = os.path.abspath(os.environ.get('CONFIG_FILE_PATH')) # Load configuration file configuration_parser = ConfigParser.SafeConfigParser() configuration_parser.read(self.configuration_file_path) # Merges the arguments and configuration for section in ("slaprunner", "slapos", "slapproxy", "slapformat", "sshkeys_authority", "gitclient", "cloud9_IDE"): configuration_dict = dict(configuration_parser.items(section)) for key in configuration_dict: if not getattr(self, key, None): setattr(self, key, configuration_dict[key]) class SlaprunnerTestCase(unittest.TestCase): def setUp(self): """Initialize slapos webrunner here""" views.app.config['TESTING'] = True self.users = ["slapuser", "slappwd", "slaprunner@nexedi.com", "SlapOS web runner"] self.updateUser = ["newslapuser", "newslappwd", "slaprunner@nexedi.com", "SlapOS web runner"] self.rcode = "41bf2657" self.repo = 'http://git.erp5.org/repos/slapos.git' self.software = "workspace/slapos/software/" # relative directory fo SR self.project = 'slapos' # Default project name self.template = 'template.cfg' self.partitionPrefix = 'slappart' self.slaposBuildout = "1.6.0-dev-SlapOS-010" #create slaprunner configuration config = Config() config.setConfig() workdir = os.path.join(config.runner_workdir, 'project') software_link = os.path.join(config.runner_workdir, 'softwareLink') views.app.config.update(**config.__dict__) #update or create all runner base directory to test_dir if not os.path.exists(workdir): os.mkdir(workdir) if not os.path.exists(software_link): os.mkdir(software_link) views.app.config.update( software_log=config.software_root.rstrip('/') + '.log', instance_log=config.instance_root.rstrip('/') + '.log', workspace=workdir, software_link=software_link, instance_profile='instance.cfg', software_profile='software.cfg', SECRET_KEY="123456", PERMANENT_SESSION_LIFETIME=datetime.timedelta(days=31), ) self.app = views.app.test_client() self.app.config = views.app.config #Create password recover code with open(os.path.join(views.app.config['etc_dir'], '.rcode'), 'w') as rpwd: rpwd.write(self.rcode) def tearDown(self): """Remove all test data""" os.unlink(os.path.join(self.app.config['etc_dir'], '.rcode')) project = os.path.join(self.app.config['etc_dir'], '.project') users = os.path.join(self.app.config['etc_dir'], '.users') if os.path.exists(users): os.unlink(users) if os.path.exists(project): os.unlink(project) if os.path.exists(self.app.config['workspace']): shutil.rmtree(self.app.config['workspace']) if os.path.exists(self.app.config['software_root']): shutil.rmtree(self.app.config['software_root']) if os.path.exists(self.app.config['instance_root']): shutil.rmtree(self.app.config['instance_root']) if os.path.exists(self.app.config['software_link']): shutil.rmtree(self.app.config['software_link']) self.logout() #Stop process killRunningProcess('slapproxy', recursive=True) killRunningProcess('slapgrid-cp', recursive=True) killRunningProcess('slapgrid-sr', recursive=True) def configAccount(self, username, password, email, name, rcode): """Helper for configAccount""" return self.app.post('/configAccount', data=dict( username=username, password=password, email=email, name=name, rcode=rcode ), follow_redirects=True) def login(self, username, password): """Helper for Login method""" return self.app.post('/doLogin', data=dict( clogin=username, cpwd=password ), follow_redirects=True) def setAccount(self): """Initialize user account and log user in""" response = loadJson(self.configAccount(self.users[0], self.users[1], self.users[2], self.users[3], self.rcode)) response2 = loadJson(self.login(self.users[0], self.users[1])) self.assertEqual(response['result'], "") self.assertEqual(response2['result'], "") def logout(self): """Helper for Logout current user""" return self.app.get('/dologout', follow_redirects=True) def updateAccount(self, newaccount, rcode): """Helper for update user account data""" return self.app.post('/updateAccount', data=dict( username=newaccount[0], password=newaccount[1], email=newaccount[2], name=newaccount[3], rcode=rcode ), follow_redirects=True) def getCurrentSR(self): return getProfilePath(self.app.config['etc_dir'], self.app.config['software_profile']) def proxyStatus(self, status=True, sleep_time=0): """Helper for testslapproxy status""" proxy = isRunning('slapproxy') if proxy != status and sleep_time != 0: time.sleep(sleep_time) proxy = isRunning('slapproxy') self.assertEqual(proxy, status) def setupProjectFolder(self, withSoftware=False): """Helper to create a project folder as for slapos.git""" base = os.path.join(self.app.config['workspace'], 'slapos') software = os.path.join(base, 'software') os.mkdir(base) os.mkdir(software) if withSoftware: testSoftware = os.path.join(software, 'slaprunner-test') sr = "[buildout]\n\n" sr += "parts = command\n\nunzip = true\nnetworkcache-section = networkcache\n\n" sr += "find-links += http://www.nexedi.org/static/packages/source/slapos.buildout/\n\n" sr += "[networkcache]\ndownload-cache-url = http://www.shacache.org/shacache" sr += "\ndownload-dir-url = http://www.shacache.org/shadir\n\n" sr += "[command]\nrecipe = zc.recipe.egg\neggs = plone.recipe.command\n\n" sr += "[versions]\nzc.buildout = %s\n" % self.slaposBuildout os.mkdir(testSoftware) open(os.path.join(testSoftware, self.app.config['software_profile']), 'w').write(sr) def setupSoftwareFolder(self): """Helper to setup compiled software release dir""" self.setupProjectFolder(withSoftware=True) md5 = hashlib.md5(os.path.join(self.app.config['workspace'], "slapos/software/slaprunner-test", self.app.config['software_profile']) ).hexdigest() base = os.path.join(self.app.config['software_root'], md5) template = os.path.join(base, self.template) content = "[buildout]\n" content += "parts = \n create-file\n\n" content += "eggs-directory = %s\n" % os.path.join(base, 'eggs') content += "develop-eggs-directory = %s\n\n" % os.path.join(base, 'develop-eggs') content += "[create-file]\nrecipe = plone.recipe.command\n" content += "filename = ${buildout:directory}/etc\n" content += "command = mkdir ${:filename} && echo 'simple file' > ${:filename}/testfile\n" os.mkdir(self.app.config['software_root']) os.mkdir(base) open(template, "w").write(content) def stopSlapproxy(self): """Kill slapproxy process""" killRunningProcess('slapproxy', recursive=True) #Begin test case here def test_wrong_login(self): """Test Login user before create session. This should return an error value""" response = self.login(self.users[0], self.users[1]) #redirect to config account page assert "