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)
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 __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 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)
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 _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 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)
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 _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 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)
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)
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)
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)
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)
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}))
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)
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)
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)
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}))
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}))
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)
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()
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()