Example #1
0
	def _packageFolder(tfile, relbasepath, docrootbasepath):
		getLogger().debug("Walking folder %s..." % docrootbasepath)
		for entry in FileSystemManager.instance().getdir(docrootbasepath):
			name, apptype = entry['name'], entry['type']
			if apptype == FileSystemManager.APPTYPE_DIR:
				relpath = "%s%s/" % (relbasepath, name) # the name of the current item within the package
				docrootpath = "%s%s/" % (docrootbasepath, name) # the name of the current item within the docroot
				getLogger().debug("Adding directory %s..." % relpath)
				tarinfo = tarfile.TarInfo(relpath)
				tarinfo.type = tarfile.DIRTYPE
				tarinfo.mode = 0755
				tarinfo.uid = os.getuid()
				tarinfo.gid = os.getgid()
				tarinfo.mtime = time.time()
				tfile.addfile(tarinfo)
				_packageFolder(tfile, relbasepath = relpath, docrootbasepath = docrootpath)
			else:
				relname = "%s%s" % (relbasepath, name) # the name of the current item within the package
				docrootname = "%s%s" % (docrootbasepath, name) # the name of the current item within the docroot
				getLogger().debug("Adding file %s..." % relname)
				tarinfo = tarfile.TarInfo(relname)
				tarinfo.type = tarfile.AREGTYPE
				tarinfo.mode = 0644
				tarinfo.uid = os.getuid()
				tarinfo.gid = os.getgid()
				tarinfo.mtime = time.time()
				content = FileSystemManager.instance().read(docrootname)
				tarinfo.size = len(content)
				contentObj = StringIO.StringIO(content)
				tfile.addfile(tarinfo, contentObj)
				contentObj.close()
				getLogger().debug("File %s added to package file (%s bytes)" % (relname, tarinfo.size))
Example #2
0
def schedulePackage(path,
                    username,
                    session,
                    at,
                    script=None,
                    profileName=None):
    """
	Schedules a package to start at <at>.
	
	If script is provided, the package's default-script attribute is ignored
	and script is used instead.
	
	@since: 1.3
	"""
    getLogger().info(">> schedulePackage(%s, script = %s)" % (path, script))
    if not path.startswith('/'): path = '/' + path

    try:
        metadata = Package.getPackageMetadata(path)

        if not script:
            script = metadata['default-script']

        scriptFilename = "%s/src/%s" % (path, script)
        scriptPath = scriptFilename

        label = "%s:%s" % ('/'.join(path.split('/')[2:]), script)

        getLogger().info("Using package script filename %s, path %s" %
                         (scriptFilename, scriptPath))

        if script.endswith(".campaign"):
            res = scheduleCampaign(FileSystemManager.instance().read(
                scriptFilename).decode('utf-8'),
                                   label,
                                   username,
                                   session,
                                   at=at,
                                   path=scriptPath)
        elif script.endswith(".ats"):
            res = scheduleAts(FileSystemManager.instance().read(
                scriptFilename).decode('utf-8'),
                              label,
                              username,
                              session,
                              at=at,
                              path=scriptPath)
        else:
            raise Exception(
                "Invalid script/default script for package execution (unrecognized job type based on extension - %s)"
                % script)

    except Exception as e:
        e = Exception("Scheduling error: %s" % (str(e)))
        getLogger().info("<< schedulePackage(...): Fault:\n%s" %
                         Tools.getBacktrace())
        raise (e)

    getLogger().info("<< schedulePackage(...): %s" % str(res))
    return res
Example #3
0
def removeDirectory(path, recursive=False):
    """
	Removes an empty directory, unless recursive is set to True.
	
	@since: 1.1

	@type  path: string
	@param path: the docroot-path to the directory to delete
	@type  recursive: bool
	@param recursive: True if we should delete files and directories in it. DANGEROUS.
	
	@rtype: bool
	@returns: True if OK, False if nothing deleted. (? to check)
	"""
    getLogger().info(">> removeDirectory(%s)" % path)
    if not path.startswith('/'): path = '/' + path

    res = False
    try:
        res = FileSystemManager.instance().rmdir(path, recursive)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< removeDirectory(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< removeDirectory(): %s" % str(res))
    return res
Example #4
0
def getDirectoryListing(path):
    """
	Returns the contents of a directory.
	Also filters some 'internal' files (in particular __init__.py files)
	
	@since: 1.0

	@type  path: string
	@param path: the path of the directory within the docroot
	
	@rtype: list of dict{'name': string, 'type': string in [ ats, campaign, module, log, directory, package, ... ] }
	@returns: the dir contents, with a name (with extension) relative to the dir,
	          and an associated application type.
	          Returns None if the directory was not accessible or in case of an error.
	"""
    getLogger().info(">> getDirectoryListing(%s)" % path)
    if not path.startswith('/'):
        path = '/' + path

    res = []
    try:
        res = FileSystemManager.instance().getdir(path)
        if res is None:
            raise Exception("Unable to get directory contents through backend")

        # Sort the entries, so that it is useless to implement it in all clients ?
        res.sort(key=operator.itemgetter('name'))
    except Exception, e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< getDirectoryListing(...): Fault:\n%s" % str(e))
        # Well, actually, we do not return a fault in this case...
        res = None
Example #5
0
def rename(path, newName):
    """
	Renames a file or a directory to newName, in the same folder.
	
	@since: 1.3

	@type  path: string
	@param path: the docroot-path to the object to rename
	@type  newName: string
	@param newName: the new name (basename) of the object, including extension,
	                if applicable.
	
	@rtype: bool
	@returns: False if newName already exists. True otherwise.
	"""
    getLogger().info(">> rename(%s, %s)" % (path, newName))
    if not path.startswith('/'): path = '/' + path

    res = False
    try:
        res = FileSystemManager.instance().rename(path, newName)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< rename(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< rename(): %s" % str(res))
    return res
Example #6
0
def getDirectoryListing(path):
	"""
	Returns the contents of a directory.
	Also filters some 'internal' files (in particular __init__.py files)
	
	@since: 1.0

	@type  path: string
	@param path: the path of the directory within the docroot
	
	@rtype: list of dict{'name': string, 'type': string in [ ats, campaign, module, log, directory, package, ... ] }
	@returns: the dir contents, with a name (with extension) relative to the dir,
	          and an associated application type.
	          Returns None if the directory was not accessible or in case of an error.
	"""
	getLogger().info(">> getDirectoryListing(%s)" % path)
	if not path.startswith('/'):
		path = '/' + path

	res = []
	try:
		res = FileSystemManager.instance().getdir(path)
		if res is None:
			raise Exception("Unable to get directory contents through backend")

		# Sort the entries, so that it is useless to implement it in all clients ?
		res.sort(key = operator.itemgetter('name'))
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< getDirectoryListing(...): Fault:\n%s" % str(e))
		# Well, actually, we do not return a fault in this case...
		res = None
Example #7
0
def getFileInfo(path):
	"""
	Gets some info about a file.
	
	Returns a dict{ 'size': integer, 'timestamp': float }
	where the size is optional (in bytes, if provided), and timestamp is
	the file modification time.

	@since: 1.0

	@type  path: string
	@param path: the path to the file whose info we want to get
	
	@rtype: a dict, or None
	@returns: None on error, or the dict of attributes.
	"""
	getLogger().info(">> getFileInfo(%s)" % path)
	if not path.startswith('/'):
		path = '/' + path

	res = None
	try:
		attributes = FileSystemManager.instance().attributes(path)
		if attributes:
			res = {}
			if attributes.size is not None:
				res['size'] = attributes.size
			if attributes.mtime is not None:
				res['timestamp'] = attributes.mtime
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< getFileInfo(...): Fault:\n" + str(e))
		raise(e)
Example #8
0
def rename(path, newName):
	"""
	Renames a file or a directory to newName, in the same folder.
	
	@since: 1.3

	@type  path: string
	@param path: the docroot-path to the object to rename
	@type  newName: string
	@param newName: the new name (basename) of the object, including extension,
	                if applicable.
	
	@rtype: bool
	@returns: False if newName already exists. True otherwise.
	"""
	getLogger().info(">> rename(%s, %s)" % (path, newName))
	if not path.startswith('/'): path = '/' + path

	res = False
	try:
		res = FileSystemManager.instance().rename(path, newName)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< rename(...): Fault:\n" + str(e))
		raise(e)
Example #9
0
def getFileInfo(path):
    """
	Gets some info about a file.
	
	Returns a dict{ 'size': integer, 'timestamp': float }
	where the size is optional (in bytes, if provided), and timestamp is
	the file modification time.

	@since: 1.0

	@type  path: string
	@param path: the path to the file whose info we want to get
	
	@rtype: a dict, or None
	@returns: None on error, or the dict of attributes.
	"""
    getLogger().info(">> getFileInfo(%s)" % path)
    if not path.startswith('/'):
        path = '/' + path

    res = None
    try:
        attributes = FileSystemManager.instance().attributes(path)
        if attributes:
            res = {}
            if attributes.size is not None:
                res['size'] = attributes.size
            if attributes.mtime is not None:
                res['timestamp'] = attributes.mtime
    except Exception, e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< getFileInfo(...): Fault:\n" + str(e))
        raise (e)
Example #10
0
def removeFile(path):
    """
	Removes a file.
	
	@since: 1.0

	@type  path: string
	@param path: the docroot-path to the file to delete
	
	@rtype: bool
	@returns: True if OK, False if nothing deleted. (? to check)
	"""
    getLogger().info(">> removeFile(%s)" % path)
    if not path.startswith('/'):
        path = '/' + path

    res = False
    try:
        res = FileSystemManager.instance().unlink(path)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< removeFile(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< removeFile(): %s" % str(res))
    return res
Example #11
0
def makeDirectory(path):
    """
	Creates a directory and all the needed directories to it, if any.
	
	@since: 1.3
	
	@type  path: string
	@param path: the docroot-path to the directory to create
	
	@rtype: bool
	@returns: True if the directory was created, False otherwise.
	"""
    getLogger().info(">> makeDirectory(%s)" % (path))
    if not path.startswith('/'): path = '/' + path

    res = False
    try:
        res = FileSystemManager.instance().mkdir(path)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< makeDirectory(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< makeDirectory(): %s" % str(res))
    return res
Example #12
0
def createPackage(path):
	"""
	Creates a new package structure with path as package root folder.
	"""
	if FileSystemManager.instance().isfile(path):
		getLogger().info("Cannot create package as %s: is a file" % path)
		raise Exception("Invalid package path: is a file")
	
	if FileSystemManager.instance().isdir(path):
		getLogger().info("Cannot create package as %s: path already exist" % path)
		raise Exception("Invalid package path: this path already exist")

	FileSystemManager.instance().mkdir("%s/profiles" % path, False)
	FileSystemManager.instance().mkdir("%s/src" % path, False)
	FileSystemManager.instance().write("%s/package.xml" % path, DEFAULT_PACKAGE_DESCRIPTION, notify = False)
	FileSystemManager.instance()._notifyDirCreated(path)
	return True		
Example #13
0
def importPackageFile(content, path):
    """
	Expand a package file to a docroot folder.
	"""
    try:
        checkPackageFile(content)
    except Exception as e:
        getLogger().info("Invalid package file: %s" % e)
        raise Exception("Invalid package file: %s" % e)

    if FileSystemManager.instance().isfile(path):
        getLogger().info("Cannot import package to %s: not a path to package" %
                         path)
        raise Exception("Invalid destination package path: is a file")

    if FileSystemManager.instance().isdir(path):
        getLogger().info(
            "Cannot import package from %s: destination path already exist" %
            path)
        raise Exception(
            "Invalid destination package path: this path already exist")

    # Minimal package tree
    FileSystemManager.instance().mkdir("%s/profiles" % path, False)
    FileSystemManager.instance().mkdir("%s/src" % path, False)

    # First unpack the package, then notify the package dir creation so that it is seen as a package folder.
    tpk = StringIO.StringIO(content)
    tfile = tarfile.open("tpk", "r:gz", tpk)
    contents = tfile.getmembers()
    for c in contents:
        # TODO
        if c.name.startswith('src/') or c.name.startswith(
                'profiles/') or c.name in ['package.xml']:
            dst = "%s/%s" % (path, c.name)
            if c.isfile():
                getLogger().info("Importing %s to %s..." % (c.name, dst))
                content = tfile.extractfile(c).read()
                FileSystemManager.instance().write(dst, content, notify=False)
        else:
            getLogger().info("Discarding importation of %s" % c.name)

    FileSystemManager.instance()._notifyDirCreated(path)
    return True
Example #14
0
def createPackageFile(path):
	"""
	Creates a testerman package file from a docroot package root folder.
	"""
	def _packageFolder(tfile, relbasepath, docrootbasepath):
		getLogger().debug("Walking folder %s..." % docrootbasepath)
		for entry in FileSystemManager.instance().getdir(docrootbasepath):
			name, apptype = entry['name'], entry['type']
			if apptype == FileSystemManager.APPTYPE_DIR:
				relpath = "%s%s/" % (relbasepath, name) # the name of the current item within the package
				docrootpath = "%s%s/" % (docrootbasepath, name) # the name of the current item within the docroot
				getLogger().debug("Adding directory %s..." % relpath)
				tarinfo = tarfile.TarInfo(relpath)
				tarinfo.type = tarfile.DIRTYPE
				tarinfo.mode = 0755
				tarinfo.uid = os.getuid()
				tarinfo.gid = os.getgid()
				tarinfo.mtime = time.time()
				tfile.addfile(tarinfo)
				_packageFolder(tfile, relbasepath = relpath, docrootbasepath = docrootpath)
			else:
				relname = "%s%s" % (relbasepath, name) # the name of the current item within the package
				docrootname = "%s%s" % (docrootbasepath, name) # the name of the current item within the docroot
				getLogger().debug("Adding file %s..." % relname)
				tarinfo = tarfile.TarInfo(relname)
				tarinfo.type = tarfile.AREGTYPE
				tarinfo.mode = 0644
				tarinfo.uid = os.getuid()
				tarinfo.gid = os.getgid()
				tarinfo.mtime = time.time()
				content = FileSystemManager.instance().read(docrootname)
				tarinfo.size = len(content)
				contentObj = StringIO.StringIO(content)
				tfile.addfile(tarinfo, contentObj)
				contentObj.close()
				getLogger().debug("File %s added to package file (%s bytes)" % (relname, tarinfo.size))
		
	getLogger().info("Creating package file from %s..." % path)
	
	if not FileSystemManager.instance().isdir(path):
		getLogger().info("Cannot create package from %s: not a path to package" % path)
		return None
	
	tpk = StringIO.StringIO()
	tfile = tarfile.open("tpk", "w:gz", tpk)
	# Now, traverse the files into path
	if not path.endswith('/'):
		path = "%s/" % path
	_packageFolder(tfile, '', path)

	tfile.close()
	
	contents = tpk.getvalue()
	tpk.close()
	getLogger().info("Package file for %s created, %s bytes" % (path, len(contents)))
	return contents
Example #15
0
def getDependencies(path, recursive=False):
    """
	Computes the file dependencies of the file referenced by path.
	
	If recursive is set to True, also searches for additional dependencies
	recursively; otherwise only direct dependencies are computed.
	
	A dependency for an ATS is a module it imports.
	A depencendy for a module is a module it imports.
	A dependency for a campaign is a a script (ats or campaign) it calls.
	
	This method may be used by a client to create a package.
	
	@since: 1.3

	@type  path: string
	@param path: a docroot path to a module, ats or campaign
	@type  recursive: boolean
	@param recursive: False for direct depencencies only. True for all
	dependencies. 
	
	@rtype: list of strings
	@returns: a list of dependencies as docroot-path to filenames.
	A dependency is only listed once (no duplicate).
	"""
    getLogger().info(">> getDependencies(%s, %s)" % (path, recursive))
    if not path.startswith('/'): path = '/' + path

    res = []
    try:
        source = FileSystemManager.instance().read(path)
        if source is None:
            raise Exception('Cannot find %s' % path)

        if path.endswith('.py'):
            res = DependencyResolver.python_getDependencyFilenames(
                source, path, recursive)
        elif path.endswith('.ats'):
            res = DependencyResolver.python_getDependencyFilenames(
                source, path, recursive)
        elif path.endswith('.campaign'):
            res = DependencyResolver.campaign_getDependencyFilenames(
                source,
                os.path.split(path)[0], recursive, path)
        else:
            raise Exception(
                'Unsupported file format, cannot resolve dependencies')

    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< getDependencies(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< getDependencies(): %s" % str(res))
    return res
Example #16
0
def getReverseDependencies(path):
    """
	Computes the reverse file dependencies of the file referenced by path,
	i.e. the list of files in the repository that reference this path (which
	is typically a module - campaigns referencing an ats or a campaign won't
	be retrieved for now).

	A reverse dependency for a module is another module or ATS that imports it.

	This method may be used by a client to check if a module is currently in use
	or not.

	Only reverse dependencies at call time are searched - if older revisions
	of files reference this module, it won't be checked.

	@type  path: string
	@param path: a docroot path to a module

	@rtype: list of strings
	@returns: a list of reverse dependencies as docroot-path to filenames.
	A dependency is only listed once (no duplicate).
	"""
    getLogger().info(">> getReverseDependencies(%s)" % (path))
    if not path.startswith('/'): path = '/' + path

    res = []
    try:
        source = FileSystemManager.instance().read(path)
        if source is None:
            raise Exception('Cannot find %s' % path)

        if path.endswith('.py'):
            # Well, we need to scan all our python and ATS files into our repository (the candidate)
            # do a:
            # candidateSource = getFile(candidatePath)
            # try:
            # 	deps = DependencyResolver.python_getDependencyFilenames(candidateSource, candidatePath, recursive = False)
            # except:
            #		deps = []
            # if path in deps and candidatePath not in res:
            #  res.append(candidatePath)
            res = []
        else:
            # Reverse dependencies is not supported on something that is not a module
            res = []

    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< getReverseDependencies(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< getReverseDependencies(): %s" % str(res))
    return res
Example #17
0
def getReverseDependencies(path):
	"""
	Computes the reverse file dependencies of the file referenced by path,
	i.e. the list of files in the repository that reference this path (which
	is typically a module - campaigns referencing an ats or a campaign won't
	be retrieved for now).

	A reverse dependency for a module is another module or ATS that imports it.

	This method may be used by a client to check if a module is currently in use
	or not.

	Only reverse dependencies at call time are searched - if older revisions
	of files reference this module, it won't be checked.

	@type  path: string
	@param path: a docroot path to a module

	@rtype: list of strings
	@returns: a list of reverse dependencies as docroot-path to filenames.
	A dependency is only listed once (no duplicate).
	"""
	getLogger().info(">> getReverseDependencies(%s)" % (path))
	if not path.startswith('/'): path = '/' + path

	res = []
	try:
		source = FileSystemManager.instance().read(path)
		if source is None:
			raise Exception('Cannot find %s' % path)
		
		if path.endswith('.py'):
			# Well, we need to scan all our python and ATS files into our repository (the candidate)
			# do a:
			# candidateSource = getFile(candidatePath)
			# try:
			# 	deps = DependencyResolver.python_getDependencyFilenames(candidateSource, candidatePath, recursive = False)
			# except:
			#		deps = []
			# if path in deps and candidatePath not in res:
			#  res.append(candidatePath)
			res = []
		else:
			# Reverse dependencies is not supported on something that is not a module
			res = []
		
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< getReverseDependencies(...): Fault:\n" + str(e))
		raise(e)
Example #18
0
def schedulePackage(path, username, session, at, script = None, profileName = None):
	"""
	Schedules a package to start at <at>.
	
	If script is provided, the package's default-script attribute is ignored
	and script is used instead.
	
	@since: 1.3
	"""
	getLogger().info(">> schedulePackage(%s, script = %s)" % (path, script))
	if not path.startswith('/'): path = '/' + path

	try:
		metadata = Package.getPackageMetadata(path)
		
		if not script:
			script = metadata['default-script']

		scriptFilename = "%s/src/%s" % (path, script)
		scriptPath = os.path.split(scriptFilename)[0]
		
		label = "%s:%s" % ('/'.join(path.split('/')[2:]), script)
		
		getLogger().info("Using package script filename %s, path %s" % (scriptFilename, scriptPath))
		
		if script.endswith(".campaign"):
			res = scheduleCampaign(FileSystemManager.instance().read(scriptFilename).decode('utf-8'), label, username, session, at = at, path = scriptPath)
		elif script.endswith(".ats"):
			res = scheduleAts(FileSystemManager.instance().read(scriptFilename).decode('utf-8'), label, username, session, at = at, path = scriptPath)
		else:
			raise Exception("Invalid script/default script for package execution (unrecognized job type based on extension - %s)" % script)
	
	except Exception, e:
		e =  Exception("Scheduling error: %s" % (str(e)))
		getLogger().info("<< schedulePackage(...): Fault:\n%s" % Tools.getBacktrace())
		raise(e)
Example #19
0
def move(source, destination):
    """
	Moves a file or a directory to destination.
	Recursive operation: if the source is a directory, the whole
	tree will be moved. 

	Logs associated to a scripts, if any, are
	NOT moved. They are kept available in the archives,
	but not associated to the script any more.
	
	FIXME: Revisions should be moved, however. 
	
	source is a docroot to an existing path or directory.
	destination is a docroot path to a destination:
	- if source is a dir, destination can be an existing dir
	  (will create a new dir in it) or a new directory name
	  (will rename the directory).
	- if source is a file, destination can be an existing dir
	  (will create the file in it, without renaming it), or
		a new file name (will rename the file).
	
	@since: 1.3

	@type  source: string
	@param source: docroot-path to the object to move
	@type  destination: string
	@param destination: docroot-path to the destination
	(if existing: must be a directory; if not existing, will rename
	the object on the fly)

	@rtype: bool
	@returns: True if the move was OK, False otherwise.
	"""
    getLogger().info(">> move(%s, %s)" % (source, destination))
    if not source.startswith('/'): source = '/' + source
    if not destination.startswith('/'): destination = '/' + destination

    res = False
    try:
        res = FileSystemManager.instance().move(source, destination)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< move(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< move(): %s" % str(res))
    return res
Example #20
0
def getDependencies(path, recursive = False):
	"""
	Computes the file dependencies of the file referenced by path.
	
	If recursive is set to True, also searches for additional dependencies
	recursively; otherwise only direct dependencies are computed.
	
	A dependency for an ATS is a module it imports.
	A depencendy for a module is a module it imports.
	A dependency for a campaign is a a script (ats or campaign) it calls.
	
	This method may be used by a client to create a package.
	
	@since: 1.3

	@type  path: string
	@param path: a docroot path to a module, ats or campaign
	@type  recursive: boolean
	@param recursive: False for direct depencencies only. True for all
	dependencies. 
	
	@rtype: list of strings
	@returns: a list of dependencies as docroot-path to filenames.
	A dependency is only listed once (no duplicate).
	"""
	getLogger().info(">> getDependencies(%s, %s)" % (path, recursive))
	if not path.startswith('/'): path = '/' + path

	res = []
	try:
		source = FileSystemManager.instance().read(path)
		if source is None:
			raise Exception('Cannot find %s' % path)
		
		if path.endswith('.py'):
			res = DependencyResolver.python_getDependencyFilenames(source, path, recursive)
		elif path.endswith('.ats'):
			res = DependencyResolver.python_getDependencyFilenames(source, path, recursive)
		elif path.endswith('.campaign'):	
			res = DependencyResolver.campaign_getDependencyFilenames(source, os.path.split(path)[0], recursive, path)
		else:
			raise Exception('Unsupported file format, cannot resolve dependencies')
		
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< getDependencies(...): Fault:\n" + str(e))
		raise(e)
Example #21
0
def getFile(path, useCompression=False):
    """
	Retrieves a file according to the path.
	The path is relative to the document root.
	
	If useCompression is set, compress the output before encoding it to mime64.
	
	@since: 1.0

	@type  path: string
	@param path: a path to a file
	@type  useCompression: bool
	@param useCompression: if True, the output is gziped before being mime64-encoded
	
	@rtype: string (utf-8 or buffer, encoded in base64), or None
	@returns: None if the file was not found,
	          or the file contents in base64 encoding, optionally compressed
	"""
    getLogger().info(">> getFile(%s, %s)" % (path, useCompression))
    if not path.startswith('/'):
        path = '/' + path

    ret = None
    try:
        contents = FileSystemManager.instance().read(path)
        if contents is None:
            ret = None
        else:
            if useCompression:
                ret = base64.encodestring(zlib.compress(contents))
            else:
                ret = base64.encodestring(contents)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< getFile(...): Fault:\n%s" % str(e))
        ret = None

    if ret is not None:
        getLogger().info("<< getFile(%s): %d bytes returned" %
                         (path, len(ret)))
    else:
        getLogger().info("<< getFile(%s): file not found" % (path))
    return ret
Example #22
0
def move(source, destination):
	"""
	Moves a file or a directory to destination.
	Recursive operation: if the source is a directory, the whole
	tree will be moved. 

	Logs associated to a scripts, if any, are
	NOT moved. They are kept available in the archives,
	but not associated to the script any more.
	
	FIXME: Revisions should be moved, however. 
	
	source is a docroot to an existing path or directory.
	destination is a docroot path to a destination:
	- if source is a dir, destination can be an existing dir
	  (will create a new dir in it) or a new directory name
	  (will rename the directory).
	- if source is a file, destination can be an existing dir
	  (will create the file in it, without renaming it), or
		a new file name (will rename the file).
	
	@since: 1.3

	@type  source: string
	@param source: docroot-path to the object to move
	@type  destination: string
	@param destination: docroot-path to the destination
	(if existing: must be a directory; if not existing, will rename
	the object on the fly)

	@rtype: bool
	@returns: True if the move was OK, False otherwise.
	"""
	getLogger().info(">> move(%s, %s)" % (source, destination))
	if not source.startswith('/'): source = '/' + source
	if not destination.startswith('/'): destination = '/' + destination

	res = False
	try:
		res = FileSystemManager.instance().move(source, destination)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< move(...): Fault:\n" + str(e))
		raise(e)
Example #23
0
def copy(source, destination):
    """
	Copies a file or a directory to destination.
	Recursive operation: if the source is a directory, the whole
	tree will be copied. 
	
	Meta children (Revisions and logs, if any) are NOT copied.
	
	source is a docroot to an existing path or directory.
	destination is a docroot path to a destination:
	- if source is a dir, destination can be an existing dir
	  (will create a new dir in it) or a new directory name
	  (will rename the directory).
	- if source is a file, destination can be an existing dir
	  (will create the file in it, without renaming it), or
		a new file name (will rename the file).
	
	@since: 1.3

	@type  source: string
	@param source: docroot-path to the object to move
	@type  destination: string
	@param destination: docroot-path to the destination
	(if existing: must be a directory; if not existing, will rename
	the object on the fly)

	@rtype: bool
	@returns: True if the move was OK, False otherwise.
	"""
    getLogger().info(">> copy(%s, %s)" % (source, destination))
    if not source.startswith('/'): source = '/' + source
    if not destination.startswith('/'): destination = '/' + destination

    res = False
    try:
        res = FileSystemManager.instance().copy(source, destination)
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< copy(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< copy(): %s" % str(res))
    return res
Example #24
0
def putFile(content, path, useCompression=False, username=None):
    """
	Writes a file to docroot/path
	
	@since: 1.0

	@type  content: utf-8 encoded (or buffer) string, encoded in mime64
	@param content: the content of the file
	@type  path: string
	@param path: a complete path, with filename and extension, relative to the document root.
	@type  useCompression: bool
	@param useCompression: (since 1.3) if set to True, the content is gziped before being mime64-encoded.
	@type  username: string
	@param username: (since 1.7) the committer/writer
	
	@rtype: bool
	@returns: True if OK, False otherwise
	"""
    getLogger().info(">> putFile(%s, %s)" % (path, useCompression))
    if not path.startswith('/'):
        path = '/' + path

    res = False
    try:
        content = base64.decodestring(content)
        if useCompression:
            content = zlib.decompress(content)
        revision = FileSystemManager.instance().write(path,
                                                      content,
                                                      username=username)
        # No revision handling for now
        # We should return the new filepath in case of a success
        # /repository/samples/[email protected]
        # etc
        res = True
    except Exception as e:
        e = Exception("Unable to perform operation: %s\n%s" %
                      (str(e), Tools.getBacktrace()))
        getLogger().info("<< putFile(...): Fault:\n" + str(e))
        raise (e)

    getLogger().info("<< putFile(): %s" % str(res))
    return res
Example #25
0
def getPackageMetadata(path):
	"""
	Extracts the different package metadata contained into the package.xml root folder.
	
	@type  path: string
	@param path: the docroot path to the package's root folder
	
	@rtype: dict[string] of unicode string
	@returns: the metadata, as a dict containing the following keys:
	          author, description, default-script, status
	"""
	descriptionFile = "%s/package.xml" % path
	try:
		content = FileSystemManager.instance().read(descriptionFile)
		if content is None:
			raise Exception("Unable to read the package description file for package %s" % path)
		# Now, parse the document
		return parsePackageDescription(content)
	except Exception, e:
		raise e
Example #26
0
def copy(source, destination):
	"""
	Copies a file or a directory to destination.
	Recursive operation: if the source is a directory, the whole
	tree will be copied. 
	
	Meta children (Revisions and logs, if any) are NOT copied.
	
	source is a docroot to an existing path or directory.
	destination is a docroot path to a destination:
	- if source is a dir, destination can be an existing dir
	  (will create a new dir in it) or a new directory name
	  (will rename the directory).
	- if source is a file, destination can be an existing dir
	  (will create the file in it, without renaming it), or
		a new file name (will rename the file).
	
	@since: 1.3

	@type  source: string
	@param source: docroot-path to the object to move
	@type  destination: string
	@param destination: docroot-path to the destination
	(if existing: must be a directory; if not existing, will rename
	the object on the fly)

	@rtype: bool
	@returns: True if the move was OK, False otherwise.
	"""
	getLogger().info(">> copy(%s, %s)" % (source, destination))
	if not source.startswith('/'): source = '/' + source
	if not destination.startswith('/'): destination = '/' + destination

	res = False
	try:
		res = FileSystemManager.instance().copy(source, destination)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< copy(...): Fault:\n" + str(e))
		raise(e)
Example #27
0
def makeDirectory(path):
	"""
	Creates a directory and all the needed directories to it, if any.
	
	@since: 1.3
	
	@type  path: string
	@param path: the docroot-path to the directory to create
	
	@rtype: bool
	@returns: True if the directory was created, False otherwise.
	"""
	getLogger().info(">> makeDirectory(%s)" % (path))
	if not path.startswith('/'): path = '/' + path

	res = False
	try:
		res = FileSystemManager.instance().mkdir(path)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< makeDirectory(...): Fault:\n" + str(e))
		raise(e)
Example #28
0
def removeFile(path):
	"""
	Removes a file.
	
	@since: 1.0

	@type  path: string
	@param path: the docroot-path to the file to delete
	
	@rtype: bool
	@returns: True if OK, False if nothing deleted. (? to check)
	"""
	getLogger().info(">> removeFile(%s)" % path)
	if not path.startswith('/'):
		path = '/' + path

	res = False
	try:
		res = FileSystemManager.instance().unlink(path)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< removeFile(...): Fault:\n" + str(e))
		raise(e)
Example #29
0
def putFile(content, path, useCompression = False, username = None):
	"""
	Writes a file to docroot/path
	
	@since: 1.0

	@type  content: utf-8 encoded (or buffer) string, encoded in mime64
	@param content: the content of the file
	@type  path: string
	@param path: a complete path, with filename and extension, relative to the document root.
	@type  useCompression: bool
	@param useCompression: (since 1.3) if set to True, the content is gziped before being mime64-encoded.
	@type  username: string
	@param username: (since 1.7) the committer/writer
	
	@rtype: bool
	@returns: True if OK, False otherwise
	"""
	getLogger().info(">> putFile(%s, %s)" % (path, useCompression))
	if not path.startswith('/'):
		path = '/' + path

	res = False
	try:
		content = base64.decodestring(content)
		if useCompression:
			content = zlib.decompress(content)
		revision = FileSystemManager.instance().write(path, content, username = username)
		# No revision handling for now
		# We should return the new filepath in case of a success
		# /repository/samples/[email protected]
		# etc
		res = True
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< putFile(...): Fault:\n" + str(e))
		raise(e)
Example #30
0
def getFile(path, useCompression = False):
	"""
	Retrieves a file according to the path.
	The path is relative to the document root.
	
	If useCompression is set, compress the output before encoding it to mime64.
	
	@since: 1.0

	@type  path: string
	@param path: a path to a file
	@type  useCompression: bool
	@param useCompression: if True, the output is gziped before being mime64-encoded
	
	@rtype: string (utf-8 or buffer, encoded in base64), or None
	@returns: None if the file was not found,
	          or the file contents in base64 encoding, optionally compressed
	"""
	getLogger().info(">> getFile(%s, %s)" % (path, useCompression))
	if not path.startswith('/'):
		path = '/' + path

	ret = None
	try:
		contents = FileSystemManager.instance().read(path)
		if contents is None:
			ret = None
		else:
			if useCompression:
				ret = base64.encodestring(zlib.compress(contents))
			else:
				ret = base64.encodestring(contents)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< getFile(...): Fault:\n%s" % str(e))
		ret = None
Example #31
0
def removeDirectory(path, recursive = False):
	"""
	Removes an empty directory, unless recursive is set to True.
	
	@since: 1.1

	@type  path: string
	@param path: the docroot-path to the directory to delete
	@type  recursive: bool
	@param recursive: True if we should delete files and directories in it. DANGEROUS.
	
	@rtype: bool
	@returns: True if OK, False if nothing deleted. (? to check)
	"""
	getLogger().info(">> removeDirectory(%s)" % path)
	if not path.startswith('/'): path = '/' + path

	res = False
	try:
		res = FileSystemManager.instance().rmdir(path, recursive)
	except Exception, e:
		e =  Exception("Unable to perform operation: %s\n%s" % (str(e), Tools.getBacktrace()))
		getLogger().info("<< removeDirectory(...): Fault:\n" + str(e))
		raise(e)
def campaign_getDependencyFilenames(source, sourcePath = None, recursive = False, sourceFilename = "<local>", moduleRootDir = '/repository'):
	"""
	Returns a list of userland module filenames
	(including their own dependencies) the campaign depends on.
	
	Calls pythong_getDependencyFilenames on ats files if recursive is True,
	and thus only userland dependencies are searched.

	NB: this works only because user modules are files only, not packages.
	
	@type  source: utf-8 string
	@param source: a Python source file (module or ATS)
	@type  sourcePath: string
	@param sourcePath: docroot path used as a 'working dir' to search for
	relative dependencies. Typically the path where the source has been extracted
	from.
	@type  recursive: bool
	@param recursive: if True, search recursively for dependencies in imported modules.
	@type  sourceFilename: string
	@param sourceFilename: a string to help identify the file that yielded the source. 
	For non-repository files, use <local>, by convention.
	@type  moduleRootDir: string
	@param moduleRootDir: the docroot directory we should start looking modules from.
	For a normal file, this is /repository. For a file contained in a package, this is the package src dir.
	
	@rtype: list of strings
	@returns: a list of docroot-path to dependencies (no duplicate).
	"""
	
	currentDependencies = []
	
	getLogger().info("%s: parsing campaign file to look for its dependencies" % sourceFilename)

	# The path of the campaign within the docroot.
	path = sourcePath

	# Based on a file parsing.
	indent = 0
	parentPresent = True
	lc = 0
	for line in source.splitlines():
		lc += 1
		# Remove comments
		line = line.split('#', 1)[0].rstrip()
		if not line:
			continue # empty line
		m = re.match(r'(?P<indent>\s*)((?P<branch>\w+|\*)\s+)?(?P<type>\w+)\s+(?P<filename>[^\s]+)(\s+with\s+(?P<mapping>.*)\s*)?', line)
		if not m:
			raise Exception('%s: parse error at line %s: invalid line format' % (sourceFilename, lc))

		type_ = m.group('type')
		filename = m.group('filename')
		indentDiff = len(m.group('indent')) - indent
		indent = indent + indentDiff

		# Filename creation within the docroot
		if filename.startswith('/'):
			# absolute path within the *repository*
			filename = '/%s%s' % (cm.get_transient('constants.repository'), filename)
		else:
			# just add the local campaign path
			filename = '%s/%s' % (path, filename)               

		# Type validation
		if not type_ in [ 'ats', 'campaign' ]:
			raise Exception('%s: error at line %s: invalid job type (%s)' % (sourceFilename, lc, type_))

		# Indentation validation
		if indentDiff > 1:
			raise Exception('%s: parse error at line %s: invalid indentation (too deep)' % (sourceFilename, lc))

		
		elif indentDiff == 1:
			if not parentPresent:
				raise Exception('%s: parse error at line %s: invalid indentation (invalid initial indentation)' % (sourceFilename, lc))

		elif indentDiff == 0:
			# the current parent remains the parent
			pass

		else:
			# negative indentation. 
			pass

		# OK, now we have at least one parent.
		parentPresent = True

		# Branch validation: ignored for dependency resolver

		# Now handle the dependency.
		if not filename in currentDependencies:
			getLogger().info('%s: campaign direct dependency added: %s' % (sourceFilename, filename))
			currentDependencies.append(filename)
			
			if recursive and type_ in ['ats', 'campaign']:
				nextDependencies = []
				nextPath, nextFilename = os.path.split(filename)
				nextSource = FileSystemManager.instance().read(filename)
				if nextSource is None:
					raise Exception('%s: missing dependency: file %s is not in the repository' % (sourceFilename, filename))
				if type_ == 'campaign':
					nextDependencies = campaign_getDependencyFilenames(nextSource, nextPath, True, filename, moduleRootDir = moduleRootDir)
				elif type_ == 'ats':
					nextDependencies = python_getDependencyFilenames(nextSource, sourceFilename = filename, recursive = True, moduleRootDir = moduleRootDir)
				
				for dep in nextDependencies:
					if not dep in currentDependencies:
						getLogger().info('%s: campaign indirect dependency added: %s' % (sourceFilename, dep))
						currentDependencies.append(dep)
		
	return currentDependencies
Example #33
0
def campaign_getDependencyFilenames(source,
                                    sourcePath=None,
                                    recursive=False,
                                    sourceFilename="<local>",
                                    moduleRootDir='/repository'):
    """
	Returns a list of userland module filenames
	(including their own dependencies) the campaign depends on.
	
	Calls pythong_getDependencyFilenames on ats files if recursive is True,
	and thus only userland dependencies are searched.

	NB: this works only because user modules are files only, not packages.
	
	@type  source: utf-8 string
	@param source: a Python source file (module or ATS)
	@type  sourcePath: string
	@param sourcePath: docroot path used as a 'working dir' to search for
	relative dependencies. Typically the path where the source has been extracted
	from.
	@type  recursive: bool
	@param recursive: if True, search recursively for dependencies in imported modules.
	@type  sourceFilename: string
	@param sourceFilename: a string to help identify the file that yielded the source. 
	For non-repository files, use <local>, by convention.
	@type  moduleRootDir: string
	@param moduleRootDir: the docroot directory we should start looking modules from.
	For a normal file, this is /repository. For a file contained in a package, this is the package src dir.
	
	@rtype: list of strings
	@returns: a list of docroot-path to dependencies (no duplicate).
	"""

    currentDependencies = []

    getLogger().info("%s: parsing campaign file to look for its dependencies" %
                     sourceFilename)

    # The path of the campaign within the docroot.
    path = sourcePath

    # Based on a file parsing.
    indent = 0
    parentPresent = True
    lc = 0
    for line in source.splitlines():
        lc += 1
        # Remove comments
        line = line.split('#', 1)[0].rstrip()
        if not line:
            continue  # empty line
        m = re.match(
            r'(?P<indent>\s*)((?P<branch>\w+|\*)\s+)?(?P<type>\w+)\s+(?P<filename>[^\s]+)(\s+with\s+(?P<mapping>.*)\s*)?',
            line)
        if not m:
            raise Exception('%s: parse error at line %s: invalid line format' %
                            (sourceFilename, lc))

        type_ = m.group('type')
        filename = m.group('filename')
        indentDiff = len(m.group('indent')) - indent
        indent = indent + indentDiff

        # Filename creation within the docroot
        if filename.startswith('/'):
            # absolute path within the *repository*
            filename = '/%s%s' % (cm.get_transient('constants.repository'),
                                  filename)
        else:
            # just add the local campaign path
            filename = '%s/%s' % (path, filename)

        # Type validation
        if not type_ in ['ats', 'campaign']:
            raise Exception('%s: error at line %s: invalid job type (%s)' %
                            (sourceFilename, lc, type_))

        # Indentation validation
        if indentDiff > 1:
            raise Exception(
                '%s: parse error at line %s: invalid indentation (too deep)' %
                (sourceFilename, lc))

        elif indentDiff == 1:
            if not parentPresent:
                raise Exception(
                    '%s: parse error at line %s: invalid indentation (invalid initial indentation)'
                    % (sourceFilename, lc))

        elif indentDiff == 0:
            # the current parent remains the parent
            pass

        else:
            # negative indentation.
            pass

        # OK, now we have at least one parent.
        parentPresent = True

        # Branch validation: ignored for dependency resolver

        # Now handle the dependency.
        if not filename in currentDependencies:
            getLogger().info('%s: campaign direct dependency added: %s' %
                             (sourceFilename, filename))
            currentDependencies.append(filename)

            if recursive and type_ in ['ats', 'campaign']:
                nextDependencies = []
                nextPath, nextFilename = os.path.split(filename)
                nextSource = FileSystemManager.instance().read(filename)
                if nextSource is None:
                    raise Exception(
                        '%s: missing dependency: file %s is not in the repository'
                        % (sourceFilename, filename))
                if type_ == 'campaign':
                    nextDependencies = campaign_getDependencyFilenames(
                        nextSource,
                        nextPath,
                        True,
                        filename,
                        moduleRootDir=moduleRootDir)
                elif type_ == 'ats':
                    nextDependencies = python_getDependencyFilenames(
                        nextSource,
                        sourceFilename=filename,
                        recursive=True,
                        moduleRootDir=moduleRootDir)

                for dep in nextDependencies:
                    if not dep in currentDependencies:
                        getLogger().info(
                            '%s: campaign indirect dependency added: %s' %
                            (sourceFilename, dep))
                        currentDependencies.append(dep)

    return currentDependencies
def python_getDependencyFilenames(source, sourceFilename, recursive = True, moduleRootDir = '/repository'):
	"""
	Returns a list of userland module filenames
	(including their own dependencies) the ATS/module depends on.
	
	Only userland dependencies are searched, i.e. modules below $docroot/repository.
	Non-userland dependencies, such as Testerman and Python std modules,
	are not reported.

	NB: this works only because user modules are files only, not packages.
	
	@type  source: utf-8 string
	@param source: a Python source file (module or ATS)
	@type  sourceFilename: string
	@param sourceFilename: the full docroot path to the source file. 
	For non-repository files, should be set to something like /repository/<anonymous.ats>
	so that we can deduce the docroot "working dir" dependencies should be searched from.
	@type  recursive: bool
	@param recursive: if True, search recursively for dependencies in imported modules.
	@type  moduleRootDir: string
	@param moduleRootDir: the docroot directory we should start looking modules from.
	For a normal file, this is /repository. For a file contained in a package, this is the package src dir.
	
	@rtype: list of strings
	@returns: a list of docroot-path to dependencies (no duplicate).
	"""
	getLogger().info("Resolving dependencies for file %s" % sourceFilename)

	# This is the PYTHONPATH that will be used to run the TE.
	# Will be used as a fallback if an import cannot be found in userland (ie in the repo)
	pythonPath = '%(root)s/modules' % dict(
		root = cm.get_transient("ts.server_root"))
	additionalPythonPath = cm.get("testerman.te.python.additional_pythonpath")
	if additionalPythonPath:
		pythonPath += ':' + additionalPythonPath
	pythonPath = pythonPath.split(':')

	# Will only include userland dependencies. System dependencies found in PYTHONPATH won't be included.
	ret = []

	# Bootstrap the deps (stored as (list of imported modules, path of the importing file) )
	toResolve = [ (d, sourceFilename) for d in python_getImportedUserlandModules(source, sourceFilename = sourceFilename) ]
	
	# the list of resolved dependencies,
	# as a map (import, fromFilename): resolvedFilename
	resolvedSoFar = {}
	
	# For each deps to resolve (a list of (import, fromFilename)),
	# we need to resolve the filename that will provide this import for this file.
	while len(toResolve):
		getLogger().debug("List of imports to resolve for script %s:\n%s" % (sourceFilename, "\n".join(["%s (used in %s)" % x for x in toResolve])))
		dep, fromFilename = toResolve.pop()
		# Some non-userland files - not resolved to build the TE userland package
		# How can we detect standard Python includes ?
		# fromFilePath starts with the "python home" ? something else ?
		getLogger().debug("Resolving import %s from %s..." % (dep, fromFilename))

		# Skip some dependencies provided by the Testerman infrastructure
		#if dep in [ ]:
		#	getLogger().info("Resolving import %s from %: skipped, not userland" % (dep, fromFilename))
		#	continue

		# Skip already resolved dependencies
		if (dep, fromFilename) in resolvedSoFar:
			getLogger().debug("Resolving import %s from %s: already resolved as %s" % (dep, fromFilename, resolvedSoFar[(dep, fromFilename)]))
			continue

		# Ordered list of filenames within the docroot that could provide the dependency:
		# (module path)
		# - first search from the local file path, if provided,
		# - then search from the userland module paths (limited to '/repository/' for now)
		modulePaths = []
		# First, try a local module (relative path) (same dir as the currently analyzed file)
		sourcePath = os.path.split(fromFilename)[0] 
		modulePaths.append(sourcePath)
		# Then fall back to standard "testerman userland paths"
		# If the filename was in a package, look from the package's root dir
		# Otherwise this the repository root.
		for modulePath in [ moduleRootDir ]:
			if modulePath and not modulePath in modulePaths:
				modulePaths.append(modulePath)

		getLogger().debug("Resolving import %s from %s: searching in paths:\n%s" % (dep, fromFilename, "\n".join(modulePaths)))

		found = None
		depSource = None
		for path in modulePaths:
			depFilename = '%s/%s.py' % (path, dep.replace('.', '/'))
			try:
				depSource = FileSystemManager.instance().read(depFilename)
			except Exception:
				pass
			if depSource is not None:
				found = depFilename
				break
		if not found:
			getLogger().debug("Resolving import %s from %s: not available in repository, searched in paths:\n%s" % (dep, fromFilename, "\n".join(modulePaths)))
			try:
				imp.find_module(dep, pythonPath)
			except:
				getLogger().debug("Resolving import %s from %s: not available in PYTHONPATH either, searched paths:\n%s" % (dep, fromFilename, "\n".join(pythonPath)))
				raise Exception('Missing module: %s (imported from %s) is not available in userland (repository, searched paths: %s) or in TE PYTHONPATH (searched paths: %s)' % (dep, fromFilename, modulePaths, pythonPath))
			getLogger().debug("Resolving import %s from %s: OK, found in PYTHONPATH, won't be included in userland dependencies")

		if found:
			# OK, we resolved a file.
			resolvedSoFar[(dep, fromFilename)] = depFilename
			getLogger().debug("Resolving import %s from %s: resolved as %s" % (dep, fromFilename, depFilename))
			if not depFilename in ret:
				ret.append(depFilename)
				getLogger().debug("Script %s is now using the following files:\n%s" % (sourceFilename, "\n".join(ret)))

			# Now, analyze the resolved file and add its own dependencies to the list to resolve,
			# if not already resolved
			if recursive:
				importedModules = python_getImportedUserlandModules(depSource, depFilename)
				for im in importedModules:
					if not (im, depFilename) in resolvedSoFar:
						toResolve.append((im, depFilename))
					else:
						getLogger().debug("Resolving import %s from %s: already resolved" % (im, depFilename))

	return ret	
Example #35
0
		raise Exception("Missing profiles folder (profiles)")
	
	# Now we should check the package.xml file, too
	return True

def importPackageFile(content, path):
	"""
	Expand a package file to a docroot folder.
	"""
	try:
		checkPackageFile(content)
	except Exception, e:
		getLogger().info("Invalid package file: %s" % e)
		raise Exception("Invalid package file: %s" % e)
	
	if FileSystemManager.instance().isfile(path):
		getLogger().info("Cannot import package to %s: not a path to package" % path)
		raise Exception("Invalid destination package path: is a file")
	
	if FileSystemManager.instance().isdir(path):
		getLogger().info("Cannot import package from %s: destination path already exist" % path)
		raise Exception("Invalid destination package path: this path already exist")


	# Minimal package tree
	FileSystemManager.instance().mkdir("%s/profiles" % path, False)
	FileSystemManager.instance().mkdir("%s/src" % path, False)

	# First unpack the package, then notify the package dir creation so that it is seen as a package folder.
	tpk = StringIO.StringIO(content)
	tfile = tarfile.open("tpk", "r:gz", tpk)
Example #36
0
def python_getDependencyFilenames(source,
                                  sourceFilename,
                                  recursive=True,
                                  moduleRootDir='/repository'):
    """
	Returns a list of userland module filenames
	(including their own dependencies) the ATS/module depends on.
	
	Only userland dependencies are searched, i.e. modules below $docroot/repository.
	Non-userland dependencies, such as Testerman and Python std modules,
	are not reported.

	NB: this works only because user modules are files only, not packages.
	
	@type  source: utf-8 string
	@param source: a Python source file (module or ATS)
	@type  sourceFilename: string
	@param sourceFilename: the full docroot path to the source file. 
	For non-repository files, should be set to something like /repository/<anonymous.ats>
	so that we can deduce the docroot "working dir" dependencies should be searched from.
	@type  recursive: bool
	@param recursive: if True, search recursively for dependencies in imported modules.
	@type  moduleRootDir: string
	@param moduleRootDir: the docroot directory we should start looking modules from.
	For a normal file, this is /repository. For a file contained in a package, this is the package src dir.
	
	@rtype: list of strings
	@returns: a list of docroot-path to dependencies (no duplicate).
	"""
    getLogger().info("Resolving dependencies for file %s" % sourceFilename)

    # This is the PYTHONPATH that will be used to run the TE.
    # Will be used as a fallback if an import cannot be found in userland (ie in the repo)
    pythonPath = '%(root)s/modules' % dict(
        root=cm.get_transient("ts.server_root"))
    additionalPythonPath = cm.get("testerman.te.python.additional_pythonpath")
    if additionalPythonPath:
        pythonPath += ':' + additionalPythonPath
    pythonPath = pythonPath.split(':')

    # Will only include userland dependencies. System dependencies found in PYTHONPATH won't be included.
    ret = []

    # Bootstrap the deps (stored as (list of imported modules, path of the importing file) )
    toResolve = [(d, sourceFilename)
                 for d in python_getImportedUserlandModules(
                     source, sourceFilename=sourceFilename)]

    # the list of resolved dependencies,
    # as a map (import, fromFilename): resolvedFilename
    resolvedSoFar = {}

    # For each deps to resolve (a list of (import, fromFilename)),
    # we need to resolve the filename that will provide this import for this file.
    while len(toResolve):
        getLogger().debug("List of imports to resolve for script %s:\n%s" %
                          (sourceFilename, "\n".join(
                              ["%s (used in %s)" % x for x in toResolve])))
        dep, fromFilename = toResolve.pop()
        # Some non-userland files - not resolved to build the TE userland package
        # How can we detect standard Python includes ?
        # fromFilePath starts with the "python home" ? something else ?
        getLogger().debug("Resolving import %s from %s..." %
                          (dep, fromFilename))

        # Skip some dependencies provided by the Testerman infrastructure
        #if dep in [ ]:
        #	getLogger().info("Resolving import %s from %: skipped, not userland" % (dep, fromFilename))
        #	continue

        # Skip already resolved dependencies
        if (dep, fromFilename) in resolvedSoFar:
            getLogger().debug(
                "Resolving import %s from %s: already resolved as %s" %
                (dep, fromFilename, resolvedSoFar[(dep, fromFilename)]))
            continue

        # Ordered list of filenames within the docroot that could provide the dependency:
        # (module path)
        # - first search from the local file path, if provided,
        # - then search from the userland module paths (limited to '/repository/' for now)
        modulePaths = []
        # First, try a local module (relative path) (same dir as the currently analyzed file)
        sourcePath = os.path.split(fromFilename)[0]
        modulePaths.append(sourcePath)
        # Then fall back to standard "testerman userland paths"
        # If the filename was in a package, look from the package's root dir
        # Otherwise this the repository root.
        for modulePath in [moduleRootDir]:
            if modulePath and not modulePath in modulePaths:
                modulePaths.append(modulePath)

        getLogger().debug(
            "Resolving import %s from %s: searching in paths:\n%s" %
            (dep, fromFilename, "\n".join(modulePaths)))

        found = None
        depSource = None
        for path in modulePaths:
            depFilename = '%s/%s.py' % (path, dep.replace('.', '/'))
            try:
                depSource = FileSystemManager.instance().read(depFilename)
            except Exception:
                pass
            if depSource is not None:
                found = depFilename
                break
        if not found:
            getLogger().debug(
                "Resolving import %s from %s: not available in repository, searched in paths:\n%s"
                % (dep, fromFilename, "\n".join(modulePaths)))
            try:
                imp.find_module(dep, pythonPath)
            except:
                getLogger().debug(
                    "Resolving import %s from %s: not available in PYTHONPATH either, searched paths:\n%s"
                    % (dep, fromFilename, "\n".join(pythonPath)))
                raise Exception(
                    'Missing module: %s (imported from %s) is not available in userland (repository, searched paths: %s) or in TE PYTHONPATH (searched paths: %s)'
                    % (dep, fromFilename, modulePaths, pythonPath))
            getLogger().debug(
                "Resolving import %s from %s: OK, found in PYTHONPATH, won't be included in userland dependencies"
            )

        if found:
            # OK, we resolved a file.
            resolvedSoFar[(dep, fromFilename)] = depFilename
            getLogger().debug("Resolving import %s from %s: resolved as %s" %
                              (dep, fromFilename, depFilename))
            if not depFilename in ret:
                ret.append(depFilename)
                getLogger().debug(
                    "Script %s is now using the following files:\n%s" %
                    (sourceFilename, "\n".join(ret)))

            # Now, analyze the resolved file and add its own dependencies to the list to resolve,
            # if not already resolved
            if recursive:
                importedModules = python_getImportedUserlandModules(
                    depSource, depFilename)
                for im in importedModules:
                    if not (im, depFilename) in resolvedSoFar:
                        toResolve.append((im, depFilename))
                    else:
                        getLogger().debug(
                            "Resolving import %s from %s: already resolved" %
                            (im, depFilename))

    return ret