def __init__(self, metadata, basedir, driver_file=None, driver_test_file=None): driver_generator = DriverGenerator(metadata) self.basedir = basedir if driver_file: self.driver_file = driver_file else: self.driver_file = driver_generator.driver_path() if driver_test_file: self.driver_test_file = driver_test_file else: self.driver_test_file = driver_generator.driver_test_path() self.driver_dependency = None self.test_dependency = None self.driver_dependency = DependencyList(self.driver_file, include_internal_init=True) self.test_dependency = DependencyList(self.driver_test_file, include_internal_init=True)
def save(self): driver_file = self.metadata.driver_dir() + '/' + DriverGenerator( self.metadata).driver_filename() driver_test_file = self.metadata.driver_dir( ) + '/test/' + DriverGenerator(self.metadata).driver_test_filename() filelist = DriverFileList(self.metadata, self._repo_dir(), driver_file, driver_test_file) return self._build_egg(filelist.files())
def __init__(self, launch_monitor=False): """ Setup the direct access server """ # Currently we are pulling the driver config information from the driver tests. There is a better way to do # this, but we are time crunched. TODO: Fix this! generator = DriverGenerator(Metadata()) __import__(generator.test_modulename()) self.test_config = InstrumentDriverTestConfig() # Test to ensure we have initialized our test config if not self.test_config.initialized: raise TestNotInitialized(msg="Tests non initialized. Missing InstrumentDriverTestCase.initalize(...)?") self.launch_monitor = launch_monitor
def __init__(self, metadata): """ @brief Constructor @param metadata IDK Metadata object """ self.metadata = metadata self._bdir = None if not self._tmp_dir(): raise InvalidParameters("missing tmp_dir configuration") if not self._tmp_dir(): raise InvalidParameters("missing working_repo configuration") self.generator = DriverGenerator(self.metadata) test_import = __import__(self._test_module())
def _store_package_files(self): """ @brief Store all files in zip archive and add them to the manifest file """ # make sure metadata is up to date self.metadata = Metadata(self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, REPODIR + '/marine-integrations') self.generator = DriverGenerator(self.metadata) egg_generator = EggGenerator(self.metadata) egg_file = egg_generator.save() # Add egg self._add_file(egg_file, 'egg', 'python driver egg package') # Add the package metadata file self._add_file(self.metadata.metadata_path(), description = 'package metadata') # Add the qualification test log self._add_file(self.log_path(), description = 'qualification tests results') # Store parameter/command string description file str_path = "%s/%s" % (self.generator.resource_dir(), self.string_file()) if os.path.exists(str_path): self._add_file(str_path, 'resource', 'driver string file') # Store additional resource files self._store_resource_files() # Finally save the manifest file. This must be last of course self._add_file(self.manifest().manifest_path(), description = 'package manifest file')
def __init__(self, metadata, basedir, driver_file = None, driver_test_file = None): driver_generator = DriverGenerator(metadata) self.basedir = basedir if driver_file: self.driver_file = driver_file else: self.driver_file = driver_generator.driver_path() if driver_test_file: self.driver_test_file = driver_test_file else: self.driver_test_file = driver_generator.driver_test_path() self.driver_dependency = DependencyList(self.driver_file, include_internal_init=True) self.test_dependency = DependencyList(self.driver_test_file, include_internal_init=True)
def __init__(self): """ @brief ctor """ self.metadata = Metadata() self._zipfile = None self._manifest = None self._compression = None self.generator = DriverGenerator(self.metadata) # Set compression level self.zipfile_compression()
def _store_package_files(self): """ @brief Store all files in zip archive and add them to the manifest file """ # make sure metadata is up to date self.metadata = Metadata(self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, REPODIR + '/marine-integrations') self.generator = DriverGenerator(self.metadata) egg_generator = EggGenerator(self.metadata) egg_file = egg_generator.save() # Add egg self._add_file(egg_file, 'egg', 'python driver egg package') # Add the package metadata file self._add_file(self.metadata.metadata_path(), description='package metadata') # Add the qualification test log self._add_file(self.log_path(), description='qualification tests results') # Store parameter/command string description file str_path = "%s/%s" % (self.generator.resource_dir(), self.string_file()) if os.path.exists(str_path): self._add_file(str_path, 'resource', 'driver string file') # Store additional resource files self._store_resource_files() # Finally save the manifest file. This must be last of course self._add_file(self.manifest().manifest_path(), description='package manifest file')
def _driver_test_filename(self): generator = DriverGenerator(self.metadata) return generator.driver_test_path()
def _driver_test_module(self): generator = DriverGenerator(self.metadata) return generator.test_modulename()
class PackageDriver(object): """ Main class for running the package driver process. """ ### # Configuration ### def string_file(self): return "strings.yml" def log_file(self): return "qualification.log" def log_path(self): return "%s/%s" % (self.metadata.idk_dir(), self.log_file()) def build_name(self): return "%s_%s_%s" % (self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name) def archive_file(self): return "%s-%s-driver.zip" % (self.build_name(), self.metadata.version) def archive_path(self): return os.path.join(os.path.expanduser("~"), self.archive_file()) ### # Public Methods ### def __init__(self): """ @brief ctor """ self._zipfile = None self._manifest = None self._compression = None # Set compression level self.zipfile_compression() def run_qualification_tests(self): """ @brief Run all qualification tests for the driver and store the results for packaging """ log.info("-- Running qualification tests") test = NoseTest(self.metadata, log_file=self.log_path()) test.report_header() if (test.run_qualification()): log.info(" ++ Qualification tests passed") return True else: log.error("Qualification tests have fail! No package created.") return False def clone_repo(self): """ clone the ooici repository into a temp location and navigate to it """ # make a temp dir to put the clone in if not os.path.exists(REPODIR): os.mkdir(REPODIR) os.chdir(REPODIR) # remove an old clone if one exists, start clean if os.path.exists(REPODIR + '/marine-integrations'): shutil.rmtree(REPODIR + '/marine-integrations') # clone the ooici repository into a temporary location log.debug('Attempting to clone repository into %s, REPODIR set to %s', os.getcwd(), REPODIR) ret = os.system( 'git clone [email protected]:ooici/marine-integrations.git') if ret < 0: raise GitCommandException("Bad return from git command") # if the directory doesn't exist, something went wrong with cloning if not os.path.exists(REPODIR + '/marine-integrations'): raise GitCommandException( 'Error creating ooici repository clone with base: %s' % REPODIR) # navigate into the cloned repository os.chdir(REPODIR + '/marine-integrations') log.debug('in cloned repository') def get_repackage_version(self, tag_base): """ Get the driver version the user wants to repackage """ # suggest the current driver version as default repkg_version = prompt.text('Driver Version to re-package', self.metadata.version) # confirm this version has the correct format self._verify_version(repkg_version) # check to make sure this driver version exists tag_name = 'release_' + tag_base + '_' + repkg_version.replace( '.', '_') cmd = 'git tag -l ' + tag_name # find out if this tag name exists output = subprocess.check_output(cmd, shell=True) if len(output) > 0: # this tag exists, check it out os.system('git checkout tags/' + tag_name) else: log.error('No driver version %s found', tag_name) raise InvalidParameters('No driver version %s found', tag_name) def make_branch(self, base_name): """ Make a new branch for this release and tag it with the same name so we can get back to it @param base_name - the base name for this instrument used to make the branch and tag names. The base should have the form: '<driver_name>_<driver_version>', where the version has the format X_X_X. This is equal to the branch name, and the tag will have 'release_' prepended to the base name. """ # create a new branch name and check it out cmd = 'git checkout -b ' + base_name output = subprocess.check_output(cmd, shell=True) log.debug('created new branch %s: %s', base_name, output) # tag the initial branch so that we can get back to it later cmd = 'git tag ' + 'release_' + base_name output = subprocess.check_output(cmd, shell=True) log.debug('created new tag %s: %s', 'release_' + base_name, output) def update_version(self): """ Update the driver version for this package. By default increment by one. After updating the metadata file, commit the change to git. """ last_dot = self.metadata.version.rfind('.') last_version = int(self.metadata.version[last_dot + 1:]) suggest_version = self.metadata.version[:last_dot + 1] + str(last_version + 1) new_version = prompt.text('Update Driver Version', suggest_version) # confirm this version has the correct format self._verify_version(new_version) if new_version != self.metadata.version: # search for the tag for this version, find out if it already exists cmd = 'git tag -l ' + 'release_' + self.build_name( ) + '_' + new_version.replace('.', '_') # find out if this tag name exists output = subprocess.check_output(cmd, shell=True) if len(output) > 0: # this tag already exists and we are not repackaging raise InvalidParameters( "Version %s already exists. To repackage, run package driver with the --repackage option", new_version) # set the new driver version in the metadata self.metadata.set_driver_version(new_version) # commit the changed file to git cmd = 'git commit ' + str(self.metadata.metadata_path( )) + ' -m \'Updated metadata driver version\'' os.system(cmd) return new_version def package_driver(self): """ @brief Store driver files in a zip package """ log.info("-- Building driver package") self._store_package_files() def run(self): print "*** Starting Driver Packaging Process***" # store the original directory since we will be navigating away from it original_dir = os.getcwd() # first create a temporary clone of ooici to work with self.clone_repo() # get which dataset agent is selected from the current metadata, use # this to get metadata from the cloned repo tmp_metadata = Metadata() # read metadata from the cloned repo self.metadata = Metadata(tmp_metadata.driver_make, tmp_metadata.driver_model, tmp_metadata.driver_name, REPODIR + '/marine-integrations') if "--repackage" in sys.argv: self.get_repackage_version(self.build_name()) else: new_version = self.update_version() base_name = self.build_name() + '_' + new_version.replace('.', '_') self.make_branch(base_name) if "--no-test" in sys.argv: f = open(self.log_path(), "w") f.write("Tests manually bypassed with --no-test option\n") f.close() self.package_driver() else: if (self.run_qualification_tests()): self.package_driver() if not "--no-push" in sys.argv and not "--repackage" in sys.argv: cmd = 'git push' output = subprocess.check_output(cmd, shell=True) if len(output) > 0: log.debug('git push returned: %s', output) # go back to the original directory os.chdir(original_dir) print "Package Created: " + self.archive_path() def zipfile(self): """ @brief Return the ZipFile object. Create the file if it isn't already open @retval ZipFile object """ if (not self._zipfile): self._zipfile = zipfile.ZipFile(self.archive_path(), mode="w") return self._zipfile def zipfile_compression(self): """ @brief What type of compression should we use for the package file. If we have access to zlib, we will compress @retval Compression type """ if (self._compression): return self._compression try: import zlib self._compression = zipfile.ZIP_DEFLATED log.info("Setting compression level to deflated") except: log.info("Setting compression level to store only") self._compression = zipfile.ZIP_STORED def manifest(self): """ @brief Return the PackageManifest object. Create it if it doesn't already exist @retval PackageManifest object """ if (not self._manifest): self._manifest = PackageManifest(self.metadata) return self._manifest ### # Private Methods ### def _store_package_files(self): """ @brief Store all files in zip archive and add them to the manifest file """ # make sure metadata is up to date self.metadata = Metadata(self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, REPODIR + '/marine-integrations') self.generator = DriverGenerator(self.metadata) egg_generator = EggGenerator(self.metadata) egg_file = egg_generator.save() # Add egg self._add_file(egg_file, 'egg', 'python driver egg package') # Add the package metadata file self._add_file(self.metadata.metadata_path(), description='package metadata') # Add the qualification test log self._add_file(self.log_path(), description='qualification tests results') # Store parameter/command string description file str_path = "%s/%s" % (self.generator.resource_dir(), self.string_file()) if os.path.exists(str_path): self._add_file(str_path, 'resource', 'driver string file') # Store additional resource files self._store_resource_files() # Finally save the manifest file. This must be last of course self._add_file(self.manifest().manifest_path(), description='package manifest file') def _store_resource_files(self): """ @brief Store additional files added by the driver developer. These files live in the driver resource dir. """ resource_dir = os.path.join(self.metadata.relative_driver_path(), "resource") log.debug( " -- Searching for developer added resource files in dir: %s", resource_dir) stringfile = self.string_file() if os.path.exists(resource_dir): for file in os.listdir(resource_dir): if file != stringfile: log.debug(" ++ found: " + file) desc = prompt.text('Describe ' + file) self._add_file(resource_dir + "/" + file, 'resource', desc) else: log.debug(" --- No resource directory found, skipping...") def _add_file(self, source, destdir=None, description=None): """ @brief Add a file to the zip package and store the file in the manifest. """ filename = os.path.basename(source) dest = filename if (destdir): dest = "%s/%s" % (destdir, filename) log.debug("archive %s to %s" % (filename, dest)) self.manifest().add_file(dest, description) self.zipfile().write(source, dest, self.zipfile_compression()) def _verify_version(self, version=None): """ Ensure we have a good version number and that it has not already been packaged and published """ if version == None: version = self.metadata.version if not version: raise ValidationFailure("Driver version required in metadata") p = re.compile("^\d+\.\d+\.\d+$") if not p.findall("%s" % version): raise ValidationFailure( "Version format incorrect '%s', should be x.x.x" % version)
class EggGenerator: """ Generate driver egg """ def __init__(self, metadata): """ @brief Constructor @param metadata IDK Metadata object """ self.metadata = metadata self._bdir = None if not self._tmp_dir(): raise InvalidParameters("missing tmp_dir configuration") if not self._tmp_dir(): raise InvalidParameters("missing working_repo configuration") self.generator = DriverGenerator(self.metadata) test_import = __import__(self._test_module()) def _test_module(self): return self.generator.test_modulename() def _driver_module(self): test_config = InstrumentDriverTestConfig() return test_config.driver_module def _driver_class(self): test_config = InstrumentDriverTestConfig() return test_config.driver_class def _repo_dir(self): return Config().get('working_repo') def _tmp_dir(self): return Config().get('tmp_dir') def _setup_path(self): return os.path.join(self._build_dir(), 'setup.py' ) def _setup_template_path(self): return os.path.join(Config().template_dir(), 'setup.tmpl' ) def _main_path(self): return os.path.join(self._build_dir(), 'mi/main.py' ) def _main_template_path(self): return os.path.join(Config().template_dir(), 'main.tmpl' ) def _build_name(self): return "%s_%s_%s_%s" % ( self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, self.metadata.version, ) def _build_dir(self): if self._bdir: return self._bdir self._bdir = self._generate_build_dir() log.info( "egg build dir: %s" % self._bdir) return self._bdir def _generate_build_dir(self): original_dir = os.path.join(self._tmp_dir(), self._build_name()) build_dir = original_dir build_count = 1 # Find a directory that doesn't exist while os.path.exists(build_dir): build_dir = "%s.%03d" % (original_dir, build_count) log.debug("build dir test: %s" % build_dir) build_count += 1 return build_dir def _stage_files(self, files): if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) for file in files: dest = os.path.join(self._build_dir(), file) destdir = dirname(dest) source = os.path.join(self._repo_dir(), file) log.debug(" Copy %s => %s" % (source, dest)) if not os.path.exists(destdir): os.makedirs(destdir) shutil.copy(source, dest) def _get_template(self, template_file): """ @brief return a string.Template object constructed with the contents of template_file @param template_file path to a file that containes a template @retval string.Template object """ try: infile = open(template_file) tmpl_str = infile.read() return Template(tmpl_str) except IOError: raise MissingTemplate(msg="Missing: %s" % template_file) def _generate_setup_file(self): if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) setup_file = self._setup_path() setup_template = self._get_template(self._setup_template_path()) log.debug("Create setup.py file: %s" % setup_file ) log.debug("setup.py template file: %s" % self._setup_template_path()) log.debug("setup.py template date: %s" % self._setup_template_data()) ofile = open(setup_file, 'w') code = setup_template.substitute(self._setup_template_data()) ofile.write(code) ofile.close() def _setup_template_data(self): name = "%s_%s_%s" % (self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name) return { 'name': name, 'version': self.metadata.version, 'description': 'ooi core driver', 'author': self.metadata.author, 'email': self.metadata.email, 'url': 'http://www.oceanobservatories.org', 'driver_module': self._driver_module(), 'driver_class': self._driver_class(), } def _generate_main_file(self): if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) main_file= self._main_path() main_template = self._get_template(self._main_template_path()) log.debug("Create mi/main.py file: %s" % main_file ) log.debug("main.py template file: %s" % self._main_template_path()) log.debug("main.py template date: %s" % self._setup_template_data()) ofile = open(main_file, 'w') code = main_template.substitute(self._setup_template_data()) ofile.write(code) ofile.close() def _verify_ready(self): """ verify that we have all the information and are in the correct state to build the egg """ self._verify_python() self._verify_metadata() self._verify_version() self._verify_git() def _verify_metadata(self): """ Ensure we have all the metadata we need to build the egg """ pass def _verify_version(self, version = None): """ Ensure we have a good version number and that it has not already been packaged and published """ if version == None: version = self.metadata.version if not version: raise ValidationFailure("Driver version required in metadata") p = re.compile("^\d+\.\d+\.\d+$") if not p.findall("%s" % version): raise ValidationFailure("Version format incorrect '%s', should be x.x.x" % version) #TODO Add check to see if there is already the same version package built def _verify_git(self): """ Ensure the repository is up to date. All files should be committed and pushed. The local repo should be in sync with the mainline """ #TODO Update this check pass def _verify_python(self): """ Ensure we build with the correct python version """ if sys.version_info < (2, 7) or sys.version_info >= (2, 8): raise ValidationFailure("Egg generation required version 2.7 of python") def _build_egg(self, files): try: self._verify_ready() self._stage_files(files) self._generate_setup_file() self._generate_main_file() cmd = "cd %s; python setup.py bdist_egg" % self._build_dir() log.info("CMD: %s" % cmd) os.system(cmd) egg_file = "%s/dist/%s_%s_%s-%s-py2.7.egg" % (self._build_dir(), self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, self.metadata.version) except ValidationFailure, e: log.error("Failed egg verification: %s" % e ) return None log.debug("Egg file created:: %s" % egg_file) return egg_file
class PackageDriver(object): """ Main class for running the package driver process. """ ### # Configuration ### def log_file(self): return "qualification.log" def log_path(self): return "%s/%s" % (self.metadata.idk_dir(), self.log_file()) def archive_file(self): return "%s_%s_%s-%s-driver.zip" % (self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, self.metadata.version) def archive_path(self): return os.path.join(os.path.expanduser("~"),self.archive_file()) ### # Public Methods ### def __init__(self): """ @brief ctor """ self.metadata = Metadata() self._zipfile = None self._manifest = None self._compression = None self.generator = DriverGenerator(self.metadata) # Set compression level self.zipfile_compression() def run_qualification_tests(self): """ @brief Run all qualification tests for the driver and store the results for packaging """ log.info("-- Running qualification tests") test = NoseTest(self.metadata, log_file=self.log_path()) test.report_header() result = test.run_qualification() if(test.run_qualification()): log.info(" ++ Qualification tests passed") return True else: log.error("Qualification tests have fail! No package created.") return False def package_driver(self): """ @brief Store driver files in a zip package """ log.info("-- Building driver package") self._store_package_files() def run(self): print "*** Starting Driver Packaging Process***" if( self.run_qualification_tests() ): self.package_driver() print "Package Created: " + self.archive_path() else: sys.exit() def zipfile(self): """ @brief Return the ZipFile object. Create the file if it isn't already open @retval ZipFile object """ if(not self._zipfile): self._zipfile = zipfile.ZipFile(self.archive_path(), mode="w") return self._zipfile def zipfile_compression(self): """ @brief What type of compression should we use for the package file. If we have access to zlib, we will compress @retval Compression type """ if(self._compression): return self._compression try: import zlib self._compression = zipfile.ZIP_DEFLATED log.info("Setting compression level to deflated") except: log.info("Setting compression level to store only") self._compression = zipfile.ZIP_STORED def manifest(self): """ @brief Return the PackageManifest object. Create it if it doesn't already exist @retval PackageManifest object """ if(not self._manifest): self._manifest = PackageManifest(self.metadata) return self._manifest ### # Private Methods ### def _store_package_files(self): """ @brief Store all files in zip archive and add them to the manifest file """ egg_generator = EggGenerator(self.metadata) egg_file = egg_generator.save() # Add egg self._add_file(egg_file, 'egg', 'python driver egg package') # Add the package metadata file self._add_file(self.metadata.metadata_path(), description = 'package metadata') # Add the qualification test log self._add_file(self.log_path(), description = 'qualification tests results') # Store additional resource files self._store_resource_files() # Finally save the manifest file. This must be last of course self._add_file(self.manifest().manifest_path(), description = 'package manifest file') def _store_resource_files(self): """ @brief Store additional files added by the driver developer. These files life in the driver resource dir. """ log.debug( " -- Searching for developer added resource files." ) for file in os.listdir(self.generator.resource_dir()): log.debug(" ++ found: " + file) desc = prompt.text( 'Describe ' + file ) self._add_file(self.generator.resource_dir() + "/" + file, 'resource', desc) def _add_file(self, source, destdir=None, description=None): """ @brief Add a file to the zip package and store the file in the manifest. """ filename = os.path.basename(source) dest = filename if(destdir): dest = "%s/%s" % (destdir, filename) log.debug( "archive %s to %s" % (filename, dest) ) self.manifest().add_file(dest, description); self.zipfile().write(source, dest, self.zipfile_compression())
class EggGenerator: """ Generate driver egg """ def __init__(self, metadata): """ @brief Constructor @param metadata IDK Metadata object """ self.metadata = metadata self._bdir = None if not self._tmp_dir(): raise InvalidParameters("missing tmp_dir configuration") if not self._tmp_dir(): raise InvalidParameters("missing working_repo configuration") self.generator = DriverGenerator(self.metadata) test_import = __import__(self._test_module()) def _test_module(self): return self.generator.test_modulename() def _driver_module(self): test_config = InstrumentDriverTestConfig() return test_config.driver_module def _driver_class(self): test_config = InstrumentDriverTestConfig() return test_config.driver_class def _repo_dir(self): return '/tmp/repoclone/marine-integrations' def _res_dir(self): return os.path.join(self._versioned_dir(), 'res') def _res_config_dir(self): return os.path.join(self._res_dir(), 'config') def _tmp_dir(self): return Config().get('tmp_dir') def _setup_path(self): return os.path.join(self._build_dir(), 'setup.py') def _setup_template_path(self): return os.path.join(Config().template_dir(), 'setup.tmpl') def _main_path(self): return os.path.join(self._versioned_dir(), 'mi/main.py') def _main_template_path(self): return os.path.join(Config().template_dir(), 'main.tmpl') def _build_name(self): return "%s_%s_%s_%s" % ( self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, self.metadata.version.replace('.', '_'), ) def _build_dir(self): if self._bdir: return self._bdir self._bdir = self._generate_build_dir() log.info("egg build dir: %s" % self._bdir) return self._bdir def _generate_build_dir(self): build_dir = os.path.join(self._tmp_dir(), self._build_name()) # clean out an old build if it exists if os.path.exists(build_dir): shutil.rmtree(build_dir) return build_dir def _versioned_dir(self): return self._build_dir() # leave in plumbing for building namespaced eggs #return os.path.join(self._build_dir(), # self._build_name()) def _stage_files(self, files): """ Copy files from the original directory into two levels of versioned directories within a staging directory, and replace the mi namespace with the versioned driver name.mi to account for the new directory (only the lower versioned dir is included in the egg) @param files - a list of files to copy into the staging directory """ # make two levels of versioned file directories, i.e. # driverA_0_1 (= build_dir) # driverA_0_1 (= versioned_dir) # then copy driverA files into the bottom versioned dir if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) if not os.path.exists(self._versioned_dir()): os.makedirs(self._versioned_dir()) for file in files: dest = os.path.join(self._versioned_dir(), file) destdir = dirname(dest) source = os.path.join(self._repo_dir(), file) # this one goes elsewhere so the InstrumentDict can find it if basename(file) == 'strings.yml': dest = os.path.join(self._res_dir(), basename(file)) destdir = dirname(dest) log.debug(" Copy %s => %s" % (source, dest)) # make sure the destination directory exists, if it doesn't make it if not os.path.exists(destdir): os.makedirs(destdir) shutil.copy(source, dest) # replace mi in the copied files with the versioned driver module.mi # this is necessary because the top namespace in the versioned files starts # with the versioned driver name directory, not mi #driver_file = open(dest, "r") #contents = driver_file.read() #driver_file.close() #new_contents = re.sub(r'(^import |^from |\'|= )mi\.|res/config/mi-logging|\'mi\'', # self._mi_replace, # contents, # count=0, # flags=re.MULTILINE) #driver_file = open(dest, "w") #driver_file.write(new_contents) #driver_file.close() # need to add mi-logging.yml special because it is not in cloned repo, only in local repository milog = "mi-logging.yml" dest = os.path.join(self._res_config_dir(), milog) destdir = dirname(dest) source = os.path.join(Config().base_dir(), "res/config/" + milog) log.debug(" Copy %s => %s" % (source, dest)) # make sure the destination directory exists, if it doesn't make it if not os.path.exists(destdir): os.makedirs(destdir) # Now that it exists, make the package scanner find it self._create_file(os.path.join(self._res_dir(), "__init__.py")) self._create_file(os.path.join(self._res_config_dir(), "__init__.py")) shutil.copy(source, dest) # we need to make sure an init file is in the versioned dir and # resource directories so that find_packages() will look in here init_file_list = [ os.path.join(self._versioned_dir(), "__init__.py"), os.path.join(self._versioned_dir(), "res", "__init__.py"), os.path.join(self._versioned_dir(), "res", "config", "__init__.py") ] for file in init_file_list: self._create_file(file) @staticmethod def _create_file(file): """ Create a file if it isnt there already """ if not os.path.exists(file): init_file = open(file, "w") init_file.close() def _mi_replace(self, matchobj): """ This function is used in regex sub to replace mi with the versioned driver name followed by mi @param matchobj - the match object from re.sub """ if matchobj.group(0) == 'res/config/mi-logging': return self._build_name() + '/' + matchobj.group(0) elif matchobj.group(0) == '\'mi\'': return '\'' + self._build_name() + '.mi\'' else: return matchobj.group(1) + self._build_name() + '.mi.' def _get_template(self, template_file): """ @brief return a string.Template object constructed with the contents of template_file @param template_file path to a file that containes a template @retval string.Template object """ try: infile = open(template_file) tmpl_str = infile.read() return Template(tmpl_str) except IOError: raise MissingTemplate(msg="Missing: %s" % template_file) def _generate_setup_file(self): if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) if not os.path.exists(self._build_dir()): raise IDKException("failed to create build dir: %s" % self._build_dir()) setup_file = self._setup_path() setup_template = self._get_template(self._setup_template_path()) log.debug("Create setup.py file: %s", setup_file) log.debug("setup.py template file: %s", self._setup_template_path()) log.debug("setup.py template date: %s", self._setup_template_data()) log.debug("setup.py template: %s", setup_template) ofile = open(setup_file, 'w') code = setup_template.substitute(self._setup_template_data()) log.debug("CODE: %s", code) ofile.write(code) ofile.close() def _setup_template_data(self): return { 'name': self._build_name(), 'version': self.metadata.version, 'description': 'ooi core driver', 'author': self.metadata.author, 'email': self.metadata.email, 'url': 'http://www.oceanobservatories.org', 'driver_module': self._driver_module(), 'driver_class': self._driver_class(), 'driver_path': self.metadata.relative_driver_path(), 'short_name': self.metadata.relative_driver_path().replace('/', '_') } def _generate_main_file(self): if not os.path.exists(self._versioned_dir()): os.makedirs(self._versioned_dir()) main_file = self._main_path() main_template = self._get_template(self._main_template_path()) log.debug("Create mi/main.py file: %s" % main_file) log.debug("main.py template file: %s" % self._main_template_path()) log.debug("main.py template date: %s" % self._setup_template_data()) ofile = open(main_file, 'w') code = main_template.substitute(self._setup_template_data()) ofile.write(code) ofile.close() def _verify_ready(self): """ verify that we have all the information and are in the correct state to build the egg """ self._verify_python() self._verify_metadata() self._verify_version() def _verify_metadata(self): """ Ensure we have all the metadata we need to build the egg """ pass def _verify_version(self, version=None): """ Ensure we have a good version number and that it has not already been packaged and published """ if version == None: version = self.metadata.version if not version: raise ValidationFailure("Driver version required in metadata") p = re.compile("^\d+\.\d+\.\d+$") if not p.findall("%s" % version): raise ValidationFailure( "Version format incorrect '%s', should be x.x.x" % version) def _verify_python(self): """ Ensure we build with the correct python version """ if sys.version_info < (2, 7) or sys.version_info >= (2, 8): raise ValidationFailure( "Egg generation required version 2.7 of python") def _build_egg(self, files): try: self._verify_ready() self._stage_files(files) self._generate_setup_file() self._generate_main_file() cmd = "cd %s; python setup.py bdist_egg" % self._build_dir() log.info("CMD: %s" % cmd) os.system(cmd) egg_file = "%s/dist/%s-%s-py2.7.egg" % (self._build_dir( ), self.metadata.relative_driver_path().replace( '/', '_'), self.metadata.version) # Remove all pyc files from the egg. There was a jira case that suggested # including the compiled py files caused the drivers to run slower. # https://jira.oceanobservatories.org/tasks/browse/OOIION-1167 cmd = "zip %s -d \*.pyc" % egg_file log.info("CMD: %s" % cmd) os.system(cmd) except ValidationFailure, e: log.error("Failed egg verification: %s" % e) return None log.debug("Egg file created: %s" % egg_file) return egg_file
class PackageDriver(object): """ Main class for running the package driver process. """ ### # Configuration ### def string_file(self): return "strings.yml" def log_file(self): return "qualification.log" def log_path(self): return "%s/%s" % (self.metadata.idk_dir(), self.log_file()) def build_name(self): return "%s_%s_%s" % (self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name) def archive_file(self): return "%s-%s-driver.zip" % (self.build_name(), self.metadata.version) def archive_path(self): return os.path.join(os.path.expanduser("~"),self.archive_file()) ### # Public Methods ### def __init__(self): """ @brief ctor """ self._zipfile = None self._manifest = None self._compression = None # Set compression level self.zipfile_compression() def run_qualification_tests(self): """ @brief Run all qualification tests for the driver and store the results for packaging """ log.info("-- Running qualification tests") test = NoseTest(self.metadata, log_file=self.log_path()) test.report_header() if(test.run_qualification()): log.info(" ++ Qualification tests passed") return True else: log.error("Qualification tests have fail! No package created.") return False def clone_repo(self): """ clone the ooici repository into a temp location and navigate to it """ # make a temp dir to put the clone in if not os.path.exists(REPODIR): os.mkdir(REPODIR) os.chdir(REPODIR) # remove an old clone if one exists, start clean if os.path.exists(REPODIR + '/marine-integrations'): shutil.rmtree(REPODIR + '/marine-integrations') # clone the ooici repository into a temporary location log.debug('Attempting to clone repository into %s, REPODIR set to %s', os.getcwd(), REPODIR) ret = os.system('git clone [email protected]:ooici/marine-integrations.git') if ret < 0: raise GitCommandException("Bad return from git command") # if the directory doesn't exist, something went wrong with cloning if not os.path.exists(REPODIR + '/marine-integrations'): raise GitCommandException('Error creating ooici repository clone with base: %s' % REPODIR) # navigate into the cloned repository os.chdir(REPODIR + '/marine-integrations') log.debug('in cloned repository') def get_repackage_version(self, tag_base): """ Get the driver version the user wants to repackage """ # suggest the current driver version as default repkg_version = prompt.text( 'Driver Version to re-package', self.metadata.version ) # confirm this version has the correct format self._verify_version(repkg_version) # check to make sure this driver version exists tag_name = 'release_' + tag_base + '_' + repkg_version.replace('.', '_') cmd = 'git tag -l ' + tag_name # find out if this tag name exists output = subprocess.check_output(cmd, shell=True) if len(output) > 0: # this tag exists, check it out os.system('git checkout tags/' + tag_name) else: log.error('No driver version %s found', tag_name) raise InvalidParameters('No driver version %s found', tag_name) def make_branch(self, base_name): """ Make a new branch for this release and tag it with the same name so we can get back to it @param base_name - the base name for this instrument used to make the branch and tag names. The base should have the form: '<driver_name>_<driver_version>', where the version has the format X_X_X. This is equal to the branch name, and the tag will have 'release_' prepended to the base name. """ # create a new branch name and check it out cmd = 'git checkout -b ' + base_name output = subprocess.check_output(cmd, shell=True) log.debug('created new branch %s: %s', base_name, output) # tag the initial branch so that we can get back to it later cmd = 'git tag ' + 'release_' + base_name output = subprocess.check_output(cmd, shell=True) log.debug('created new tag %s: %s', 'release_' + base_name, output) def update_version(self): """ Update the driver version for this package. By default increment by one. After updating the metadata file, commit the change to git. """ last_dot = self.metadata.version.rfind('.') last_version = int(self.metadata.version[last_dot+1:]) suggest_version = self.metadata.version[:last_dot+1] + str(last_version + 1) new_version = prompt.text('Update Driver Version', suggest_version ) # confirm this version has the correct format self._verify_version(new_version) if new_version != self.metadata.version: # search for the tag for this version, find out if it already exists cmd = 'git tag -l ' + 'release_' + self.build_name() + '_' + new_version.replace('.', '_') # find out if this tag name exists output = subprocess.check_output(cmd, shell=True) if len(output) > 0: # this tag already exists and we are not repackaging raise InvalidParameters("Version %s already exists. To repackage, run package driver with the --repackage option", new_version) # set the new driver version in the metadata self.metadata.set_driver_version(new_version) # commit the changed file to git cmd = 'git commit ' + str(self.metadata.metadata_path()) + ' -m \'Updated metadata driver version\'' os.system(cmd) return new_version def package_driver(self): """ @brief Store driver files in a zip package """ log.info("-- Building driver package") self._store_package_files() def run(self): print "*** Starting Driver Packaging Process***" # store the original directory since we will be navigating away from it original_dir = os.getcwd() # first create a temporary clone of ooici to work with self.clone_repo() # get which dataset agent is selected from the current metadata, use # this to get metadata from the cloned repo tmp_metadata = Metadata() # read metadata from the cloned repo self.metadata = Metadata(tmp_metadata.driver_make, tmp_metadata.driver_model, tmp_metadata.driver_name, REPODIR + '/marine-integrations') if "--repackage" in sys.argv: self.get_repackage_version(self.build_name()) else: new_version = self.update_version() base_name = self.build_name() + '_' + new_version.replace('.', '_') self.make_branch(base_name) if "--no-test" in sys.argv: f = open(self.log_path(), "w") f.write("Tests manually bypassed with --no-test option\n") f.close() self.package_driver() else: if(self.run_qualification_tests()): self.package_driver() if not "--no-push" in sys.argv and not "--repackage" in sys.argv: cmd = 'git push' output = subprocess.check_output(cmd, shell=True) if len(output) > 0: log.debug('git push returned: %s', output) # go back to the original directory os.chdir(original_dir) print "Package Created: " + self.archive_path() def zipfile(self): """ @brief Return the ZipFile object. Create the file if it isn't already open @retval ZipFile object """ if(not self._zipfile): self._zipfile = zipfile.ZipFile(self.archive_path(), mode="w") return self._zipfile def zipfile_compression(self): """ @brief What type of compression should we use for the package file. If we have access to zlib, we will compress @retval Compression type """ if(self._compression): return self._compression try: import zlib self._compression = zipfile.ZIP_DEFLATED log.info("Setting compression level to deflated") except: log.info("Setting compression level to store only") self._compression = zipfile.ZIP_STORED def manifest(self): """ @brief Return the PackageManifest object. Create it if it doesn't already exist @retval PackageManifest object """ if(not self._manifest): self._manifest = PackageManifest(self.metadata) return self._manifest ### # Private Methods ### def _store_package_files(self): """ @brief Store all files in zip archive and add them to the manifest file """ # make sure metadata is up to date self.metadata = Metadata(self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, REPODIR + '/marine-integrations') self.generator = DriverGenerator(self.metadata) egg_generator = EggGenerator(self.metadata) egg_file = egg_generator.save() # Add egg self._add_file(egg_file, 'egg', 'python driver egg package') # Add the package metadata file self._add_file(self.metadata.metadata_path(), description = 'package metadata') # Add the qualification test log self._add_file(self.log_path(), description = 'qualification tests results') # Store parameter/command string description file str_path = "%s/%s" % (self.generator.resource_dir(), self.string_file()) if os.path.exists(str_path): self._add_file(str_path, 'resource', 'driver string file') # Store additional resource files self._store_resource_files() # Finally save the manifest file. This must be last of course self._add_file(self.manifest().manifest_path(), description = 'package manifest file') def _store_resource_files(self): """ @brief Store additional files added by the driver developer. These files live in the driver resource dir. """ resource_dir = os.path.join(self.metadata.relative_driver_path(), "resource") log.debug(" -- Searching for developer added resource files in dir: %s", resource_dir) stringfile = self.string_file() if os.path.exists(resource_dir): for file in os.listdir(resource_dir): if file != stringfile: log.debug(" ++ found: " + file) desc = prompt.text('Describe ' + file) self._add_file(resource_dir + "/" + file, 'resource', desc) else: log.debug(" --- No resource directory found, skipping...") def _add_file(self, source, destdir=None, description=None): """ @brief Add a file to the zip package and store the file in the manifest. """ filename = os.path.basename(source) dest = filename if(destdir): dest = "%s/%s" % (destdir, filename) log.debug("archive %s to %s" % (filename, dest)) self.manifest().add_file(dest, description); self.zipfile().write(source, dest, self.zipfile_compression()) def _verify_version(self, version = None): """ Ensure we have a good version number and that it has not already been packaged and published """ if version == None: version = self.metadata.version if not version: raise ValidationFailure("Driver version required in metadata") p = re.compile("^\d+\.\d+\.\d+$") if not p.findall("%s" % version): raise ValidationFailure("Version format incorrect '%s', should be x.x.x" % version)
class EggGenerator: """ Generate driver egg """ def __init__(self, metadata, repo_dir=REPODIR): """ @brief Constructor @param metadata IDK Metadata object """ self.metadata = metadata self._bdir = None self._repodir = repo_dir if not self._tmp_dir(): raise InvalidParameters("missing tmp_dir configuration") if not self._tmp_dir(): raise InvalidParameters("missing working_repo configuration") self.generator = DriverGenerator(self.metadata) test_import = __import__(self._test_module()) def _test_module(self): return self.generator.test_modulename() def _driver_module(self): test_config = InstrumentDriverTestConfig() return test_config.driver_module def _driver_class(self): test_config = InstrumentDriverTestConfig() return test_config.driver_class def _repo_dir(self): return self._repodir def _res_dir(self): return os.path.join(self._versioned_dir(), 'res') def _res_config_dir(self): return os.path.join(self._res_dir(), 'config' ) def _tmp_dir(self): return Config().get('tmp_dir') def _setup_path(self): return os.path.join(self._build_dir(), 'setup.py' ) def _setup_template_path(self): return os.path.join(Config().template_dir(), 'setup.tmpl' ) def _main_path(self): return os.path.join(self._versioned_dir(), 'mi/main.py' ) def _main_template_path(self): return os.path.join(Config().template_dir(), 'main.tmpl' ) def _build_name(self): return "%s_%s_%s_%s" % ( self.metadata.driver_make, self.metadata.driver_model, self.metadata.driver_name, self.metadata.version.replace('.', '_'), ) def _build_dir(self): if self._bdir: return self._bdir self._bdir = self._generate_build_dir() log.info( "egg build dir: %s" % self._bdir) return self._bdir def _generate_build_dir(self): build_dir = os.path.join(self._tmp_dir(), self._build_name()) # clean out an old build if it exists if os.path.exists(build_dir): shutil.rmtree(build_dir) return build_dir def _versioned_dir(self): return self._build_dir() #return os.path.join(self._build_dir(), # self._build_name()) def _stage_files(self, files): """ Copy files from the original directory into two levels of versioned directories within a staging directory, and replace the mi namespace with the versioned driver name.mi to account for the new directory (only the lower versioned dir is included in the egg) @param files - a list of files to copy into the staging directory """ log.error(repr(files)) # make two levels of versioned file directories, i.e. # driverA_0_1 (= build_dir) # driverA_0_1 (= versioned_dir) # then copy driverA files into the bottom versioned dir if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) if not os.path.exists(self._versioned_dir()): os.makedirs(self._versioned_dir()) for file in files: if file not in ['res/config/__init__.py', 'res/__init__.py']: dest = os.path.join(self._versioned_dir(), file) destdir = dirname(dest) source = os.path.join(self._repo_dir(), file) # this one goes elsewhere so the InstrumentDict can find it if basename(file) == 'strings.yml': dest = os.path.join(self._res_dir(), basename(file)) destdir = dirname(dest) log.debug(" Copy %s => %s" % (source, dest)) # make sure the destination directory exists, if it doesn't make it if not os.path.exists(destdir): os.makedirs(destdir) shutil.copy(source, dest) # replace mi in the copied files with the versioned driver module.mi # this is necessary because the top namespace in the versioned files starts # with the versioned driver name directory, not mi #driver_file = open(dest, "r") #contents = driver_file.read() #driver_file.close() #new_contents = re.sub(r'(^import |^from |\'|= )mi\.|res/config/mi-logging|\'mi\'', # self._mi_replace, # contents, # count=0, # flags=re.MULTILINE) #driver_file = open(dest, "w") #driver_file.write(new_contents) #driver_file.close() # need to add mi-logging.yml special because it is not in cloned repo, only in local repository milog = "mi-logging.yml" dest = os.path.join(self._res_config_dir(), milog) destdir = dirname(dest) source = os.path.join(Config().base_dir(), "res/config/" + milog) log.debug(" Copy %s => %s" % (source, dest)) # make sure the destination directory exists, if it doesn't make it if not os.path.exists(destdir): os.makedirs(destdir) shutil.copy(source, dest) # we need to make sure an init file is in the versioned dir and # resource directories so that find_packages() will look in here init_file_list = [os.path.join(self._versioned_dir(), "__init__.py"), os.path.join(self._versioned_dir(), "res", "__init__.py"), os.path.join(self._versioned_dir(), "res", "config", "__init__.py")] for file in init_file_list: self._create_file(file) @staticmethod def _create_file(file): """ Create a file if it isnt there already """ if not os.path.exists(file): init_file = open(file, "w") init_file.close() def _mi_replace(self, matchobj): """ This function is used in regex sub to replace mi with the versioned driver name followed by mi @param matchobj - the match object from re.sub """ if matchobj.group(0) == 'res/config/mi-logging': return self._build_name() + '/' + matchobj.group(0) elif matchobj.group(0) == '\'mi\'': return '\'' + self._build_name() + '.mi\'' else: return matchobj.group(1) + self._build_name() + '.mi.' def _get_template(self, template_file): """ @brief return a string.Template object constructed with the contents of template_file @param template_file path to a file that containes a template @retval string.Template object """ try: infile = open(template_file) tmpl_str = infile.read() return Template(tmpl_str) except IOError: raise MissingTemplate(msg="Missing: %s" % template_file) def _generate_setup_file(self): if not os.path.exists(self._build_dir()): os.makedirs(self._build_dir()) if not os.path.exists(self._build_dir()): raise IDKException("failed to create build dir: %s" % self._build_dir()) setup_file = self._setup_path() setup_template = self._get_template(self._setup_template_path()) log.debug("Create setup.py file: %s", setup_file ) log.debug("setup.py template file: %s", self._setup_template_path()) log.debug("setup.py template date: %s", self._setup_template_data()) log.debug("setup.py template: %s", setup_template) ofile = open(setup_file, 'w') code = setup_template.substitute(self._setup_template_data()) log.debug("CODE: %s", code) ofile.write(code) ofile.close() def _setup_template_data(self): return { 'name': self._build_name(), 'version': self.metadata.version, 'description': 'ooi core driver', 'author': self.metadata.author, 'email': self.metadata.email, 'url': 'http://www.oceanobservatories.org', 'driver_module': self._driver_module(), 'driver_class': self._driver_class(), 'driver_path': self.metadata.relative_driver_path(), 'short_name': self.metadata.relative_driver_path().replace('/', '_') } def _generate_main_file(self): if not os.path.exists(self._versioned_dir()): os.makedirs(self._versioned_dir()) main_file= self._main_path() main_template = self._get_template(self._main_template_path()) log.debug("Create mi/main.py file: %s" % main_file ) log.debug("main.py template file: %s" % self._main_template_path()) log.debug("main.py template date: %s" % self._setup_template_data()) ofile = open(main_file, 'w') code = main_template.substitute(self._setup_template_data()) ofile.write(code) ofile.close() def _verify_ready(self): """ verify that we have all the information and are in the correct state to build the egg """ self._verify_python() self._verify_metadata() self._verify_version() def _verify_metadata(self): """ Ensure we have all the metadata we need to build the egg """ pass def _verify_version(self, version = None): """ Ensure we have a good version number and that it has not already been packaged and published """ if version == None: version = self.metadata.version if not version: raise ValidationFailure("Driver version required in metadata") p = re.compile("^\d+\.\d+\.\d+$") if not p.findall("%s" % version): raise ValidationFailure("Version format incorrect '%s', should be x.x.x" % version) def _verify_python(self): """ Ensure we build with the correct python version """ if sys.version_info < (2, 7) or sys.version_info >= (2, 8): raise ValidationFailure("Egg generation required version 2.7 of python") def _build_egg(self, files): try: self._verify_ready() self._stage_files(files) self._generate_setup_file() self._generate_main_file() cmd = "cd %s; python setup.py bdist_egg" % self._build_dir() log.info("CMD: %s" % cmd) os.system(cmd) egg_file = "%s/dist/%s-%s-py2.7.egg" % (self._build_dir(), self.metadata.relative_driver_path().replace('/', '_'), self.metadata.version) # Remove all pyc files from the egg. There was a jira case that suggested # including the compiled py files caused the drivers to run slower. # https://jira.oceanobservatories.org/tasks/browse/OOIION-1167 cmd = "zip %s -d \*.pyc" % egg_file log.info("CMD: %s" % cmd) os.system(cmd) except ValidationFailure, e: log.error("Failed egg verification: %s" % e ) return None log.debug("Egg file created: %s" % egg_file) return egg_file
def _data_test_module(self): generator = DriverGenerator(self.metadata) return generator.data_test_modulename()
def generate_code(self, force = False): """ @brief generate the directory structure, code and tests for the new driver. """ driver = DriverGenerator( self.metadata, force = force ) driver.generate()
def get_driver_generator(self): return DriverGenerator(self.metadata)