Exemple #1
0
    def test(self):
        tmpdir = tempfile.mkdtemp(prefix='laymantmp_')
        makeconf = os.path.join(tmpdir, 'make.conf')
        reposconf = os.path.join(tmpdir, 'repos.conf')

        make_txt =\
        'PORTDIR_OVERLAY="\n'\
        '$PORTDIR_OVERLAY"'

        # Create the .conf files so layman doesn't
        # complain.
        with fileopen(makeconf, 'w') as f:
            f.write(make_txt)

        with fileopen(reposconf, 'w') as f:
            f.write('')

        my_opts = {
            'installed': HERE + '/testfiles/global-overlays.xml',
            'make_conf': makeconf,
            'nocheck': 'yes',
            'storage': tmpdir,
            'repos_conf': reposconf,
            'conf_type': ['make.conf', 'repos.conf'],
        }

        config = OptionConfig(my_opts)
        config.set_option('quietness', 3)

        a = DB(config)
        config['output'].set_colorize(False)

        conf = RepoConfManager(config, a.overlays)

        # Set up our success tracker.
        success = []

        # Add all the overlays in global_overlays.xml.
        for overlay in a.overlays.keys():
            conf_success = conf.add(a.overlays[overlay])
            if conf_success == False:
                success.append(False)
            else:
                success.append(True)

        # Disable one overlay.
        self.assertTrue(conf.disable(a.overlays['wrobel']))

        # Enable disabled overlay.
        self.assertTrue(conf.enable(a.overlays['wrobel']))

        # Delete all the overlays in global_overlays.xml.
        for overlay in a.overlays.keys():
            self.assertTrue(conf.delete(a.overlays[overlay]))

        # Clean up.
        os.unlink(makeconf)
        os.unlink(reposconf)

        shutil.rmtree(tmpdir)
Exemple #2
0
    def __call__(self):
        self.args_parser()
        options = None
        if self.args.config:
            options = {
                'config': self.args.config,
            }

        self.config = OptionConfig(options=options, root=self.root)
        # fix the config path
        defaults = self.config.get_defaults()
        defaults['config'] = defaults['config'] \
            % {'configdir': defaults['configdir']}
        self.config.update_defaults({'config': defaults['config']})

        self.config.read_config(defaults)

        layman_inst = LaymanAPI(config=self.config)

        self.output = layman_inst.output

        if self.args.setup_help:
            self.print_instructions()
        elif not self.check_is_new(self.args.rebuild):
            self.rename_check()
        if self.args.migrate_db:
            self.migrate_database(self.args.migrate_db)
Exemple #3
0
    def __call__(self, overlay_package=None, path=None):

        if not overlay_package:
            self.args_parser()
            options = {}
            for key in vars(self.args):
                options[key] = vars(self.args)[key]
            self.config = OptionConfig(options=options)
            reload_config(self.config)

            self.auto_complete = False
            self.list_info = self.config.get_option('list_autocomplete')
            self.no_extra = self.config.get_option('no_extra')
            self.sudo = self.config.get_option('sudo')

            if self.list_info:
                self.list_templates()

            if self.args.set_autocomplete:
                if 'ALL' in self.args.set_autocomplete:
                    self.templates = AUTOCOMPLETE_TEMPLATE.keys()
                    self.auto_complete = True
                else:
                    self.templates = self.args.set_autocomplete
                    self.auto_complete = True

            msg = 'How many overlays would you like to create?: '
            for x in range(1, int(get_input(msg)) + 1):
                self.output.notice('')
                self.output.info('Overlay #%(x)s: ' % ({'x': str(x)}))
                self.output.info('~~~~~~~~~~~~~')

                self.required = copy.deepcopy(COMPONENT_DEFAULTS)

                if not self.no_extra:
                    self.output.notice('')
                    self.update_required()
                    self.output.notice('')

                self.get_overlay_components()
                ovl = Overlay.Overlay(config=self.config,
                                      ovl_dict=self.overlay,
                                      ignore=1)
                self.overlays.append((self.overlay['name'], ovl))
        else:
            ovl_name, ovl = overlay_package
            self.overlays.append((ovl_name, ovl))

        result = self.write(path)
        return result
Exemple #4
0
    def test(self):
        tmpdir = tempfile.mkdtemp(prefix='laymantmp_')
        cache = os.path.join(tmpdir, 'cache')
        my_opts = {
            'overlays': ['file://' + HERE + '/testfiles/global-overlays.xml'],
            'cache': cache,
            'nocheck': 'yes',
            'proxy': None
        }
        config = OptionConfig(my_opts)
        db = RemoteDB(config)
        self.assertEqual(db.cache(), (True, True))

        db_xml = fileopen(db.filepath(config['overlays']) + '.xml')

        test_line = '      A collection of ebuilds from Gunnar Wrobel '\
                    '[[email protected]].\n'
        self.assertEqual(db_xml.readlines()[19], test_line)

        for line in db_xml.readlines():
            print(line, end='')

        db_xml.close()
        keys = sorted(db.overlays)
        self.assertEqual(keys, ['wrobel', 'wrobel-stable'])

        shutil.rmtree(tmpdir)
Exemple #5
0
    def __call__(self):
        self.args_parser()
        options = None
        if self.args.config:
            options = {
                'config': self.args.config,
            }

        self.config = OptionConfig(options=options, root=self.root)
        # fix the config path
        defaults = self.config.get_defaults()
        defaults['config'] = defaults['config'] \
            % {'configdir': defaults['configdir']}
        self.config.update_defaults({'config': defaults['config']})

        self.config.read_config(defaults)

        layman_inst = LaymanAPI(config=self.config)

        self.output = layman_inst.output

        if self.args.setup_help:
            self.print_instructions()
        elif not self.check_is_new():
            self.rename_check()
Exemple #6
0
    def _get_layman_api(self):
        '''
        Initializes layman api.

        @rtype layman.api.LaymanAPI instance
        '''
        # Make it so that we aren't initializing the
        # LaymanAPI instance if it already exists and
        # if the current storage location hasn't been
        # changed for the new repository.
        self.storage = self.repo.location.replace(self.repo.name, '')

        if self._layman and self.storage in self.current_storage:
            return self._layman

        config = BareConfig()
        configdir = {'configdir': config.get_option('configdir')}

        self.message = Message(out=sys.stdout, err=sys.stderr)
        self.current_storage = self.storage
        options = {
            'config': config.get_option('config') % (configdir),
            'quiet': self.settings.get('PORTAGE_QUIET'),
            'quietness': config.get_option('quietness'),
            'overlay_defs': config.get_option('overlay_defs') % (configdir),
            'output': self.message,
            'nocolor': self.settings.get('NOCOLOR'),
            'root': self.settings.get('EROOT'),
            'storage': self.current_storage,
            'verbose': self.settings.get('PORTAGE_VERBOSE'),
            'width': self.settings.get('COLUMNWIDTH'),
        }
        self.config = OptionConfig(options=options, root=options['root'])

        # Reloads config to read custom overlay
        # xml files.
        reload_config(self.config)

        layman_api = LaymanAPI(self.config,
                               report_errors=True,
                               output=self.config['output'])

        self._layman = layman_api

        return layman_api
Exemple #7
0
    def make_OptionConfig(self):
        my_opts = {
            'overlays': ["http://www.gentoo-overlays.org/repositories.xml"]
        }
        new_defaults = {'configdir': '/etc/test-dir'}

        a = OptionConfig(options=my_opts, defaults=new_defaults)

        # Test components of the OptionConfig class:
        assertEqual(a['overlays'], self.test_url)
        assertEqual(a['configdir'], my_opts['configdir'])
        assertEqual(sorted(a), self.test_keys)
Exemple #8
0
    def __call__(self, overlay_package=None, path=None):

        if not overlay_package:
            self.args_parser()
            options = {}
            for key in vars(self.args):
                options[key] = vars(self.args)[key]
            self.config = OptionConfig(options=options)
            reload_config(self.config)

            self.auto_complete = False
            self.list_info     = self.config.get_option('list_autocomplete')
            self.no_extra      = self.config.get_option('no_extra')
            self.sudo          = self.config.get_option('sudo')

            if self.list_info:
                self.list_templates()

            if self.args.set_autocomplete:
                if 'ALL' in self.args.set_autocomplete:
                    self.templates = AUTOCOMPLETE_TEMPLATE.keys()
                    self.auto_complete = True
                else:
                    self.templates = self.args.set_autocomplete
                    self.auto_complete = True

            msg = 'How many overlays would you like to create?: '
            for x in range(1, int(get_input(msg))+1):
                self.output.notice('')
                self.output.info('Overlay #%(x)s: ' % ({'x': str(x)}))
                self.output.info('~~~~~~~~~~~~~')

                self.required = copy.deepcopy(COMPONENT_DEFAULTS)

                if not self.no_extra:
                    self.output.notice('')
                    self.update_required()
                    self.output.notice('')

                self.get_overlay_components()
                ovl = Overlay.Overlay(config=self.config, ovl_dict=self.overlay, ignore=1)
                self.overlays.append((self.overlay['name'], ovl))
        else:
            ovl_name, ovl = overlay_package
            self.overlays.append((ovl_name, ovl))

        result = self.write(path)
        return result
Exemple #9
0
    def _get_layman_api(self):
        '''
        Initializes layman api.

        @rtype layman.api.LaymanAPI instance
        '''
        # Make it so that we aren't initializing the
        # LaymanAPI instance if it already exists and
        # if the current storage location hasn't been
        # changed for the new repository.
        self.storage = self.repo.location.replace(self.repo.name, '')

        if self._layman and self.storage in self.current_storage:
            return self._layman

        config = BareConfig()
        configdir = {'configdir': config.get_option('configdir')}

        self.message = Message(out=sys.stdout, err=sys.stderr)
        self.current_storage = self.storage
        options = {
            'config': config.get_option('config') % (configdir),
            'quiet': self.settings.get('PORTAGE_QUIET'),
            'quietness': config.get_option('quietness'),
            'overlay_defs': config.get_option('overlay_defs') % (configdir),
            'output': self.message,
            'nocolor': self.settings.get('NOCOLOR'),
            'root': self.settings.get('EROOT'),
            'storage': self.current_storage,
            'verbose': self.settings.get('PORTAGE_VERBOSE'),
            'width': self.settings.get('COLUMNWIDTH'),

        }
        self.config = OptionConfig(options=options, root=options['root'])

        # Reloads config to read custom overlay
        # xml files.
        reload_config(self.config)

        layman_api = LaymanAPI(self.config,
                               report_errors=True,
                               output=self.config['output']
                               )

        self._layman = layman_api

        return layman_api
Exemple #10
0
    def test(self):
        tmpdir = tempfile.mkdtemp(prefix='laymantmp_')
        cache = os.path.join(tmpdir, 'cache')

        my_opts = {
                   'overlays': ['file://'\
                                + HERE + '/testfiles/global-overlays.xml'],
                   'db_type': 'xml',
                   'cache': cache,
                   'nocheck': 'yes',
                   'proxy': None,
                   'quietness': 3
                  }

        config = OptionConfig(my_opts)

        api = LaymanAPI(config)
        self.assertTrue(api.fetch_remote_list())

        filename = api._get_remote_db().filepath(config['overlays']) + '.xml'

        with fileopen(filename, 'r') as b:
            description = b.readlines()[19]
            self.assertEqual(description, '      A collection of ebuilds from '\
                                          'Gunnar Wrobel [[email protected]].\n')
            for line in b.readlines():
                print(line, end='')

        # Check if we get available overlays.
        available = api.get_available()
        self.assertEqual(available, ['wrobel', 'wrobel-stable'])

        # Test the info of an overlay.
        info = api.get_info_str(['wrobel'], verbose=True, local=False)
        test_info = 'wrobel\n~~~~~~\nSource  : https://overlays.gentoo.org'\
                    '/svn/dev/wrobel\nContact : [email protected]\nType    '\
                    ': Subversion; Priority: 10\nQuality : experimental\n\n'\
                    'Description:\n  Test\n'

        info = info['wrobel'][0].decode('utf-8')
        self.assertEqual(info, test_info)

        os.unlink(filename)
        shutil.rmtree(tmpdir)
Exemple #11
0
    def test(self):
        temp_dir_path = tempfile.mkdtemp()
        my_opts = {
                   'overlays': ['file://'\
                        + HERE + '/testfiles/global-overlays.xml'],
                   'nocheck': 'yes',
                   'proxy': None,
                   'quietness': 3,
                  }

        config = OptionConfig(my_opts)

        ovl_dict = {
            'name': 'wrobel',
            'description': ['Test'],
            'owner': [{
                'name': 'nobody',
                'email': '*****@*****.**'
            }],
            'status': 'official',
            'source':
            [['https://overlays.gentoo.org/svn/dev/wrobel', 'svn', '']],
            'priority': '10',
        }

        a = Overlay(config=config, ovl_dict=ovl_dict, ignore=config['ignore'])
        ovl = (ovl_dict['name'], a)
        path = temp_dir_path + '/overlay.xml'
        create_overlay_xml = Interactive(config=config)

        create_overlay_xml(overlay_package=ovl, path=path)
        self.assertTrue(os.path.exists(path))

        with fileopen(path, 'r') as xml:
            test_line = '    <source type="svn">'\
                        'https://overlays.gentoo.org/svn/dev/wrobel</source>\n'
            self.assertTrue(test_line in xml.readlines())
            for line in xml.readlines():
                print(line, end='')

        shutil.rmtree(temp_dir_path)
Exemple #12
0
class Main(object):
    def __init__(self, root=None, config=None, output=None):
        self.parser = None
        self.output = output
        self.config = config
        self.args = None
        self.root = root

    def args_parser(self):
        self.parser = argparse.ArgumentParser(
            prog='layman-updater', description="Layman's update script")
        self.parser.add_argument("-H",
                                 '--setup_help',
                                 action='store_true',
                                 help='Print the NEW INSTALL help messages.')
        self.parser.add_argument("-c",
                                 "--config",
                                 help='the path to config file')
        self.parser.add_argument("-m",
                                 "--migrate_db",
                                 help="the database you'd like to migrate to")
        self.parser.add_argument(
            "-R",
            "--rebuild",
            action='store_true',
            help='rebuild the Package Manager config file')
        self.parser.add_argument('--version',
                                 action='version',
                                 version='%(prog)s ' + VERSION)

        self.args = self.parser.parse_args()

    def __call__(self):
        self.args_parser()
        options = None
        if self.args.config:
            options = {
                'config': self.args.config,
            }

        self.config = OptionConfig(options=options, root=self.root)
        # fix the config path
        defaults = self.config.get_defaults()
        defaults['config'] = defaults['config'] \
            % {'configdir': defaults['configdir']}
        self.config.update_defaults({'config': defaults['config']})

        self.config.read_config(defaults)

        layman_inst = LaymanAPI(config=self.config)

        self.output = layman_inst.output

        if self.args.setup_help:
            self.print_instructions()
        elif not self.check_is_new(self.args.rebuild):
            self.rename_check()
        if self.args.migrate_db:
            self.migrate_database(self.args.migrate_db)

    def check_is_new(self, rebuild=False):
        print_instructions = False
        if isinstance(self.config['conf_type'], STR):
            self.config.set_option('conf_type',
                                   self.config['conf_type'].split(','))
        for i in self.config['conf_type']:
            conf = i.replace('.', '_').strip()
            if conf and (rebuild or not os.access(self.config[conf], os.F_OK)):
                getattr(self, 'create_%(conf)s' % {'conf': conf})()
                print_instructions = True
        if print_instructions:
            self.print_instructions()
            return True
        return False

    def migrate_database(self, migrate_type):
        if migrate_type not in DB_TYPES:
            msg = 'migrate_database() error; invalid migration type: '\
                  '"%(db_type)s"' % {'db_type': migrate_type}
            self.output.die(msg)

        db = DB(self.config)
        installed = self.config['installed']
        old_ext = os.path.splitext(installed)[1]
        backup_name = installed + '.' + self.config['db_type']
        if old_ext == "." + self.config['db_type']:
            backup_name = installed + '.bak'
        new_name = installed.replace(old_ext, '.db')

        if not os.path.isfile(installed):
            msg = 'migrate_database() error; database file "%(loc)s" does not '\
                  'exist!' % {'loc': backup_name}
            self.output.error('  ' + msg)
            raise Exception(msg)

        msg = '  Creating backup of "%(db)s" at:\n "%(loc)s"\n'\
              % {'db': installed, 'loc': backup_name}
        self.output.info(msg)

        try:
            if migrate_type in ('json', 'xml'):
                shutil.copy(installed, backup_name)
            else:
                shutil.move(installed, backup_name)
        except IOError as err:
            msg = '  migrate_database() error; failed to back up old database '\
                  'file.\n  Error was: %(err)s' % {'err': err}
            self.output.error(msg)
            raise err

        db.write(installed, migrate_type=migrate_type)

        try:
            os.rename(installed, new_name)
        except OSError as err:
            msg = '  migrate_database() error: failed to rename old database '\
                  ' to "%(name)s".\n  Error was: %(err)s' % {'err': err}
            self.output.error(msg)
            raise err

        msg = '  Successfully migrated database from "%(from_type)s" to '\
              ' "%(to_type)s"\n' % {'from_type': self.config['db_type'],
                                   'to_type': migrate_type}
        self.output.info(msg)

        self.set_db_type(migrate_type, os.path.basename(new_name))

        msg = '  Warning: Please be sure to update your config file via '\
              'the\n  `dispatch-conf` command or you *will* lose database '\
              'functionality!\n'
        self.output.warn(msg)

    def rename_check(self):
        '''Checks for and renames the installed db if needed
        '''
        newname = self.config['installed']

        # check and handle the name change
        if not os.access(newname, os.F_OK):
            if os.access(self.config['local_list'], os.F_OK):
                self.output.info(
                    "  Layman automatic db rename utility, "
                    "performing update", 2)
                rename_db(self.config, newname, self.output)
        elif os.access(newname, os.F_OK) and \
            os.access(self.config['local_list'], os.F_OK):
            self.output.error("  Automatic db rename failed: "
                              "Both old and new files exist")
            self.output.error(" Old file: %s still exists" %
                              self.config['local_list'])
            self.output.error("  New file: %s already exists" % newname)
        elif os.access(newname, os.F_OK):
            self.output.info("  Automatic db rename: "
                             "db already updated: %s" % newname)
        else:
            self.output.info("  Automatic db rename: " "nothing to update")
        return

    def print_instructions(self):
        messages = [
            "You are now ready to add overlays into your system.",
            "",
            "  layman -L",
            "",
            "will display a list of available overlays.",
            "",
            "Select an overlay and add it using",
            "",
            "  layman -a overlay-name",
            "",
        ]
        if 'make.conf' in self.config['conf_type']:
            make_conf = '/etc/portage/make.conf'
            if not os.access(make_conf, os.F_OK):
                make_conf = '/etc/make.conf'
            messages += [
                "If this is the very first overlay you add with layman,",
                "you need to append the following statement to your",
                "%s file:" % make_conf,
                "",
                "  source /var/lib/layman/make.conf",
                "",
                "If you modify the 'storage' parameter in the layman",
                "configuration file (/etc/layman/layman.cfg) you will",
                "need to adapt the path given above to the new storage",
                "directory.",
                "",
            ]

        for message in messages:
            self.output.info("  " + message)

    def create_make_conf(self):
        self.output.info("  Creating layman's make.conf file")
        layman_inst = LaymanAPI(config=self.config)
        overlays = {}
        for ovl in layman_inst.get_installed():
            overlays[ovl] = layman_inst._get_installed_db().select(ovl)
        # create layman's %(storage)s/make.conf
        # so portage won't error
        from layman.config_modules.makeconf.makeconf import ConfigHandler
        maker = ConfigHandler(self.config, overlays)
        maker.write()

    def create_repos_conf(self):
        self.output.info("  Creating layman's repos.conf file")

        if os.path.isdir(self.config['repos_conf']):
            msg = '  create_repos_conf() error: %s is a directory and will\n'\
                  '  not be written to.' % self.config['repos_conf']
            self.output.error(msg)
            return None

        conf_dir = os.path.dirname(self.config['repos_conf'])

        if not os.path.isdir(conf_dir):
            try:
                os.mkdir(conf_dir)
            except OSError as e:
                self.output.error('  create_repos_conf() error creating %s: '\
                                  % conf_dir)
                self.output.error('  "%s"' % e)
                return None

        layman_inst = LaymanAPI(config=self.config)
        overlays = {}
        for ovl in layman_inst.get_installed():
            overlays[ovl] = layman_inst._get_installed_db().select(ovl)
        # create layman's %(repos_conf) so layman
        # can write the overlays to it.
        open(self.config['repos_conf'], 'w').close()
        from layman.config_modules.reposconf.reposconf import ConfigHandler
        repos_conf = ConfigHandler(self.config, overlays, rebuild=True)
        repos_conf.write()

    def set_db_type(self, migrate_type, installed):
        config_path = self.config['config']\
                      % {'configdir': self.config['configdir']}
        db_type_found = False
        installed_found = False
        new_conf = os.path.dirname(config_path) + '/' + '._cfg0000_' +\
                   os.path.basename(config_path)
        new_lines = []

        try:
            shutil.copy(config_path, new_conf)
        except IOError as err:
            msg = '  set_db_type() error; failed to copy "%(old)s" to '\
                  '"%(new)s\n  Error was: %(err)s"' % {'old': config_path,
                                                       'new': new_conf,
                                                       'err': err}
            self.output.error(msg)
            raise err

        if not os.path.isfile(new_conf):
            msg = 'set_db_type() error; failed to read config at "%(path)s".'\
                  % {'path': new_conf}
            self.output.error('  ' + msg)
            raise Exception(msg)

        try:
            with fileopen(new_conf, 'r') as laymanconf:
                lines = laymanconf.readlines()
        except Exception as err:
            msg = '  set_db_type() error; failed to read config at "%(path)s".'\
                  '\n  Error was: "%(err)s"' % {'path': new_conf, 'err': err}
            self.output.error(msg)
            raise err

        for line in lines:
            if re.search('^#*\s*db_type\s*:', line):
                db_type_found = True
                line = 'db_type : ' + migrate_type + '\n'
            if re.search('^#*\s*installed\s*:', line):
                installed_found = True
                line = 'installed : %(storage)s/' + installed + '\n'
            new_lines.append(line)

        if not db_type_found:
            new_lines.append('db_type : ' + migrate_type + '\n')
        if not installed_found:
            new_lines.append('installed : %(storage)s/' + installed + '\n')

        with fileopen(new_conf, 'w') as laymanconf:
            for line in new_lines:
                laymanconf.write(line)
Exemple #13
0
class PyLayman(SyncBase):
    '''
    Layman sync class which makes use of layman's modules to
    perform desired actions.
    '''

    short_desc = "Perform sync operations on layman based repositories"

    @staticmethod
    def name():
        '''
        Returns sync plugin name.

        @rtype str
        '''
        return "Layman"

    def __init__(self):
        SyncBase.__init__(self, 'layman', 'app-portage/layman')

        self._layman = None
        self.storage = ''
        self.current_storage = ''


    def _get_layman_api(self):
        '''
        Initializes layman api.

        @rtype layman.api.LaymanAPI instance
        '''
        # Make it so that we aren't initializing the
        # LaymanAPI instance if it already exists and
        # if the current storage location hasn't been
        # changed for the new repository.
        self.storage = self.repo.location.replace(self.repo.name, '')

        if self._layman and self.storage in self.current_storage:
            return self._layman

        config = BareConfig()
        configdir = {'configdir': config.get_option('configdir')}

        self.message = Message(out=sys.stdout, err=sys.stderr)
        self.current_storage = self.storage
        options = {
            'config': config.get_option('config') % (configdir),
            'quiet': self.settings.get('PORTAGE_QUIET'),
            'quietness': config.get_option('quietness'),
            'overlay_defs': config.get_option('overlay_defs') % (configdir),
            'output': self.message,
            'nocolor': self.settings.get('NOCOLOR'),
            'root': self.settings.get('EROOT'),
            'storage': self.current_storage,
            'verbose': self.settings.get('PORTAGE_VERBOSE'),
            'width': self.settings.get('COLUMNWIDTH'),

        }
        self.config = OptionConfig(options=options, root=options['root'])

        # Reloads config to read custom overlay
        # xml files.
        reload_config(self.config)

        layman_api = LaymanAPI(self.config,
                               report_errors=True,
                               output=self.config['output']
                               )

        self._layman = layman_api

        return layman_api


    def _eval_exitcode(self, exitcode):
        '''
        Evaluates the boolean returned by layman's API
        when performing a task and returns the proper exitcode.

        @params exitcode: boolean value reflecting the successful
                          execution of a task.
        @rtype int
        '''
        if exitcode == True:
            return 0
        return 1


    def new(self, **kwargs):
        '''Do the initial download and install of the repository'''
        layman_inst = self._get_layman_api()
        # Update the remote list before adding anything.
        layman_inst.fetch_remote_list()
        available_overlays = layman_inst.get_available(dbreload=True)
        emerge_config = self.options.get('emerge_config', None)
        portdb = self.options.get('portdb', None)

        msg = '>>> Starting to add new layman overlay %(repo)s'\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')

        if self.repo.name not in available_overlays:
            overlay_package = create_overlay_package(repo=self.repo,\
                                logger=self.logger,\
                                xterm_titles=self.xter_titles)
            create_overlay_xml = Interactive(config=self.config)
            path = self.config.get_option('overlay_defs') + '/reposconf.xml'
            result = create_overlay_xml(overlay_package=overlay_package,
                        path=path)

            if not result:
                msg = '!!! layman add error in %(repo)s: Failed to add'\
                      '%(repo)s to %(path)s' % ({'repo': self.repo.name,
                                                  'path': path})
                self.logger(self.xterm_titles, msg)
                writemsg_level(msg + '\n', level=logging.ERROR, noiselevel=-1)
                return (exitcode, False)

        results = layman_inst.add_repos(self.repo.name)
        exitcode = self._eval_exitcode(results)

        if exitcode != os.EX_OK:
            msg = "!!! layman add error in %(repo)s"\
                % ({'repo': self.repo.name})
            self.logger(self.xterm_titles, msg)
            writemsg_level(msg + "\n", level=logging.ERROR, noiselevel=-1)
            return (exitcode, False)
        msg = ">>> Addition of layman repo succeeded: %(repo)s"\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')

        return (exitcode, True)

    def _sync(self):
        ''' Update existing repository'''
        layman_inst = self._get_layman_api()

        emerge_config = self.options.get('emerge_config', None)
        portdb = self.options.get('portdb', None)

        msg = '>>> Starting layman sync for %(repo)s...'\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')

        results = layman_inst.sync(self.repo.name)
        exitcode = self._eval_exitcode(results)

        if exitcode != os.EX_OK:
            exitcode = self.new()[0]
            if exitcode != os.EX_OK:
                msg = "!!! layman sync error in %(repo)s"\
                    % ({'repo': self.repo.name})
                self.logger(self.xterm_titles, msg)
                writemsg_level(msg + "\n", level=logging.ERROR, noiselevel=-1)
                return (exitcode, False)
            else:
                return (exitcode, True)

        msg = ">>> layman sync succeeded: %(repo)s"\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + "\n")

        return (exitcode, True)
Exemple #14
0
class PyLayman(NewBase):
    '''
    Layman sync class which makes use of layman's modules to
    perform desired actions.
    '''

    short_desc = "Perform sync operations on layman based repositories"

    @staticmethod
    def name():
        '''
        Returns sync plugin name.

        @rtype str
        '''
        return "Layman"

    def __init__(self):
        NewBase.__init__(self, 'layman', 'app-portage/layman')

        self._layman = None
        self.storage = ''
        self.current_storage = ''

    def _get_layman_api(self):
        '''
        Initializes layman api.

        @rtype layman.api.LaymanAPI instance
        '''
        # Make it so that we aren't initializing the
        # LaymanAPI instance if it already exists and
        # if the current storage location hasn't been
        # changed for the new repository.
        self.storage = self.repo.location.replace(self.repo.name, '')

        if self._layman and self.storage in self.current_storage:
            return self._layman

        config = BareConfig()
        configdir = {'configdir': config.get_option('configdir')}

        self.message = Message(out=sys.stdout, err=sys.stderr)
        self.current_storage = self.storage
        options = {
            'config': config.get_option('config') % (configdir),
            'quiet': self.settings.get('PORTAGE_QUIET'),
            'quietness': config.get_option('quietness'),
            'overlay_defs': config.get_option('overlay_defs') % (configdir),
            'output': self.message,
            'nocolor': self.settings.get('NOCOLOR'),
            'root': self.settings.get('EROOT'),
            'storage': self.current_storage,
            'verbose': self.settings.get('PORTAGE_VERBOSE'),
            'width': self.settings.get('COLUMNWIDTH'),
        }
        self.config = OptionConfig(options=options, root=options['root'])

        # Reloads config to read custom overlay
        # xml files.
        reload_config(self.config)

        layman_api = LaymanAPI(self.config,
                               report_errors=True,
                               output=self.config['output'])

        self._layman = layman_api

        return layman_api

    def _eval_exitcode(self, exitcode):
        '''
        Evaluates the boolean returned by layman's API
        when performing a task and returns the proper exitcode.

        @params exitcode: boolean value reflecting the successful
                          execution of a task.
        @rtype int
        '''
        if exitcode == True:
            return 0
        return 1

    def new(self, **kwargs):
        '''Do the initial download and install of the repository'''
        layman_inst = self._get_layman_api()
        # Update the remote list before adding anything.
        layman_inst.fetch_remote_list()
        available_overlays = layman_inst.get_available(dbreload=True)

        msg = '>>> Starting to add new layman overlay %(repo)s'\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')

        if self.repo.name not in available_overlays:
            overlay_package = create_overlay_package(repo=self.repo,\
                                logger=self.logger,\
                                xterm_titles=self.xter_titles)
            create_overlay_xml = Interactive(config=self.config)
            path = self.config.get_option('overlay_defs') + '/reposconf.xml'
            result = create_overlay_xml(overlay_package=overlay_package,
                                        path=path)

            if not result:
                msg = '!!! layman add error in %(repo)s: Failed to add'\
                      '%(repo)s to %(path)s' % ({'repo': self.repo.name,
                                                  'path': path})
                self.logger(self.xterm_titles, msg)
                writemsg_level(msg + '\n', level=logging.ERROR, noiselevel=-1)
                return (1, False)

        results = layman_inst.add_repos(self.repo.name)
        exitcode = self._eval_exitcode(results)

        if exitcode != os.EX_OK:
            msg = "!!! layman add error in %(repo)s"\
                % ({'repo': self.repo.name})
            self.logger(self.xterm_titles, msg)
            writemsg_level(msg + "\n", level=logging.ERROR, noiselevel=-1)
            return (exitcode, False)
        msg = ">>> Addition of layman repo succeeded: %(repo)s"\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')
        msg = '>>> laymansync sez... "Hasta la add ya, baby!"'
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')

        return (exitcode, True)

    def update(self):
        ''' Update existing repository'''
        layman_inst = self._get_layman_api()

        msg = '>>> Starting layman sync for %(repo)s...'\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + '\n')

        results = layman_inst.sync(self.repo.name)
        exitcode = self._eval_exitcode(results)

        if exitcode != os.EX_OK:
            exitcode = self.new()[0]
            if exitcode != os.EX_OK:
                msg = "!!! layman sync error in %(repo)s"\
                    % ({'repo': self.repo.name})
                self.logger(self.xterm_titles, msg)
                writemsg_level(msg + "\n", level=logging.ERROR, noiselevel=-1)
                return (exitcode, False)
            else:
                return (exitcode, True)

        msg = ">>> layman sync succeeded: %(repo)s"\
            % ({'repo': self.repo.name})
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + "\n")
        msg = '>>> laymansync sez... "Hasta la sync ya, baby!"'
        self.logger(self.xterm_titles, msg)
        writemsg_level(msg + "\n")

        return (exitcode, True)
Exemple #15
0
class Interactive(object):
    def __init__(self, config=None):
        self.config = config
        if self.config:
            reload_config(self.config)
        self.layman_inst = LaymanAPI(config=self.config)
        self.output = self.config.get_option('output')
        self.overlay = {}
        self.overlays = []
        self.overlays_available = self.layman_inst.get_available()
        self.supported_types = list(self.layman_inst.supported_types())

    def args_parser(self):
        self.parser = argparse.ArgumentParser(prog='layman-overlay-maker',
            description='Layman\'s utility script to create overlay'\
                        'definitions.')
        self.parser.add_argument('-l',
                                 '--list-autocomplete',
                                 action='store_true',
                                 help='Lists all available mirrors supported'\
                                 ' for information auto-completion'
                                )
        self.parser.add_argument('-n',
                                 '--no-extra',
                                 action='store_true',
                                 help='Don\'t add any extra overlay info,'\
                                 ' just the bare minimum requirements')
        self.parser.add_argument('-s',
                                 '--set-autocomplete',
                                 nargs='+',
                                 help='Enables auto-completion support for'\
                                 ' the selected mirror. Specify "ALL" to'\
                                 ' enable support for all available mirrors')
        self.parser.add_argument('-S',
                                 '--sudo',
                                 action='store_true',
                                 help='Bypass checks to see if an overlay'\
                                 ' name being inputted is already being used'\
                                 ' and choose the installation dir for the'\
                                 ' XML')
        self.parser.add_argument('-V',
                                 '--version',
                                 action='version',
                                 version='%(prog)s ' + VERSION)

        self.args = self.parser.parse_args()

    def __call__(self, overlay_package=None, path=None):

        if not overlay_package:
            self.args_parser()
            options = {}
            for key in vars(self.args):
                options[key] = vars(self.args)[key]
            self.config = OptionConfig(options=options)
            reload_config(self.config)

            self.auto_complete = False
            self.list_info = self.config.get_option('list_autocomplete')
            self.no_extra = self.config.get_option('no_extra')
            self.sudo = self.config.get_option('sudo')

            if self.list_info:
                self.list_templates()

            if self.args.set_autocomplete:
                if 'ALL' in self.args.set_autocomplete:
                    self.templates = AUTOCOMPLETE_TEMPLATE.keys()
                    self.auto_complete = True
                else:
                    self.templates = self.args.set_autocomplete
                    self.auto_complete = True

            msg = 'How many overlays would you like to create?: '
            for x in range(1, int(get_input(msg)) + 1):
                self.output.notice('')
                self.output.info('Overlay #%(x)s: ' % ({'x': str(x)}))
                self.output.info('~~~~~~~~~~~~~')

                self.required = copy.deepcopy(COMPONENT_DEFAULTS)

                if not self.no_extra:
                    self.output.notice('')
                    self.update_required()
                    self.output.notice('')

                self.get_overlay_components()
                ovl = Overlay.Overlay(config=self.config,
                                      ovl_dict=self.overlay,
                                      ignore=1)
                self.overlays.append((self.overlay['name'], ovl))
        else:
            ovl_name, ovl = overlay_package
            self.overlays.append((ovl_name, ovl))

        result = self.write(path)
        return result

    def check_overlay_type(self, ovl_type):
        '''
        Validates overlay type.

        @params ovl_type: str of overlay type
        @rtype None or str (if overlay type is valid).
        '''
        if ovl_type.lower() in self.supported_types:
            return ovl_type.lower()

        msg = '!!! Specified type "%(type)s" not valid.' % ({'type': ovl_type})
        self.output.warn(msg)
        msg = 'Supported types include: %(types)s.'\
              % ({'types': ', '.join(self.supported_types)})
        self.output.warn(msg)
        return None

    def guess_overlay_type(self, source_uri):
        '''
        Guesses the overlay type based on the source given.

        @params source-uri: str of source.
        @rtype None or str (if overlay type was guessed correctly).
        '''

        type_checks = copy.deepcopy(self.supported_types)

        #Modify the type checks for special overlay types.
        if 'tar' in type_checks:
            type_checks.remove(type_checks[type_checks.index('tar')])
            type_checks.insert(len(type_checks), '.tar')

        if 'bzr' in type_checks:
            type_checks.remove(self.supported_types[type_checks.index('bzr')])
            type_checks.insert(len(type_checks), 'bazaar')

        for guess in type_checks:
            if guess in source_uri:
                return guess

        if 'bitbucket.org' in source_uri:
            return 'mercurial'

        return None

    def update_required(self):
        '''
        Prompts user for optional components and updates
        the required components accordingly.
        '''
        for possible in POSSIBLE_COMPONENTS:
            if possible not in self.required:
                msg = 'Include %(comp)s for this overlay? [y/n]: '\
                        % ({'comp': possible})
                if ((possible in 'homepage' or possible in 'feed')
                        and self.auto_complete):
                    available = False
                else:
                    available = get_ans(msg)
                if available:
                    self.required.append(possible)

    def get_description(self):
        '''
        Prompts user for an overlay's description(s)
        and updates overlay dict with value(s).
        '''
        #TODO: Currently a "stub" function. Add multiple description
        # field support later down the road.
        descriptions = []

        desc = get_input('Define overlay\'s description: ')
        descriptions.append(desc)

        self.overlay['description'] = descriptions

    def get_feed(self):
        '''
        Prompts user for any overlay RSS feeds
        and updates overlay dict with values.
        '''
        msg = 'How many RSS feeds exist for this overlay?: '
        feed_amount = int(get_input(msg))
        feeds = []

        for i in range(1, feed_amount + 1):
            extra = ''
            if feed_amount > 1:
                extra = '[%(i)s]' % {'i': str(i)}
            feeds.append(get_input('Define overlay%(extra)s feed: '\
                         % {'extra': extra}))

        self.overlay['feed'] = feeds
        self.output.notice('')

    def get_name(self):
        '''
        Prompts user for the overlay name
        and updates the overlay dict.
        '''
        name = get_input('Define overlay name: ')

        if not self.sudo:
            while name in self.overlays_available:
                msg = '!!! Overlay name already defined in list of installed'\
                      ' overlays.'
                self.output.warn(msg)
                msg = 'Please specify a different overlay name: '
                name = get_input(msg, color='yellow')

        self.overlay['name'] = name

    def get_source(self):
        '''
        Prompts user for possible overlay source
        information such as type, url, and branch
        and then appends the values to the overlay
        being created.
        '''
        ovl_type = None

        if self.auto_complete:
            source_amount = 1
        else:
            msg = 'How many different sources, protocols, or mirrors exist '\
                  'for this overlay?: '
            source_amount = int(get_input(msg))

        self.overlay['source'] = []

        for i in range(1, source_amount + 1):
            sources = []
            correct = False
            extra = ''
            if source_amount > 1:
                extra = '[%(i)s]\'s' % {'i': str(i)}

            msg = 'Define source%(extra)s URL: ' % {'extra': extra}
            sources.append(get_input(msg))

            ovl_type = self.guess_overlay_type(sources[0])
            if ovl_type:
                msg = 'Is "%(type)s" the correct overlay type?: '\
                    % ({'type': ovl_type})
                correct = get_ans(msg)
            while not ovl_type or not correct:
                msg = 'Please provide overlay type: '
                ovl_type = self.check_overlay_type(\
                            get_input(msg, color='yellow'))
                correct = True

            sources.append(ovl_type)
            if 'branch' in self.required:
                msg = 'Define source%(extra)s branch (if applicable): '\
                      % {'extra': extra}
                sources.append(get_input(msg))
            else:
                sources.append('')

            if self.auto_complete:
                sources = self._set_additional_info(sources)
                for source in sources:
                    self.overlay['source'].append(source)
            else:
                self.overlay['source'].append(sources)
        self.output.notice('')

    def get_owner(self):
        '''
        Prompts user for overlay owner info and
        then appends the values to the overlay
        being created.
        '''
        self.overlay['owner'] = []
        self.output.notice('')

        msg = 'How many people own this overlay?: '
        owner_amount = int(get_input(msg))

        for i in range(1, owner_amount + 1):
            owner = {}
            extra = ''

            if owner_amount > 1:
                extra = '[%(i)s]\'s' % {'i': str(i)}

            owner['name'] = get_input('Define owner%(extra)s name: '\
                            % {'extra': extra})
            owner['email'] = get_input('Define owner%(extra)s email: '\
                             % {'extra': extra})
            self.overlay['owner'].append(owner)

        self.output.notice('')

    def get_component(self, component, msg):
        '''
        Sets overlay component value.

        @params component: (str) component to be set
        @params msg: (str) prompt message for component
        '''
        if component not in ('branch', 'type'):
            if component in ('description', 'feed', 'name', 'owner', 'source'):
                getattr(self, 'get_%(comp)s' % ({'comp': component}))()
            else:
                self.overlay[component] = get_input(msg)

    def get_overlay_components(self):
        '''
        Acquires overlay components via user interface.
        '''
        self.overlay = {}

        for component in self.required:
            self.get_component(component, 'Define %(comp)s: '\
                                % ({'comp': component}))

    def list_templates(self):
        '''
        Lists all available mirrors that support information auto-completion
        and exits.
        '''
        self.output.info('Mirrors supported for information auto-completion: ')
        self.output.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
        for i in sorted(AUTOCOMPLETE_TEMPLATE):
            self.output.info(i)
        sys.exit()

    def read(self, path):
        '''
        Reads overlay.xml files and appends the contents
        to be sorted when writing to the file.

        @params path: path to file to be read
        '''
        try:
            document = ET.parse(path)
        except ET.ParseError as error:
            msg = 'Interactive.read(); encountered error: %(error)s'\
                % ({'error': error})
            raise Exception(msg)

        overlays = document.findall('overlay') + document.findall('repo')

        for overlay in overlays:
            ovl_name = overlay.find('name')
            ovl = Overlay.Overlay(config=self.config, xml=overlay, ignore=1)
            self.overlays.append((ovl_name.text, ovl))

    def _set_additional_info(self, source):
        '''
        Sets additional possible overlay information.

        @params source: list of the source URL, type, and branch.
        '''
        feeds = []
        sources = []
        url = self._split_source_url(source[0])
        attrs = {'branch': source[2], 'tail': '/'.join(url[3:])}
        mirror = url[2].split('.')

        for i in self.templates:
            if i in mirror:
                if i not in ('gentoo'):
                    attrs['info'] = attrs['tail'].replace('.git', '')

                if attrs['branch']:
                    try:
                        TEMPLATE = AUTOCOMPLETE_TEMPLATE[i + '-branch']
                    except KeyError:
                        TEMPLATE = AUTOCOMPLETE_TEMPLATE[i]
                else:
                    TEMPLATE = AUTOCOMPLETE_TEMPLATE[i]

                self.overlay['homepage'] = TEMPLATE['homepage'] % attrs

                if i in ('bitbucket') and 'git' in (source[1]):
                    return [source]

                for s in TEMPLATE['source']:
                    source = (s[0] % attrs, s[1], s[2] % attrs)
                    sources.append(source)
                for f in TEMPLATE['feed']:
                    feed = (f % attrs)
                    feeds.append(feed)
                self.overlay['feed'] = feeds

        if sources:
            return sources

        return [source]

    def _split_source_url(self, source_url):
        '''
        Splits the given source URL based on
        the source URL type.

        @params source_url: str, represents the URL for the repo.
        @rtype str: The newly split url components.
        '''
        url = None
        if re.search("^(git://)|(http://)|(https://)|(ssh://)", source_url):
            url = source_url.split('/')
        if re.search('^git\+ssh://', source_url):
            url = source_url.replace('+ssh', '')
            url = url.replace('git@', '').split('/')
        if re.search('^git@', source_url):
            url = source_url.replace('@', '//')
            url = url.replace(':', '/')
            url = url.replace('//', '://').split('/')
        if url:
            return url
        raise Exception('Interactive._split_source_url(); error: Unable '\
                        'to split URL.')

    def _sort_to_tree(self):
        '''
        Sorts all Overlay objects by overlay name
        and converts the sorted overlay objects to
        XML that is then appended to the tree.
        '''
        self.overlays = sorted(self.overlays)
        for overlay in self.overlays:
            self.tree.append(overlay[1].to_xml())

    def write(self, destination):
        '''
        Writes overlay file to desired location.

        @params destination: path & file to write xml to.
        @rtype bool: reflects success or failure to write xml.
        '''
        if not destination:
            filepath = self.config.get_option('overlay_defs')
            if self.sudo:
                filepath = get_input('Desired file destination dir: ')
            filename = get_input('Desired overlay file name: ')

            if not filename.endswith('.xml'):
                filename += ".xml"

            if not filepath.endswith(os.path.sep):
                filepath += os.path.sep

            destination = filepath + filename

        self.tree = ET.Element('repositories',
                               version='1.1',
                               encoding=_UNICODE)

        if os.path.isfile(destination):
            self.read(destination)

        self._sort_to_tree()
        indent(self.tree)
        self.tree = ET.ElementTree(self.tree)

        try:
            with fileopen(destination, 'w') as xml:
                self.tree.write(xml, encoding=_UNICODE)
            msg = 'Successfully wrote repo(s) to: %(path)s'\
                  % ({'path': destination})
            self.output.info(msg)
            return True

        except IOError as e:
            raise Exception("Writing XML failed: %(error)s" % ({'error': e}))
Exemple #16
0
    def test(self):
        repo_name     = 'tar_test_overlay'
        temp_dir_path = tempfile.mkdtemp(prefix='laymantmp_')
        db_file       = os.path.join(temp_dir_path, 'installed.xml')
        make_conf     = os.path.join(temp_dir_path, 'make.conf')
        repo_conf     = os.path.join(temp_dir_path, 'repos.conf')

        tar_source_path = os.path.join(HERE, 'testfiles', 'layman-test.tar.bz2')

        (_, temp_tarball_path) = tempfile.mkstemp()
        shutil.copyfile(tar_source_path, temp_tarball_path)

        # Write overlay collection XML
        xml_text = '''\
<?xml version="1.0" encoding="UTF-8"?>
<repositories xmlns="" version="1.0">
  <repo quality="experimental" status="unofficial">
    <name>%(repo_name)s</name>
    <description>XXXXXXXXXXX</description>
    <owner>
      <email>[email protected]</email>
    </owner>
    <source type="tar">file://%(temp_tarball_url)s</source>
  </repo>
</repositories>
        '''\
        % {
            'temp_tarball_url': urllib.pathname2url(temp_tarball_path),
            'repo_name': repo_name
          }

        (fd, temp_xml_path) = tempfile.mkstemp()

        my_opts = {'installed'     : temp_xml_path,
                   'conf_type'     : ['make.conf', 'repos.conf'],
                   'db_type'       : 'xml',
                   'nocheck'       : 'yes',
                   'make_conf'     : make_conf,
                   'repos_conf'    : repo_conf,
                   'storage'       : temp_dir_path,
                   'check_official': False}

        with os.fdopen(fd, 'w') as f:
            f.write(xml_text)

        with fileopen(make_conf, 'w') as f:
            f.write('PORTDIR_OVERLAY="$PORTDIR_OVERLAY"\n')

        with fileopen(repo_conf, 'w') as f:
            f.write('')

        config = OptionConfig(options=my_opts)
        config.set_option('quietness', 3)

        a = DB(config)
        config.set_option('installed', db_file)

        # Add an overlay to a fresh DB file.
        b = DB(config)
        b.add(a.select(repo_name))

        # Make sure it's actually installed.
        specific_overlay_path = os.path.join(temp_dir_path, repo_name)
        self.assertTrue(os.path.exists(specific_overlay_path))

        # Check the DbBase to ensure that it's reading the installed.xml.
        c = DbBase(config, paths=[db_file,])
        self.assertEqual(list(c.overlays), ['tar_test_overlay'])

        # Make sure the configs have been written to correctly.
        conf = RepoConfManager(config, b.overlays)
        self.assertEqual(list(conf.overlays), ['tar_test_overlay'])

        # Delete the overlay from the second DB.
        b.delete(b.select(repo_name))
        self.assertEqual(b.overlays, {})

        # Ensure the installed.xml has been cleaned properly.
        c = DbBase(config, paths=[db_file,])
        self.assertEqual(c.overlays, {})

        conf = RepoConfManager(config, b.overlays)
        self.assertEqual(conf.overlays, {})

        # Clean up.
        os.unlink(temp_xml_path)
        os.unlink(temp_tarball_path)
        shutil.rmtree(temp_dir_path)
Exemple #17
0
class Main(object):

    def __init__(self, root=None, config=None, output=None):
        self.parser = None
        self.output = output
        self.config = config
        self.args = None
        self.root = root

    def args_parser(self):
        self.parser = argparse.ArgumentParser(prog='layman-updater',
            description="Layman's update script")
        self.parser.add_argument("-H", '--setup_help', action='store_true',
            help = 'Print the NEW INSTALL help messages.')
        self.parser.add_argument("-c", "--config",
            help='the path to config file')
        self.parser.add_argument("-m", "--migrate_db",
            help="the database you'd like to migrate to")
        self.parser.add_argument("-R", "--rebuild", action='store_true',
            help='rebuild the Package Manager config file')
        self.parser.add_argument('--version', action='version',
            version='%(prog)s ' + VERSION)

        self.args = self.parser.parse_args()

    def __call__(self):
        self.args_parser()
        options = None
        if self.args.config:
            options = {
                'config': self.args.config,
            }

        self.config = OptionConfig(options=options, root=self.root)
        # fix the config path
        defaults = self.config.get_defaults()
        defaults['config'] = defaults['config'] \
            % {'configdir': defaults['configdir']}
        self.config.update_defaults({'config': defaults['config']})

        self.config.read_config(defaults)

        layman_inst = LaymanAPI(config=self.config)

        self.output = layman_inst.output

        if self.args.setup_help:
            self.print_instructions()
        elif not self.check_is_new(self.args.rebuild):
            self.rename_check()
        if self.args.migrate_db:
            self.migrate_database(self.args.migrate_db)

    def check_is_new(self, rebuild=False):
        print_instructions = False
        if isinstance(self.config['conf_type'], STR):
            self.config.set_option('conf_type',
                                   self.config['conf_type'].split(','))
        for i in self.config['conf_type']:
            conf = i.replace('.', '_').strip()
            if conf and (rebuild or not os.access(self.config[conf], os.F_OK)):
                getattr(self, 'create_%(conf)s' % {'conf': conf})()
                print_instructions = True
        if print_instructions:
            self.print_instructions()
            return True
        return False



    def migrate_database(self, migrate_type):
        if migrate_type not in DB_TYPES:
            msg = 'migrate_database() error; invalid migration type: '\
                  '"%(db_type)s"' % {'db_type': migrate_type}
            self.output.die(msg)

        db = DB(self.config)
        installed = self.config['installed']
        old_ext = os.path.splitext(installed)[1]
        backup_name = installed + '.' + self.config['db_type']
        if old_ext == "." + self.config['db_type']:
            backup_name = installed + '.bak'
        new_name = installed.replace(old_ext, '.db')

        if not os.path.isfile(installed):
            msg = 'migrate_database() error; database file "%(loc)s" does not '\
                  'exist!' % {'loc': backup_name}
            self.output.error('  ' + msg)
            raise Exception(msg)

        msg = '  Creating backup of "%(db)s" at:\n "%(loc)s"\n'\
              % {'db': installed, 'loc': backup_name}
        self.output.info(msg)

        try:
            if migrate_type in ('json', 'xml'):
                shutil.copy(installed, backup_name)
            else:
                shutil.move(installed, backup_name)
        except IOError as err:
            msg = '  migrate_database() error; failed to back up old database '\
                  'file.\n  Error was: %(err)s' % {'err': err}
            self.output.error(msg)
            raise err

        db.write(installed, migrate_type=migrate_type)

        try:
            os.rename(installed, new_name)
        except OSError as err:
            msg = '  migrate_database() error: failed to rename old database '\
                  ' to "%(name)s".\n  Error was: %(err)s' % {'err': err}
            self.output.error(msg)
            raise err

        msg = '  Successfully migrated database from "%(from_type)s" to '\
              ' "%(to_type)s"\n' % {'from_type': self.config['db_type'],
                                   'to_type': migrate_type}
        self.output.info(msg)

        self.set_db_type(migrate_type, os.path.basename(new_name))

        msg = '  Warning: Please be sure to update your config file via '\
              'the\n  `dispatch-conf` command or you *will* lose database '\
              'functionality!\n'
        self.output.warn(msg)


    def rename_check(self):
        '''Checks for and renames the installed db if needed
        '''
        newname = self.config['installed']

        # check and handle the name change
        if not os.access(newname, os.F_OK):
            if os.access(self.config['local_list'], os.F_OK):
                self.output.info("  Layman automatic db rename utility, "
                    "performing update", 2)
                rename_db(self.config, newname, self.output)
        elif os.access(newname, os.F_OK) and \
            os.access(self.config['local_list'], os.F_OK):
            self.output.error("  Automatic db rename failed: "
                "Both old and new files exist")
            self.output.error(" Old file: %s still exists"
                % self.config['local_list'])
            self.output.error("  New file: %s already exists" % newname)
        elif os.access(newname, os.F_OK):
            self.output.info("  Automatic db rename: "
                "db already updated: %s" % newname)
        else:
            self.output.info("  Automatic db rename: "
                "nothing to update")
        return


    def print_instructions(self):
        messages = [
            "You are now ready to add overlays into your system.",
            "",
            "  layman -L",
            "",
            "will display a list of available overlays.",
            "",
            "Select an overlay and add it using",
            "",
            "  layman -a overlay-name",
            "",]
        if 'make.conf' in self.config['conf_type']:
            make_conf = '/etc/portage/make.conf'
            if not os.access(make_conf, os.F_OK):
                make_conf = '/etc/make.conf'
            messages += [
                "If this is the very first overlay you add with layman,",
                "you need to append the following statement to your",
                "%s file:" % make_conf,
                "",
                "  source /var/lib/layman/make.conf",
                "",
                "If you modify the 'storage' parameter in the layman",
                "configuration file (/etc/layman/layman.cfg) you will",
                "need to adapt the path given above to the new storage",
                "directory.",
                "",]

        for message in messages:
            self.output.info("  " + message)


    def create_make_conf(self):
        self.output.info("  Creating layman's make.conf file")
        layman_inst = LaymanAPI(config=self.config)
        overlays = {}
        for ovl in layman_inst.get_installed():
            overlays[ovl] = layman_inst._get_installed_db().select(ovl)
        # create layman's %(storage)s/make.conf
        # so portage won't error
        from layman.config_modules.makeconf.makeconf import ConfigHandler
        maker = ConfigHandler(self.config, overlays)
        maker.write()


    def create_repos_conf(self):
        self.output.info("  Creating layman's repos.conf file")

        if os.path.isdir(self.config['repos_conf']):
            msg = '  create_repos_conf() error: %s is a directory and will\n'\
                  '  not be written to.' % self.config['repos_conf']
            self.output.error(msg)
            return None

        conf_dir = os.path.dirname(self.config['repos_conf'])

        if not os.path.isdir(conf_dir):
            try:
                os.mkdir(conf_dir)
            except OSError as e:
                self.output.error('  create_repos_conf() error creating %s: '\
                                  % conf_dir)
                self.output.error('  "%s"' % e)
                return None

        layman_inst = LaymanAPI(config=self.config)
        overlays = {}
        for ovl in layman_inst.get_installed():
            overlays[ovl] = layman_inst._get_installed_db().select(ovl)
        # create layman's %(repos_conf) so layman
        # can write the overlays to it.
        open(self.config['repos_conf'], 'w').close()
        from layman.config_modules.reposconf.reposconf import ConfigHandler
        repos_conf = ConfigHandler(self.config, overlays)
        repos_conf.write()


    def set_db_type(self, migrate_type, installed):
        config_path = self.config['config']\
                      % {'configdir': self.config['configdir']}
        db_type_found = False
        installed_found = False
        new_conf = os.path.dirname(config_path) + '/' + '._cfg0000_' +\
                   os.path.basename(config_path)
        new_lines = []

        try:
            shutil.copy(config_path, new_conf)
        except IOError as err:
            msg = '  set_db_type() error; failed to copy "%(old)s" to '\
                  '"%(new)s\n  Error was: %(err)s"' % {'old': config_path,
                                                       'new': new_conf,
                                                       'err': err}
            self.output.error(msg)
            raise err

        if not os.path.isfile(new_conf):
            msg = 'set_db_type() error; failed to read config at "%(path)s".'\
                  % {'path': new_conf}
            self.output.error('  ' + msg)
            raise Exception(msg)

        try:
            with fileopen(new_conf, 'r') as laymanconf:
                lines = laymanconf.readlines()
        except Exception as err:
            msg = '  set_db_type() error; failed to read config at "%(path)s".'\
                  '\n  Error was: "%(err)s"' % {'path': new_conf, 'err': err}
            self.output.error(msg)
            raise err

        for line in lines:
            if re.search('^#*\s*db_type\s*:', line):
                db_type_found = True
                line = 'db_type : ' + migrate_type + '\n'
            if re.search('^#*\s*installed\s*:', line):
                installed_found = True
                line = 'installed : %(storage)s/' + installed + '\n'
            new_lines.append(line)

        if not db_type_found:
            new_lines.append('db_type : ' + migrate_type + '\n')
        if not installed_found:
            new_lines.append('installed : %(storage)s/' + installed + '\n')

        with fileopen(new_conf, 'w') as laymanconf:
            for line in new_lines:
                laymanconf.write(line)
Exemple #18
0
    def test(self):
        repo_name = 'tar_test_overlay'
        temp_dir_path = tempfile.mkdtemp(prefix='laymantmp_')
        db_file = os.path.join(temp_dir_path, 'installed.xml')
        make_conf = os.path.join(temp_dir_path, 'make.conf')
        repo_conf = os.path.join(temp_dir_path, 'repos.conf')

        tar_source_path = os.path.join(HERE, 'testfiles',
                                       'layman-test.tar.bz2')

        (_, temp_tarball_path) = tempfile.mkstemp()
        shutil.copyfile(tar_source_path, temp_tarball_path)

        # Write overlay collection XML
        xml_text = '''\
<?xml version="1.0" encoding="UTF-8"?>
<repositories xmlns="" version="1.0">
  <repo quality="experimental" status="unofficial">
    <name>%(repo_name)s</name>
    <description>XXXXXXXXXXX</description>
    <owner>
      <email>[email protected]</email>
    </owner>
    <source type="tar">file://%(temp_tarball_url)s</source>
  </repo>
</repositories>
        '''\
        % {
            'temp_tarball_url': urllib.pathname2url(temp_tarball_path),
            'repo_name': repo_name
          }

        (fd, temp_xml_path) = tempfile.mkstemp()

        my_opts = {
            'installed': temp_xml_path,
            'conf_type': ['make.conf', 'repos.conf'],
            'db_type': 'xml',
            'nocheck': 'yes',
            'make_conf': make_conf,
            'repos_conf': repo_conf,
            'storage': temp_dir_path,
            'check_official': False
        }

        with os.fdopen(fd, 'w') as f:
            f.write(xml_text)

        with fileopen(make_conf, 'w') as f:
            f.write('PORTDIR_OVERLAY="$PORTDIR_OVERLAY"\n')

        with fileopen(repo_conf, 'w') as f:
            f.write('')

        config = OptionConfig(options=my_opts)
        config.set_option('quietness', 3)

        a = DB(config)
        config.set_option('installed', db_file)

        # Add an overlay to a fresh DB file.
        b = DB(config)
        b.add(a.select(repo_name))

        # Make sure it's actually installed.
        specific_overlay_path = os.path.join(temp_dir_path, repo_name)
        self.assertTrue(os.path.exists(specific_overlay_path))

        # Check the DbBase to ensure that it's reading the installed.xml.
        c = DbBase(config, paths=[
            db_file,
        ])
        self.assertEqual(list(c.overlays), ['tar_test_overlay'])

        # Make sure the configs have been written to correctly.
        conf = RepoConfManager(config, b.overlays)
        self.assertEqual(list(conf.overlays), ['tar_test_overlay'])

        # Delete the overlay from the second DB.
        b.delete(b.select(repo_name))
        self.assertEqual(b.overlays, {})

        # Ensure the installed.xml has been cleaned properly.
        c = DbBase(config, paths=[
            db_file,
        ])
        self.assertEqual(c.overlays, {})

        conf = RepoConfManager(config, b.overlays)
        self.assertEqual(conf.overlays, {})

        # Clean up.
        os.unlink(temp_xml_path)
        os.unlink(temp_tarball_path)
        shutil.rmtree(temp_dir_path)
Exemple #19
0
class Interactive(object):

    def __init__(self, config=None):
        self.config = config
        if self.config:
           reload_config(self.config)
        self.layman_inst = LaymanAPI(config=self.config)
        self.output = self.config.get_option('output')
        self.overlay = {}
        self.overlays = []
        self.overlays_available = self.layman_inst.get_available()
        self.supported_types = list(self.layman_inst.supported_types())


    def args_parser(self):
        self.parser = argparse.ArgumentParser(prog='layman-overlay-maker',
            description='Layman\'s utility script to create overlay'\
                        'definitions.')
        self.parser.add_argument('-l',
                                 '--list-autocomplete',
                                 action='store_true',
                                 help='Lists all available mirrors supported'\
                                 ' for information auto-completion'
                                )
        self.parser.add_argument('-n',
                                 '--no-extra',
                                 action='store_true',
                                 help='Don\'t add any extra overlay info,'\
                                 ' just the bare minimum requirements')
        self.parser.add_argument('-s',
                                 '--set-autocomplete',
                                 nargs='+',
                                 help='Enables auto-completion support for'\
                                 ' the selected mirror. Specify "ALL" to'\
                                 ' enable support for all available mirrors')
        self.parser.add_argument('-S',
                                 '--sudo',
                                 action='store_true',
                                 help='Bypass checks to see if an overlay'\
                                 ' name being inputted is already being used'\
                                 ' and choose the installation dir for the'\
                                 ' XML')
        self.parser.add_argument('-V',
                                 '--version',
                                 action='version',
                                 version='%(prog)s ' + VERSION)

        self.args = self.parser.parse_args()


    def __call__(self, overlay_package=None, path=None):

        if not overlay_package:
            self.args_parser()
            options = {}
            for key in vars(self.args):
                options[key] = vars(self.args)[key]
            self.config = OptionConfig(options=options)
            reload_config(self.config)

            self.auto_complete = False
            self.list_info     = self.config.get_option('list_autocomplete')
            self.no_extra      = self.config.get_option('no_extra')
            self.sudo          = self.config.get_option('sudo')

            if self.list_info:
                self.list_templates()

            if self.args.set_autocomplete:
                if 'ALL' in self.args.set_autocomplete:
                    self.templates = AUTOCOMPLETE_TEMPLATE.keys()
                    self.auto_complete = True
                else:
                    self.templates = self.args.set_autocomplete
                    self.auto_complete = True

            msg = 'How many overlays would you like to create?: '
            for x in range(1, int(get_input(msg))+1):
                self.output.notice('')
                self.output.info('Overlay #%(x)s: ' % ({'x': str(x)}))
                self.output.info('~~~~~~~~~~~~~')

                self.required = copy.deepcopy(COMPONENT_DEFAULTS)

                if not self.no_extra:
                    self.output.notice('')
                    self.update_required()
                    self.output.notice('')

                self.get_overlay_components()
                ovl = Overlay.Overlay(config=self.config, ovl_dict=self.overlay, ignore=1)
                self.overlays.append((self.overlay['name'], ovl))
        else:
            ovl_name, ovl = overlay_package
            self.overlays.append((ovl_name, ovl))

        result = self.write(path)
        return result


    def check_overlay_type(self, ovl_type):
        '''
        Validates overlay type.

        @params ovl_type: str of overlay type
        @rtype None or str (if overlay type is valid).
        '''
        if ovl_type.lower() in self.supported_types:
            return ovl_type.lower()

        msg = '!!! Specified type "%(type)s" not valid.' % ({'type': ovl_type})
        self.output.warn(msg)
        msg = 'Supported types include: %(types)s.'\
              % ({'types': ', '.join(self.supported_types)})
        self.output.warn(msg)
        return None


    def guess_overlay_type(self, source_uri):
        '''
        Guesses the overlay type based on the source given.

        @params source-uri: str of source.
        @rtype None or str (if overlay type was guessed correctly).
        '''

        type_checks = copy.deepcopy(self.supported_types)

        #Modify the type checks for special overlay types.
        if 'tar' in type_checks:
            type_checks.remove(type_checks[type_checks.index('tar')])
            type_checks.insert(len(type_checks), '.tar')

        if 'bzr' in type_checks:
            type_checks.remove(self.supported_types[type_checks.index('bzr')])
            type_checks.insert(len(type_checks), 'bazaar')

        for guess in type_checks:
            if guess in source_uri:
                return guess

        if 'bitbucket.org' in source_uri:
            return 'mercurial'

        return None


    def update_required(self):
        '''
        Prompts user for optional components and updates
        the required components accordingly.
        '''
        for possible in POSSIBLE_COMPONENTS:
            if possible not in self.required:
                msg = 'Include %(comp)s for this overlay? [y/n]: '\
                        % ({'comp': possible})
                if ((possible in 'homepage' or possible in 'feed') and
                    self.auto_complete):
                    available = False
                else:
                    available = get_ans(msg)
                if available:
                    self.required.append(possible)


    def get_description(self):
        '''
        Prompts user for an overlay's description(s)
        and updates overlay dict with value(s).
        '''
        #TODO: Currently a "stub" function. Add multiple description
        # field support later down the road.
        descriptions = []

        desc = get_input('Define overlay\'s description: ')
        descriptions.append(desc)

        self.overlay['description'] = descriptions


    def get_feed(self):
        '''
        Prompts user for any overlay RSS feeds
        and updates overlay dict with values.
        '''
        msg = 'How many RSS feeds exist for this overlay?: '
        feed_amount = int(get_input(msg))
        feeds = []

        for i in range(1, feed_amount + 1):
            extra = ''
            if feed_amount > 1:
                extra = '[%(i)s]' % {'i': str(i)}
            feeds.append(get_input('Define overlay%(extra)s feed: '\
                         % {'extra': extra}))

        self.overlay['feed'] = feeds
        self.output.notice('')


    def get_name(self):
        '''
        Prompts user for the overlay name
        and updates the overlay dict.
        '''
        name = get_input('Define overlay name: ')

        if not self.sudo:
            while name in self.overlays_available:
                msg = '!!! Overlay name already defined in list of installed'\
                      ' overlays.'
                self.output.warn(msg)
                msg = 'Please specify a different overlay name: '
                name = get_input(msg, color='yellow')

        self.overlay['name'] = name


    def get_source(self):
        '''
        Prompts user for possible overlay source
        information such as type, url, and branch
        and then appends the values to the overlay
        being created.
        '''
        ovl_type = None

        if self.auto_complete:
            source_amount = 1
        else:
            msg = 'How many different sources, protocols, or mirrors exist '\
                  'for this overlay?: '
            source_amount = int(get_input(msg))

        self.overlay['source'] = []

        for i in range(1, source_amount + 1):
            sources = []
            correct = False
            extra = ''
            if source_amount > 1:
                extra = '[%(i)s]\'s' % {'i': str(i)}

            msg = 'Define source%(extra)s URL: ' % {'extra': extra}
            sources.append(get_input(msg))

            ovl_type = self.guess_overlay_type(sources[0])
            if ovl_type:
                msg = 'Is "%(type)s" the correct overlay type?: '\
                    % ({'type': ovl_type})
                correct = get_ans(msg)
            while not ovl_type or not correct:
                msg = 'Please provide overlay type: '
                ovl_type = self.check_overlay_type(\
                            get_input(msg, color='yellow'))
                correct = True

            sources.append(ovl_type)
            if 'branch' in self.required:
                msg = 'Define source%(extra)s branch (if applicable): '\
                      % {'extra': extra}
                sources.append(get_input(msg))
            else:
                sources.append('')

            if self.auto_complete:
                sources = self._set_additional_info(sources)
                for source in sources:
                    self.overlay['source'].append(source)
            else:
                self.overlay['source'].append(sources)
        self.output.notice('')


    def get_owner(self):
        '''
        Prompts user for overlay owner info and
        then appends the values to the overlay
        being created.
        '''
        self.overlay['owner'] = []
        self.output.notice('')

        msg = 'How many people own this overlay?: '
        owner_amount = int(get_input(msg))

        for i in range(1, owner_amount + 1):
            owner = {}
            extra = ''

            if owner_amount > 1:
                extra = '[%(i)s]\'s' % {'i': str(i)}

            owner['name'] = get_input('Define owner%(extra)s name: '\
                            % {'extra': extra})
            owner['email'] = get_input('Define owner%(extra)s email: '\
                             % {'extra': extra})
            self.overlay['owner'].append(owner)

        self.output.notice('')


    def get_component(self, component, msg):
        '''
        Sets overlay component value.

        @params component: (str) component to be set
        @params msg: (str) prompt message for component
        '''
        if component not in ('branch', 'type'):
            if component in ('description', 'feed', 'name', 'owner', 'source'):
                getattr(self, 'get_%(comp)s' % ({'comp': component}))()
            else:
                self.overlay[component] = get_input(msg)


    def get_overlay_components(self):
        '''
        Acquires overlay components via user interface.
        '''
        self.overlay = {}

        for component in self.required:
            self.get_component(component, 'Define %(comp)s: '\
                                % ({'comp': component}))


    def list_templates(self):
        '''
        Lists all available mirrors that support information auto-completion
        and exits.
        '''
        self.output.info('Mirrors supported for information auto-completion: ')
        self.output.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
        for i in sorted(AUTOCOMPLETE_TEMPLATE):
            self.output.info(i)
        sys.exit()


    def read(self, path):
        '''
        Reads overlay.xml files and appends the contents
        to be sorted when writing to the file.

        @params path: path to file to be read
        '''
        try:
            document = ET.parse(path)
        except ET.ParseError as error:
            msg = 'Interactive.read(); encountered error: %(error)s'\
                % ({'error': error})
            raise Exception(msg)

        overlays = document.findall('overlay') + document.findall('repo')

        for overlay in overlays:
            ovl_name = overlay.find('name')
            ovl = Overlay.Overlay(config=self.config, xml=overlay, ignore=1)
            self.overlays.append((ovl_name.text, ovl))


    def _set_additional_info(self, source):
        '''
        Sets additional possible overlay information.

        @params source: list of the source URL, type, and branch.
        '''
        feeds = []
        sources = []
        url = self._split_source_url(source[0])
        attrs = {'branch': source[2], 'tail': '/'.join(url[3:])}
        mirror = url[2].split('.')

        for i in self.templates:
            if i in mirror:
                if i not in ('gentoo'):
                    attrs['info'] = attrs['tail'].replace('.git', '')

                if attrs['branch']:
                    try:
                        TEMPLATE = AUTOCOMPLETE_TEMPLATE[i+'-branch']
                    except KeyError:
                        TEMPLATE = AUTOCOMPLETE_TEMPLATE[i]
                else:
                    TEMPLATE = AUTOCOMPLETE_TEMPLATE[i]

                self.overlay['homepage'] = TEMPLATE['homepage'] % attrs

                if i in ('bitbucket') and 'git' in (source[1]):
                    return [source]

                for s in TEMPLATE['source']:
                    source = (s[0] % attrs, s[1], s[2] % attrs)
                    sources.append(source)
                for f in TEMPLATE['feed']:
                    feed = (f % attrs)
                    feeds.append(feed)
                self.overlay['feed'] = feeds

        if sources:
            return sources

        return [source]


    def _split_source_url(self, source_url):
        '''
        Splits the given source URL based on
        the source URL type.

        @params source_url: str, represents the URL for the repo.
        @rtype str: The newly split url components.
        '''
        url = None
        if re.search("^(git://)|(http://)|(https://)|(ssh://)", source_url):
            url = source_url.split('/')
        if re.search('^git\+ssh://', source_url):
            url = source_url.replace('+ssh', '')
            url = url.replace('git@', '').split('/')
        if re.search('^git@', source_url):
            url = source_url.replace('@', '//')
            url = url.replace(':', '/')
            url = url.replace('//', '://').split('/')
        if url:
            return url
        raise Exception('Interactive._split_source_url(); error: Unable '\
                        'to split URL.')


    def _sort_to_tree(self):
        '''
        Sorts all Overlay objects by overlay name
        and converts the sorted overlay objects to
        XML that is then appended to the tree.
        '''
        self.overlays = sorted(self.overlays)
        for overlay in self.overlays:
            self.tree.append(overlay[1].to_xml())


    def write(self, destination):
        '''
        Writes overlay file to desired location.

        @params destination: path & file to write xml to.
        @rtype bool: reflects success or failure to write xml.
        '''
        if not destination:
            filepath = self.config.get_option('overlay_defs')
            if self.sudo:
                filepath = get_input('Desired file destination dir: ')
            filename = get_input('Desired overlay file name: ')

            if not filename.endswith('.xml'):
                filename += ".xml"

            if not filepath.endswith(os.path.sep):
                filepath += os.path.sep

            destination = filepath + filename

        self.tree = ET.Element('repositories', version='1.1', encoding=_UNICODE)

        if os.path.isfile(destination):
            self.read(destination)

        self._sort_to_tree()
        indent(self.tree)
        self.tree = ET.ElementTree(self.tree)

        try:
            with fileopen(destination, 'w') as xml:
                self.tree.write(xml, encoding=_UNICODE)
            msg = 'Successfully wrote repo(s) to: %(path)s'\
                  % ({'path': destination})
            self.output.info(msg)
            return True

        except IOError as e:
            raise Exception("Writing XML failed: %(error)s" % ({'error': e}))
Exemple #20
0
class Interactive(object):
    def __init__(self, config=None):
        self.config = config
        if self.config:
            reload_config(self.config)
        self.layman_inst = LaymanAPI(config=self.config)
        self.output = self.config.get_option("output")
        self.overlay = {}
        self.overlays = []
        self.overlays_available = self.layman_inst.get_available()
        self.supported_types = list(self.layman_inst.supported_types())

    def args_parser(self):
        self.parser = argparse.ArgumentParser(
            prog="layman-overlay-maker", description="Layman's utility script to create overlay" "definitions."
        )
        self.parser.add_argument(
            "-l",
            "--list-autocomplete",
            action="store_true",
            help="Lists all available mirrors supported" " for information auto-completion",
        )
        self.parser.add_argument(
            "-n",
            "--no-extra",
            action="store_true",
            help="Don't add any extra overlay info," " just the bare minimum requirements",
        )
        self.parser.add_argument(
            "-s",
            "--set-autocomplete",
            nargs="+",
            help="Enables auto-completion support for"
            ' the selected mirror. Specify "ALL" to'
            " enable support for all available mirrors",
        )
        self.parser.add_argument(
            "-S",
            "--sudo",
            action="store_true",
            help="Bypass checks to see if an overlay"
            " name being inputted is already being used"
            " and choose the installation dir for the"
            " XML",
        )
        self.parser.add_argument("-V", "--version", action="version", version="%(prog)s " + VERSION)

        self.args = self.parser.parse_args()

    def __call__(self, overlay_package=None, path=None):

        if not overlay_package:
            self.args_parser()
            options = {}
            for key in vars(self.args):
                options[key] = vars(self.args)[key]
            self.config = OptionConfig(options=options)
            reload_config(self.config)

            self.auto_complete = False
            self.list_info = self.config.get_option("list_autocomplete")
            self.no_extra = self.config.get_option("no_extra")
            self.sudo = self.config.get_option("sudo")

            if self.list_info:
                self.list_templates()

            if self.args.set_autocomplete:
                if "ALL" in self.args.set_autocomplete:
                    self.templates = AUTOCOMPLETE_TEMPLATE.keys()
                    self.auto_complete = True
                else:
                    self.templates = self.args.set_autocomplete
                    self.auto_complete = True

            msg = "How many overlays would you like to create?: "
            for x in range(1, int(get_input(msg)) + 1):
                self.output.notice("")
                self.output.info("Overlay #%(x)s: " % ({"x": str(x)}))
                self.output.info("~~~~~~~~~~~~~")

                self.required = copy.deepcopy(COMPONENT_DEFAULTS)

                if not self.no_extra:
                    self.output.notice("")
                    self.update_required()
                    self.output.notice("")

                self.get_overlay_components()
                ovl = Overlay.Overlay(config=self.config, ovl_dict=self.overlay, ignore=1)
                self.overlays.append((self.overlay["name"], ovl))
        else:
            ovl_name, ovl = overlay_package
            self.overlays.append((ovl_name, ovl))

        result = self.write(path)
        return result

    def check_overlay_type(self, ovl_type):
        """
        Validates overlay type.

        @params ovl_type: str of overlay type
        @rtype None or str (if overlay type is valid).
        """
        if ovl_type.lower() in self.supported_types:
            return ovl_type.lower()

        msg = '!!! Specified type "%(type)s" not valid.' % ({"type": ovl_type})
        self.output.warn(msg)
        msg = "Supported types include: %(types)s." % ({"types": ", ".join(self.supported_types)})
        self.output.warn(msg)
        return None

    def guess_overlay_type(self, source_uri):
        """
        Guesses the overlay type based on the source given.

        @params source-uri: str of source.
        @rtype None or str (if overlay type was guessed correctly).
        """

        type_checks = copy.deepcopy(self.supported_types)

        # Modify the type checks for special overlay types.
        if "tar" in type_checks:
            type_checks.remove(type_checks[type_checks.index("tar")])
            type_checks.insert(len(type_checks), ".tar")

        if "bzr" in type_checks:
            type_checks.remove(self.supported_types[type_checks.index("bzr")])
            type_checks.insert(len(type_checks), "bazaar")

        for guess in type_checks:
            if guess in source_uri:
                return guess

        if "bitbucket.org" in source_uri:
            return "mercurial"

        return None

    def update_required(self):
        """
        Prompts user for optional components and updates
        the required components accordingly.
        """
        for possible in POSSIBLE_COMPONENTS:
            if possible not in self.required:
                msg = "Include %(comp)s for this overlay? [y/n]: " % ({"comp": possible})
                if (possible in "homepage" or possible in "feed") and self.auto_complete:
                    available = False
                else:
                    available = get_ans(msg)
                if available:
                    self.required.append(possible)

    def get_description(self):
        """
        Prompts user for an overlay's description(s)
        and updates overlay dict with value(s).
        """
        # TODO: Currently a "stub" function. Add multiple description
        # field support later down the road.
        descriptions = []

        desc = get_input("Define overlay's description: ")
        descriptions.append(desc)

        self.overlay["description"] = descriptions

    def get_feed(self):
        """
        Prompts user for any overlay RSS feeds
        and updates overlay dict with values.
        """
        msg = "How many RSS feeds exist for this overlay?: "
        feed_amount = int(get_input(msg))
        feeds = []

        for i in range(1, feed_amount + 1):
            if feed_amount > 1:
                msg = "Define overlay feed[%(i)s]: " % ({"i": str(i)})
                feeds.append(get_input(msg))
            else:
                feeds.append(get_input("Define overlay feed: "))

        self.overlay["feed"] = feeds
        self.output.notice("")

    def get_name(self):
        """
        Prompts user for the overlay name
        and updates the overlay dict.
        """
        name = get_input("Define overlay name: ")

        if not self.sudo:
            while name in self.overlays_available:
                msg = "!!! Overlay name already defined in list of installed" " overlays."
                self.output.warn(msg)
                msg = "Please specify a different overlay name: "
                name = get_input(msg, color="yellow")

        self.overlay["name"] = name

    def get_source(self):
        """
        Prompts user for possible overlay source
        information such as type, url, and branch
        and then appends the values to the overlay
        being created.
        """
        ovl_type = None

        if self.auto_complete:
            source_amount = 1
        else:
            msg = "How many different sources, protocols, or mirrors exist " "for this overlay?: "
            source_amount = int(get_input(msg))

        self.overlay["source"] = []

        for i in range(1, source_amount + 1):
            sources = []
            correct = False
            if source_amount > 1:
                msg = "Define source[%(i)s]'s URL: " % ({"i": str(i)})
                sources.append(get_input(msg))

                ovl_type = self.guess_overlay_type(sources[0])
                if ovl_type:
                    msg = 'Is "%(type)s" the correct overlay type?: ' % ({"type": ovl_type})
                    correct = get_ans(msg)
                while not ovl_type or not correct:
                    msg = "Please provide overlay type: "
                    ovl_type = self.check_overlay_type(get_input(msg, color="yellow"))
                    correct = True

                sources.append(ovl_type)
                if "branch" in self.required:
                    msg = "Define source[%(i)s]'s branch (if applicable): " % ({"i": str(i)})
                    sources.append(get_input(msg))
                else:
                    sources.append("")
            else:
                sources.append(get_input("Define source URL: "))

                ovl_type = self.guess_overlay_type(sources[0])
                if ovl_type:
                    msg = "Is %(type)s the correct overlay type?: " % ({"type": ovl_type})
                    correct = get_ans(msg)
                while not ovl_type or not correct:
                    msg = "Please provide overlay type: "
                    ovl_type = self.check_overlay_type(get_input(msg, color="yellow"))
                    correct = True

                sources.append(ovl_type)
                if "branch" in self.required:
                    msg = "Define source branch (if applicable): "
                    sources.append(get_input(msg))
                else:
                    sources.append("")
            if self.auto_complete:
                sources = self._set_additional_info(sources)
                for source in sources:
                    self.overlay["source"].append(source)
            else:
                self.overlay["source"].append(sources)
        self.output.notice("")

    def get_owner(self):
        """
        Prompts user for overlay owner info and
        then appends the values to the overlay
        being created.
        """
        self.output.notice("")
        self.overlay["owner_name"] = get_input("Define owner name: ")
        self.overlay["owner_email"] = get_input("Define owner email: ")
        self.output.notice("")

    def get_component(self, component, msg):
        """
        Sets overlay component value.

        @params component: (str) component to be set
        @params msg: (str) prompt message for component
        """
        if component not in ("branch", "type"):
            if component in ("description", "feed", "name", "owner", "source"):
                getattr(self, "get_%(comp)s" % ({"comp": component}))()
            else:
                self.overlay[component] = get_input(msg)

    def get_overlay_components(self):
        """
        Acquires overlay components via user interface.
        """
        self.overlay = {}

        for component in self.required:
            self.get_component(component, "Define %(comp)s: " % ({"comp": component}))

    def list_templates(self):
        """
        Lists all available mirrors that support information auto-completion
        and exits.
        """
        self.output.info("Mirrors supported for information auto-completion: ")
        self.output.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
        for i in sorted(AUTOCOMPLETE_TEMPLATE):
            self.output.info(i)
        sys.exit()

    def read(self, path):
        """
        Reads overlay.xml files and appends the contents
        to be sorted when writing to the file.

        @params path: path to file to be read
        """
        try:
            document = ET.parse(path)
        except ET.ParseError as error:
            msg = "Interactive.read(); encountered error: %(error)s" % ({"error": error})
            raise Exception(msg)

        overlays = document.findall("overlay") + document.findall("repo")

        for overlay in overlays:
            ovl_name = overlay.find("name")
            ovl = Overlay.Overlay(config=self.config, xml=overlay, ignore=1)
            self.overlays.append((ovl_name.text, ovl))

    def _set_additional_info(self, source):
        """
        Sets additional possible overlay information.

        @params source: list of the source URL, type, and branch.
        """
        feeds = []
        sources = []
        url = self._split_source_url(source[0])
        attrs = {"branch": source[2], "tail": "/".join(url[3:])}
        mirror = url[2].split(".")

        for i in self.templates:
            if i in mirror:
                if i not in ("gentoo"):
                    attrs["info"] = attrs["tail"].replace(".git", "")

                if attrs["branch"]:
                    try:
                        TEMPLATE = AUTOCOMPLETE_TEMPLATE[i + "-branch"]
                    except KeyError:
                        TEMPLATE = AUTOCOMPLETE_TEMPLATE[i]
                else:
                    TEMPLATE = AUTOCOMPLETE_TEMPLATE[i]

                self.overlay["homepage"] = TEMPLATE["homepage"] % attrs

                if i in ("bitbucket") and "git" in (source[1]):
                    return [source]

                for s in TEMPLATE["source"]:
                    source = (s[0] % attrs, s[1], s[2] % attrs)
                    sources.append(source)
                for f in TEMPLATE["feed"]:
                    feed = f % attrs
                    feeds.append(feed)
                self.overlay["feed"] = feeds

        if sources:
            return sources

        return [source]

    def _split_source_url(self, source_url):
        """
        Splits the given source URL based on
        the source URL type.

        @params source_url: str, represents the URL for the repo.
        @rtype str: The newly split url components.
        """
        url = None
        if re.search("^(git://)|(http://)|(https://)|(ssh://)", source_url):
            url = source_url.split("/")
        if re.search("^git\+ssh://", source_url):
            url = source_url.replace("+ssh", "")
            url = url.replace("git@", "").split("/")
        if re.search("^git@", source_url):
            url = source_url.replace("@", "//")
            url = url.replace(":", "/")
            url = url.replace("//", "://").split("/")
        if url:
            return url
        raise Exception("Interactive._split_source_url(); error: Unable " "to split URL.")

    def _sort_to_tree(self):
        """
        Sorts all Overlay objects by overlay name
        and converts the sorted overlay objects to
        XML that is then appended to the tree.
        """
        self.overlays = sorted(self.overlays)
        for overlay in self.overlays:
            self.tree.append(overlay[1].to_xml())

    def write(self, destination):
        """
        Writes overlay file to desired location.

        @params destination: path & file to write xml to.
        @rtype bool: reflects success or failure to write xml.
        """
        if not destination:
            filepath = self.config.get_option("overlay_defs")
            if self.sudo:
                filepath = get_input("Desired file destination dir: ")
            filename = get_input("Desired overlay file name: ")

            if not filename.endswith(".xml"):
                filename += ".xml"

            if not filepath.endswith(os.path.sep):
                filepath += os.path.sep

            destination = filepath + filename

        self.tree = ET.Element("repositories", version="1.1", encoding=_UNICODE)

        if os.path.isfile(destination):
            self.read(destination)

        self._sort_to_tree()
        indent(self.tree)
        self.tree = ET.ElementTree(self.tree)

        try:
            with fileopen(destination, "w") as xml:
                self.tree.write(xml, encoding=_UNICODE)
            msg = "Successfully wrote repo(s) to: %(path)s" % ({"path": destination})
            self.output.info(msg)
            return True

        except IOError as e:
            raise Exception("Writing XML failed: %(error)s" % ({"error": e}))
Exemple #21
0
    def test(self):
        tmpdir = tempfile.mkdtemp(prefix='laymantmp_')
        makeconf = os.path.join(tmpdir, 'make.conf')
        reposconf = os.path.join(tmpdir, 'repos.conf')

        make_txt =\
        'PORTDIR_OVERLAY="\n'\
        '$PORTDIR_OVERLAY"'

        # Create the .conf files so layman doesn't
        # complain.
        with fileopen(makeconf, 'w') as f:
            f.write(make_txt)

        with fileopen(reposconf, 'w') as f:
            f.write('')

        my_opts = {
                   'installed' :
                   HERE + '/testfiles/global-overlays.xml',
                   'make_conf' : makeconf,
                   'nocheck'    : 'yes',
                   'storage'   : tmpdir,
                   'repos_conf' : reposconf,
                   'conf_type' : ['make.conf', 'repos.conf'],
                   }

        config = OptionConfig(my_opts)
        config.set_option('quietness', 3)

        a = DB(config)
        config['output'].set_colorize(False)

        conf = RepoConfManager(config, a.overlays)

        # Set up our success tracker.
        success = []

        # Add all the overlays in global_overlays.xml.
        for overlay in a.overlays.keys():
            conf_success = conf.add(a.overlays[overlay])
            if False in conf_success:
                success.append(False)
            else:
                success.append(True)

        # Disable one overlay.
        conf_success = conf.disable(a.overlays['wrobel'])
        if False in conf_success:
            success.append(False)
        else:
            success.append(True)

        # Enable disabled overlay.
        conf_success = conf.enable(a.overlays['wrobel'])
        if False in conf_success:
            success.append(False)
        else:
            success.append(True)
        # Delete all the overlays in global_overlays.xml.
        for overlay in a.overlays.keys():
            conf_success = conf.delete(a.overlays[overlay])
            if False in conf_success:
                success.append(False)
            else:
                success.append(True)

        # Clean up.
        os.unlink(makeconf)
        os.unlink(reposconf)

        shutil.rmtree(tmpdir)

        if False in success:
            success = False
        else:
            success = True

        self.assertTrue(success)
Exemple #22
0
class Main(object):

    def __init__(self, root=None, config=None, output=None):
        self.parser = None
        self.output = output
        self.config = config
        self.args = None
        self.root = root

    def args_parser(self):
        self.parser = argparse.ArgumentParser(prog='layman-updater',
            description="Layman's update script")
        self.parser.add_argument("-H", '--setup_help', action='store_true',
            help = 'Print the NEW INSTALL help messages.')
        self.parser.add_argument("-c", "--config",
            help='the path to config file')
        self.parser.add_argument('--version', action='version',
            version='%(prog)s ' + VERSION)

        self.args = self.parser.parse_args()

    def __call__(self):
        self.args_parser()
        options = None
        if self.args.config:
            options = {
                'config': self.args.config,
            }

        self.config = OptionConfig(options=options, root=self.root)
        # fix the config path
        defaults = self.config.get_defaults()
        defaults['config'] = defaults['config'] \
            % {'configdir': defaults['configdir']}
        self.config.update_defaults({'config': defaults['config']})

        self.config.read_config(defaults)

        layman_inst = LaymanAPI(config=self.config)

        self.output = layman_inst.output

        if self.args.setup_help:
            self.print_instructions()
        elif not self.check_is_new():
            self.rename_check()


    def check_is_new(self):
        print_instructions = False
        if isinstance(self.config['conf_type'], STR):
            self.config.set_option('conf_type',
                                   self.config['conf_type'].split(', '))
        for i in self.config['conf_type']:
            conf = i.replace('.', '_')
            if not os.access(self.config[conf], os.F_OK):
                getattr(self, 'create_%(conf)s' % {'conf': conf})()
                print_instructions = True
        if print_instructions:
            self.print_instructions()
            return True
        return False


    def rename_check(self):
        '''Checks for and renames the installed db if needed
        '''
        newname = self.config['installed']

        # check and handle the name change
        if not os.access(newname, os.F_OK):
            if os.access(self.config['local_list'], os.F_OK):
                self.output.info("  Layman automatic db rename utility, "
                    "performing update", 2)
                rename_db(self.config, newname, self.output)
        elif os.access(newname, os.F_OK) and \
            os.access(self.config['local_list'], os.F_OK):
            self.output.error("  Automatic db rename failed: "
                "Both old and new files exist")
            self.output.error(" Old file: %s still exists"
                % self.config['local_list'])
            self.output.error("  New file: %s already exists" % newname)
        elif os.access(newname, os.F_OK):
            self.output.info("  Automatic db rename: "
                "db already updated: %s" % newname)
        else:
            self.output.info("  Automatic db rename: "
                "nothing to update")
        return


    def print_instructions(self):
        make_conf = '/etc/portage/make.conf'
        if not os.access(make_conf, os.F_OK):
            make_conf = '/etc/make.conf'
        messages = [
            "You are now ready to add overlays into your system.",
            "",
            "  layman -L",
            "",
            "will display a list of available overlays.",
            "",
            "Select an overlay and add it using",
            "",
            "  layman -a overlay-name",
            "",
            "If this is the very first overlay you add with layman,",
            "you need to append the following statement to your",
            "%s file:" %make_conf,
            "",
            "  source /var/lib/layman/make.conf",
            "",
            "If you modify the 'storage' parameter in the layman",
            "configuration file (/etc/layman/layman.cfg) you will",
            "need to adapt the path given above to the new storage",
            "directory.",
            "",
        ]

        for message in messages:
            self.output.info("  " + message)


    def create_make_conf(self):
        self.output.info("  Creating layman's make.conf file")
        # create layman's %(storage)s/make.conf
        # so portage won't error
        from layman.config_modules.makeconf.makeconf import ConfigHandler
        maker = ConfigHandler(self.config, None)
        maker.write()


    def create_repos_conf(self):
        self.output.info("  Creating layman's repos.conf file")
        layman_inst = LaymanAPI(config=self.config)
        overlays = {}
        for ovl in layman_inst.get_installed():
            overlays[ovl] = layman_inst._get_installed_db().select(ovl)
        # create layman's %(repos_conf) so layman
        # can write the overlays to it.
        open(self.config['repos_conf'], 'w').close()
        from layman.config_modules.reposconf.reposconf import ConfigHandler
        repos_conf = ConfigHandler(self.config, overlays)
        repos_conf.write()
Exemple #23
0
class Main(object):

    def __init__(self, root=None):
        self.parser = None
        self.output = None
        self.config = None
        self.args = None
        self.root = root

    def args_parser(self):
        self.parser = argparse.ArgumentParser(prog='layman-updater',
            description="Layman's update script")
        self.parser.add_argument("-c", "--config",
            help='the path to config file')
        self.parser.add_argument('--version', action='version',
            version='%(prog)s ' + VERSION)
        self.args = self.parser.parse_args()

    def __call__(self):
        self.args_parser()
        options = None
        if self.args.config:
            options = {
                'config': self.args.config,
            }

        self.config = OptionConfig(options=options, root=self.root)
        # fix the config path
        defaults = self.config.get_defaults()
        defaults['config'] = defaults['config'] \
                % {'configdir': defaults['configdir']}
        self.config.update_defaults({'config': defaults['config']})

        self.config.read_config(defaults)

        layman_inst = LaymanAPI(config=self.config)

        self.output = layman_inst.output

        if not self.check_is_new():
            self.rename_check()


    def check_is_new(self):
        if not os.access(self.config['make_conf'], os.F_OK):
            self.create_make_conf()
            self.print_instructions()
            return True
        return False


    def rename_check(self):
        '''Checks for and renames the installed db if needed
        '''
        newname = self.config['installed']

        # check and handle the name change
        if not os.access(newname, os.F_OK):
            if os.access(self.config['local_list'], os.F_OK):
                self.output.info("  Layman automatic db rename utility, "
                    "performing update", 2)
                rename_db(self.config, newname, self.output)
        elif os.access(newname, os.F_OK) and \
            os.access(self.config['local_list'], os.F_OK):
            self.output.error("  Automatic db rename failed: "
                "Both old and new files exist")
            self.output.error(" Old file: %s still exists"
                % self.config['local_list'])
            self.output.error("  New file: %s already exists" % newname)
        elif os.access(newname, os.F_OK):
            self.output.info("  Automatic db rename: "
                "db already updated: %s" % newname)
        else:
            self.output.info("  Automatic db rename: "
                "nothing to update")
        return


    def print_instructions(self):
        make_conf = '/etc/portage/make.conf'
        if not os.access(make_conf, os.F_OK):
            make_conf = '/etc/make.conf'
        messages = [
            "You are now ready to add overlays into your system.",
            "",
            "  layman -L",
            "",
            "will display a list of available overlays.",
            "",
            "Select an overlay and add it using",
            "",
            "  layman -a overlay-name",
            "",
            "If this is the very first overlay you add with layman,",
            "you need to append the following statement to your",
            "%s file:" %make_conf,
            "",
            "  source /var/lib/layman/make.conf",
            "",
            "If you modify the 'storage' parameter in the layman",
            "configuration file (/etc/layman/layman.cfg) you will",
            "need to adapt the path given above to the new storage",
            "directory.",
            "",
        ]

        for message in messages:
            self.output.info("  " + message)


    def create_make_conf(self):
        self.output.info("  Creating layman's make.conf file")
        # create layman's %(storage)s/make.conf
        # so portage won't error
        from layman.makeconf import MakeConf
        maker = MakeConf(self.config, None)
        maker.write()