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()
Beispiel #8
0
    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):
        """
        @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 _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()
Beispiel #12
0
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
Beispiel #18
0
 def _driver_test_filename(self):
     generator = DriverGenerator(self.metadata)
     return generator.driver_test_path()
Beispiel #19
0
 def _data_test_module(self):
     generator = DriverGenerator(self.metadata)
     return generator.data_test_modulename()
Beispiel #20
0
 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()
Beispiel #21
0
 def get_driver_generator(self):
     return DriverGenerator(self.metadata)