def _parse_spec(self, spec): # auto convert spec string if type(spec) is str: try: spec = Spec.parse(spec) except ValueError as e: log_path.error("error parsing spec: %r -> %s", spec, e) return None # check spec append = spec.get_append() name = spec.get_name() cfg = spec.get_cfg() elements = spec.get_src_list() if len(elements) == 0: log_path.error("no elements in assign: %r", spec) return None # append? if append: assign = self.get_assign(name) if assign: log_path.info( "appending to existing assign: name='%s': %r", name, elements ) assign.append(elements) return assign log_path.warning("can't append to non-existing assign: '%s'", name) # fall through log_path.info("create new assign: name='%s': %r", name, elements) return Assign(name, elements, cfg)
def sys_to_ami_path(self, sys_path): """try to map an absolute system path back to an amiga path if multiple volumes overlap then take the shortest amiga path return ami_path or None if sys_path can't be mapped """ if not os.path.isabs(sys_path): log_path.error("vol: sys_to_ami_path: no abs path: '%s'", sys_path) return None res_len = None result = None for vol_sys_path in self.sys2volume: cp = os.path.commonprefix([vol_sys_path, sys_path]) if cp == vol_sys_path: remainder = sys_path[len(vol_sys_path):] n = len(remainder) if n > 0 and remainder[0] == '/': remainder = remainder[1:] n -= 1 # get volume name and build amiga path vol_name = self.sys2volume[vol_sys_path] vol_name = self.orig_names[vol_name] ami_path = vol_name + ":" + remainder log_path.debug("vol: sys_to_ami_path: sys='%s' -> ami='%s'", sys_path, ami_path) if result is None or n < res_len: result = ami_path res_len = n # return best result log_path.info("vol: sys_to_ami_path: sys='%s' -> ami=%s", sys_path, result) return result
def _add_auto_volumes(self): vm = self.get_vol_mgr() am = self.get_assign_mgr() # require a default 'system' volume if none is given if vm.get_num_volumes() == 0: log_path.info("no volume exists: auto adding default 'system:' volume") if not vm.add_volume('system:?create'): return False # run through auto volumes for av in self.auto_volumes: av = av.lower() if not am.is_assign(av) and not vm.is_volume(av): if av == 'root': spec = "root:/" elif av == 'ram': spec = "ram:?temp" else: log_path.error("invalid auto volume: '%s'", av) return False # add auto volume log_path.info("auto adding volume: %s", spec) if not vm.add_volume(spec): return False # done return True
def _parse_spec(self, spec): # auto parse string spec if type(spec) is str: try: spec = Spec.parse(spec) except ValueError as e: log_path.error("error parsing spec: %r -> %s", spec, e) return None # check spec name = spec.get_name() src_list = spec.get_src_list() cfg = spec.get_cfg() n = len(src_list) if n == 0: # local path path = self._get_local_vol_path(name) log_path.debug("local path='%s'", path) if path is None: return None elif n == 1: path = resolve_sys_path(src_list[0]) log_path.debug("resolved path='%s'", path) if n > 1: log_path.error("only one source in volume spec allowed!") return None log_path.debug("name='%s', path='%s'", name, path) # create volume return Volume(name, path, cfg)
def _add_auto_volumes(self): vm = self.get_vol_mgr() am = self.get_assign_mgr() # require a default 'system' volume if none is given if vm.get_num_volumes() == 0: log_path.info( "no volume exists: auto adding default 'system:' volume") if not vm.add_volume('system:?create'): return False # run through auto volumes for av in self.auto_volumes: av = av.lower() if not am.is_assign(av) and not vm.is_volume(av): if av == 'root': spec = "root:/" elif av == 'ram': spec = "ram:?temp" else: log_path.error("invalid auto volume: '%s'", av) return False # add auto volume log_path.info("auto adding volume: %s", spec) if not vm.add_volume(spec): return False # done return True
def _parse_spec(self, spec): # auto convert spec string if type(spec) is str: try: spec = Spec.parse(spec) except ValueError as e: log_path.error("error parsing spec: %r -> %s", spec, e) return None # check spec append = spec.get_append() name = spec.get_name() cfg = spec.get_cfg() elements = spec.get_src_list() if len(elements) == 0: log_path.error("no elements in assign: %r", spec) return None # append? if append: assign = self.get_assign(name) if assign: log_path.info("appending to existing assign: name='%s': %r", name, elements) assign.append(elements) return assign log_path.warn("can't append to non-existing assign: '%s'", name) # fall through log_path.info("create new assign: name='%s': %r", name, elements) return Assign(name, elements, cfg)
def create_rel_sys_path(self, rel_path): if type(rel_path) in (list, tuple): rel_path = os.path.join(*rel_path) dir_path = os.path.join(self.path, rel_path) if os.path.isdir(dir_path): log_path.debug( "rel sys path in volume already exists '%s' + %s -> %s", self.name, rel_path, dir_path, ) return dir_path try: log_path.debug( "creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path, ) os.makedirs(dir_path) return dir_path except OSError as e: log_path.error( "error creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path, ) return None
def _create_path(self, path): # try to create path try: log_path.info("creating volume dir: %s", path) os.makedirs(path) return path except OSError as e: log_path.error("error creating volume dir: %s -> %s", path, e) return None
def ami_volume_of_path(self, path): ami_path = AmiPath(path) p = ami_path.prefix() if p is None: log_path.error("ami_volume_of_path: expect absolute path: '%s'", path) return None else: log_path.info("ami_volume_of_path: path='%s' -> volume='%s'", path, p) return p
def setup(self): log_path.debug("setting up assigns") for a in self.assigns: if not a.setup(self): log_path.error("error setting up assign: %s", a) return False a.is_setup = True self.is_setup = True return True
def setup(self): # setup all defined volumes log_path.debug("setting up volumes") for volume in self.volumes: if not volume.setup(): log_path.error("Error settign up volume: %s", volume) return False volume.is_setup = True self.is_setup = True return True
def _create_dir(self, volume, rel_dir): log_path.debug("%s: create assign dir: rel_dir='%s'", self.name, rel_dir) path = volume.create_rel_sys_path(rel_dir) if not path: log_path.error("assign '%s': can't create relative dir: %s", self.name, rel_dir) return False else: log_path.debug("created sys path: %s", path) return True
def validate_assigns(self): assigns = self.assign_mgr.get_all_names() for a in assigns: try: paths = self.assign_mgr.get_assign(a).get_assigns() for path in paths: self.volpaths(path, strict=True) except AmiPathError as e: log_path.error("invalid assign: '%s': %s", a, e) return False return True
def del_assign(self, name): lo_name = name.lower() if self.assigns.has_key(lo_name): alist = self.assigns[lo_name] log_path.info("del assign: name='%s' -> paths=%s", name, alist) del self.assigns[lo_name] del self.orig_names[lo_name] return True else: log_path.error("assign not found: %s", name) return False
def _ensure_volume_or_assign(self, path_name): if len(path_name) == 0: log_path.error("invalid empty assign: %s", path_name) return False # make sure it maps to a volume or another assign split = self._split_volume_remainder(path_name) if split is None: log_path.error("assign has to map to volume or another assign: %s", path_name) return False return True
def _create_dir(self, volume, rel_dir): log_path.debug("%s: create assign dir: rel_dir='%s'", self.name, rel_dir) path = volume.create_rel_sys_path(rel_dir) if not path: log_path.error( "assign '%s': can't create relative dir: %s", self.name, rel_dir ) return False else: log_path.debug("created sys path: %s", path) return True
def ami_to_sys_path(self, ami_path, fast=False): """Map an Amiga path to a system path. An absolute Amiga path with volume prefix is expected. Any other path returns None. If volume does not exist also return None. It replaces the volume with the sys_path prefix. Furthermore, the remaining Amiga path is mapped to the system file system and case corrected if a corresponding entry is found. If 'fast' mode is enabled then the original case of the path elements is kept if the underlying FS is case insensitive. Return None on error or system path """ # find volume pos = ami_path.find(':') if pos <= 0: log_path.debug("vol: ami_to_sys_path: empty volume: %s", ami_path) return None vol_name = ami_path[:pos].lower() # check volume name if vol_name in self.vols_by_name: volume = self.vols_by_name[vol_name] vol_sys_path = volume.get_path() remainder = ami_path[pos + 1:] # only volume name given if len(remainder) == 0: log_path.info("vol: direct volume: ami='%s' -> sys='%s'", ami_path, vol_sys_path) return vol_sys_path # invalid volume:/... path if remainder[0] == '/': log_path.error("vol: ami_to_sys_path: invalid :/ path: %s", ami_path) return None # follow ami path along in sys world dirs = remainder.split('/') sys_path = self._follow_path_no_case(vol_sys_path, dirs, fast) log_path.info("vol: ami_to_sys_path: ami='%s' -> sys='%s'", ami_path, sys_path) return sys_path else: log_path.error("vol: ami_to_sys_path: volume='%s' not found: %s", vol_name, ami_path) return None
def _create_temp(self, path): if os.path.exists(path): log_path.error("temp volume volume path already exists: '%s'", path) return False # create temp dir try: log_path.debug("creating temp dir: %s", path) os.makedirs(path) return True except OSError: log_path.error("error creating temp dir: '%s'", path) return False
def ami_to_sys_path(self, ami_path, fast=False): """Map an Amiga path to a system path. An absolute Amiga path with volume prefix is expected. Any other path returns None. If volume does not exist also return None. It replaces the volume with the sys_path prefix. Furthermore, the remaining Amiga path is mapped to the system file system and case corrected if a corresponding entry is found. If 'fast' mode is enabled then the original case of the path elements is kept if the underlying FS is case insensitive. Return None on error or system path """ # find volume pos = ami_path.find(':') if pos <= 0: log_path.debug("vol: ami_to_sys_path: empty volume: %s", ami_path) return None vol_name = ami_path[:pos].lower() # check volume name if vol_name in self.vols_by_name: volume = self.vols_by_name[vol_name] vol_sys_path = volume.get_path() remainder = ami_path[pos+1:] # only volume name given if len(remainder) == 0: log_path.info("vol: direct volume: ami='%s' -> sys='%s'", ami_path, vol_sys_path) return vol_sys_path # invalid volume:/... path if remainder[0] == '/': log_path.error("vol: ami_to_sys_path: invalid :/ path: %s", ami_path) return None # follow ami path along in sys world dirs = remainder.split('/') sys_path = self._follow_path_no_case(vol_sys_path, dirs, fast) log_path.info("vol: ami_to_sys_path: ami='%s' -> sys='%s'", ami_path, sys_path) return sys_path else: log_path.error("vol: ami_to_sys_path: volume='%s' not found: %s", vol_name, ami_path) return None
def setup(self): path = self.path # temp dir? if 'temp' in self.cfg: if not self._create_temp(path): return False # does path exist? elif not os.path.isdir(path): if 'create' in self.cfg: if not self._create_path(path): return False else: log_path.error("volume path does not exist: '%s'", path) return False return True
def create_rel_sys_path(self, rel_path): if type(rel_path) in (list, tuple): rel_path = os.path.join(*rel_path) dir_path = os.path.join(self.path, rel_path) if os.path.isdir(dir_path): log_path.debug("rel sys path in volume already exists '%s' + %s -> %s", self.name, rel_path, dir_path) return dir_path try: log_path.debug("creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path) os.makedirs(dir_path) return dir_path except OSError as e: log_path.error("error creating rel sys path in volume '%s' + %s -> %s", self.name, rel_path, dir_path) return None
def _setup_base_dir(self): # ensure that vols_base_dir exists base_dir = self.vols_base_dir if not base_dir: raise ValueError("volume manager: no base dir given!") base_dir = resolve_sys_path(base_dir) if not os.path.isdir(base_dir): try: log_path.info("creating volume base dir: %s", base_dir) os.makedirs(base_dir) except OSError as e: log_path.error("error creating volume base dir: %s -> %s", base_dir, e) return None else: log_path.debug("found base dir: %s", base_dir) return base_dir
def parse_config(self, cfg): if cfg is None: return True path = cfg.path if path is None: return True if path.cwd: self.cwd.set(path.cwd) if path.command: self.cmd_paths.set(path.command) # enforce validation try: log_path.debug("env: parse_config: %s", cfg) self.cwd.resolve() self.cmd_paths.resolve() log_path.debug("env: done: %s", self) except AmiPathError as e: log_path.error("env: parse config error: %s", e) return False return True
def add_assign(self, spec): assign = self._parse_spec(spec) if assign is None: return None # check name: is volume? lo_name = assign.get_lo_name() if self.vol_mgr.is_volume(lo_name): log_path.error("assign with a volume name is not allowed: %s", lo_name) return None # check name: duplicate assign elif lo_name in self.assigns_by_name: # after setup do not allow duplicate volumes if self.is_setup: log_path.error("duplicate assign: %s", lo_name) return None # before setup simply overwrite existing assign else: old_assign = self.assigns_by_name[lo_name] log_path.info("overwriting assign: %s", old_assign) self.assigns.remove(old_assign) del self.assigns_by_name[lo_name] # after global setup try to setup assign immediately if self.is_setup: if not assign.setup(self): log_path.error("error setting up assign: %s", assign) return None assign.is_setup = True # finally add assign log_path.info("adding assign: %s", assign) self.assigns_by_name[lo_name] = assign self.assigns.append(assign) return assign
def add_volume(self, spec): # get volume for spec volume = self._parse_spec(spec) if volume is None: log_path.error("invalid volume spec: '%s'", spec) return None # check if volume name already exists? lo_name = volume.get_lo_name() if lo_name in self.vols_by_name: # after setup do not allow duplicate volumes if self.is_setup: log_path.error("duplicate volume name: '%s'", volume.get_name()) return None # before setup simply overwrite existing volume else: old_volume = self.vols_by_name[lo_name] log_path.info("overwriting volume: %s", old_volume) self.volumes.remove(old_volume) del self.vols_by_name[lo_name] # after global setup try to setup volume immediately if self.is_setup: if not volume.setup(): log_path.error("error setting up volume: %s", volume) return None volume.is_setup = True # finally add volume log_path.info("adding volume: %s", volume) self.vols_by_name[lo_name] = volume self.volumes.append(volume) return volume
def setup(self, mgr): vmgr = mgr.get_volume_mgr() for a in self.assigns: log_path.info("assign '%s': checking: %s", self.name, a) # check assign volpaths = mgr.resolve_assigns(a, as_list=True) for volpath in volpaths: res = mgr._split_volume_remainder(volpath) if res is None: log_path.error("assign '%s': no absolute path: %s", self.name, volpath) return False # resolve volume volname, rel_dir = res volume = vmgr.get_volume(volname) if not volume: log_path.error("assign '%s': volume not found: %s", self.name, a) return False # create assign dir? if 'create' in self.cfg: if not self._create_dir(volume, rel_dir): return False return True
def setup(self, mgr): vmgr = mgr.get_volume_mgr() for a in self.assigns: log_path.info("assign '%s': checking: %s", self.name, a) # check assign volpaths = mgr.resolve_assigns(a, as_list=True) for volpath in volpaths: res = mgr._split_volume_remainder(volpath) if res is None: log_path.error( "assign '%s': no absolute path: %s", self.name, volpath ) return False # resolve volume volname, rel_dir = res volume = vmgr.get_volume(volname) if not volume: log_path.error("assign '%s': volume not found: %s", self.name, a) return False # create assign dir? if "create" in self.cfg: if not self._create_dir(volume, rel_dir): return False return True
def add_volume(self, name, sys_path): # ensure volume name is lower case lo_name = name.lower() # check path and name sys_path = self.resolve_sys_path(sys_path) if not os.path.isdir(sys_path): log_path.error("invalid volume path: '%s' -> %s" % (name, sys_path)) return False elif sys_path in self.sys2volume: log_path.error("duplicate volume mapping: '%s' -> %s" % (name, sys_path)) return False elif lo_name in self.volume2sys: log_path.error("duplicate volume name: '%s'", name) return False else: log_path.info("add volume: '%s:' -> %s", name, sys_path) self.volume2sys[lo_name] = sys_path self.sys2volume[sys_path] = lo_name self.orig_names[lo_name] = name return True
def add_assign(self, name, path_list, append=False): # also allow path string instead of list if type(path_list) is str: path_list = [path_list] # check name: empty? if len(name) == 0: log_path.error("empty assign added!") return False # check name: is volume? lo_name = name.lower() if self.vol_mgr.is_volume(lo_name): log_path.error("assign with a volume name: %s", name) return False # check name: duplicate assign elif lo_name in self.assigns: log_path.error("duplicate assign: %s", name) return False # check path_list alist = [] for path_name in path_list: if not self._ensure_volume_or_assign(path_name): return False alist.append(path_name) # setup assign list if append and self.assigns.has_key(lo_name): self.assigns[lo_name] += alist else: self.assigns[lo_name] = alist # save exact cased name self.orig_names[lo_name] = name log_path.info("add assign: name='%s' -> paths=%s", name, alist) return True
def _delete_temp(self, path): try: log_path.debug("removing temp dir: %s", path) shutil.rmtree(path) except: log_path.error("error removing temp dir: '%s'", path)