def mount(source, target, fs, flags=0): if ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True ).mount(source, target, fs, flags) < 0: log.error("Failed to mount {} ({}) to {} with flags {}: {}". format(source, fs, target, flags, os.strerror(ctypes.get_errno()))) exit(1)
def inject_to_vif(self, vif): mac = get_vif_mac(vif) try: self.inject_packet(vif, mac) log.info('Inject IGMP query to vif:%s, mac:%s' % (vif, mac)) except: log.error('Inject IGMP query to vif:%s, mac:%s failed' % (vif, mac))
def setup_cgroup(domid, pid): cg_dir = get_cg_dir(domid) try: os.mkdir(cg_dir, 0o755) except OSError as e: if e.errno != errno.EEXIST: log.error("Failed to create cgroup: {}".format(cg_dir)) exit(1) try: # unbuffered write to ensure each one is flushed immediately with open(cg_dir + "/tasks", "w", 0) as tasks, \ open(cg_dir + "/devices.deny", "w", 0) as deny, \ open(cg_dir + "/devices.allow", "w", 0) as allow: # deny all deny.write("a") # grant rw access to /dev/null by default allow.write(get_ctl("/dev/null", "rw")) tasks.write(str(pid)) except (IOError, OSError, RuntimeError) as e: log.error("Failed to setup cgroup: {}".format(str(e))) exit(1)
def setup_cgroup(domid, pid): cg_dir = get_cg_dir(domid) try: os.mkdir(cg_dir, 0755) except OSError as e: if e.errno != errno.EEXIST: log.error("Failed to create cgroup: {}".format(cg_dir)) exit(1) try: # unbuffered write to ensure each one is flushed immediately with open(cg_dir + "/tasks", "w", 0) as tasks, \ open(cg_dir + "/devices.deny", "w", 0) as deny, \ open(cg_dir + "/devices.allow", "w", 0) as allow: # deny all deny.write("a") # grant rw access to /dev/null by default allow.write(get_ctl("/dev/null", "rw")) tasks.write(str(pid)) except (IOError, OSError, RuntimeError) as e: log.error("Failed to setup cgroup: {}".format(str(e))) exit(1)
def remap_netdevs(remap_list): # # rename everything sideways to safe faffing with temp renanes # for x in ( x for x in os.listdir("/sys/class/net/") if x[:3] == "eth" ): # util.runCmd2(['ip', 'link', 'set', x, 'name', 'side-'+x]) for cmd in remap_list: parse_arg(cmd) # Grab the current state from biosdevname current_eths = all_devices_all_names() current_state = [] for nic in current_eths.keys(): eth = current_eths[nic] if not ("BIOS device" in eth and "Kernel name" in eth and "Assigned MAC" in eth and "Bus Info" in eth and "all_ethN" in eth["BIOS device"] and "physical" in eth["BIOS device"]): LOG.error("Interface information for '%s' from biosdevname is " "incomplete; Discarding." % (eth.get("Kernel name", "Unknown"), )) try: current_state.append( MACPCI(eth["Assigned MAC"], eth["Bus Info"], kname=eth["Kernel name"], order=int(eth["BIOS device"]["all_ethN"][3:]), ppn=eth["BIOS device"]["physical"], label=eth.get("SMBIOS Label", ""))) except Exception, e: LOG.error("Can't generate current state for interface '%s' - " "%s" % (eth, e))
def _device_ctl(path, domid, allow): cg_dir = get_cg_dir(domid) file_name = "/devices.allow" if allow else "/devices.deny" try: with open(cg_dir + file_name, "w") as f: f.write(get_ctl(path, "rw")) except (IOError, OSError, RuntimeError) as e: log.error("Failed to {} {}: {}".format("allow" if allow else "deny", path, str(e))) exit(1)
def _device_ctl(path, domid, allow): cg_dir = get_cg_dir(domid) file_name = "/devices.allow" if allow else "/devices.deny" try: with open(cg_dir + file_name, "w") as f: f.write(get_ctl(path, "rw")) except (IOError, OSError, RuntimeError) as e: log.error("Failed to {} {}: {}".format( "allow" if allow else "deny", path, str(e))) exit(1)
def copy_ownership(src_root, src_path, dst_root, dst_path): st = os.lstat('%s/%s' % (src_root, src_path)) try: new_uid = dst_uid_map[src_uid_map[st.st_uid]] new_gid = dst_gid_map[src_gid_map[st.st_gid]] except IndexError as e: logger.error('Failed to copy ownership') logger.logException(e) return if st.st_uid != new_uid or st.st_gid != new_gid: os.lchown('%s/%s' % (dst_root, dst_path), new_uid, new_gid)
def detach(device, domid): path = dev_path(device) uid, gid = load_device_ids(device) # restore uid, gid of the device file. try: os.chown(path, uid, gid) except OSError as e: log.error("Failed to chown device file {}: {}".format(path, str(e))) exit(1) # remove device from cgroup allow list deny_device(path, domid)
def dev_path(device): # check device node pattern # example: "4-1", "1-2.1.2" pat = re.compile(r"\d+-\d+(\.\d+)*$") if pat.match(device) is None: log.error("Unexpected device node: {}".format(device)) exit(1) try: bus = read_int("/sys/bus/usb/devices/{}/busnum".format(device)) dev = read_int("/sys/bus/usb/devices/{}/devnum".format(device)) return "/dev/bus/usb/{0:03d}/{1:03d}".format(bus, dev) except (IOError, ValueError) as e: log.error("Failed to get device path {}: {}".format(device, str(e))) exit(1)
def load_device_ids(device): ids_path = get_ids_path(device) try: with open(ids_path) as f: uid, gid = map(int, f.readline().split()) except (IOError, ValueError) as e: log.error("Failed to load device ids: {}".format(str(e))) try: os.remove(ids_path) except OSError as e: # ignore and continue log.warning("Failed to remove device ids: {}".format(str(e))) return uid, gid
def save_device_ids(device): path = dev_path(device) try: stat = os.stat(path) ids_info = "{} {}".format(stat.st_uid, stat.st_gid) except OSError as e: log.error("Failed to stat {}: {}".format(path, str(e))) exit(1) try: with open(get_ids_path(device), "w") as f: f.write(ids_info) except IOError as e: log.error("Failed to save device ids {}: {}".format(path, str(e))) exit(1)
def cleanup(domid): # remove the cgroup if one has been created. if os.path.isdir(get_cg_dir(domid)): try: os.rmdir(get_cg_dir(domid)) except OSError as e: # log and continue log.error("Failed to remove cgroup qemu-{}: {}" .format(domid, str(e))) # umount /dev, /sys from chroot directory if they are mounted. root_dir = get_root_dir(domid) dev_dir = root_dir + "/dev" sys_dir = root_dir + "/sys" if os.path.isdir(dev_dir + "/bus"): umount(dev_dir) if os.path.isdir(sys_dir + "/devices"): umount(sys_dir)
def cleanup(domid): # remove the cgroup if one has been created. if os.path.isdir(get_cg_dir(domid)): try: os.rmdir(get_cg_dir(domid)) except OSError as e: # log and continue log.error("Failed to remove cgroup qemu-{}: {}".format( domid, str(e))) # umount /dev, /sys from chroot directory if they are mounted. root_dir = get_root_dir(domid) dev_dir = root_dir + "/dev" sys_dir = root_dir + "/sys" if os.path.isdir(dev_dir + "/bus"): umount(dev_dir) if os.path.isdir(sys_dir + "/devices"): umount(sys_dir)
def attach(device, domid, pid, reset_only): path = dev_path(device) # reset device try: with open(path, "w") as f: # USBDEVFS_RESET _IO('U', 20) USBDEVFS_RESET = (ord('U') << 8) | 20 fcntl.ioctl(f.fileno(), USBDEVFS_RESET, 0) except IOError as e: # log and continue log.error("Failed to reset {}: {}".format(path, str(e))) if reset_only: return save_device_ids(device) # set device file uid/gid try: os.chown(path, pwd.getpwnam("qemu_base").pw_uid + domid, grp.getgrnam("qemu_base").gr_gid + domid) except OSError as e: log.error("Failed to chown device file {}: {}".format(path, str(e))) exit(1) root_dir = get_root_dir(domid) dev_dir = root_dir + "/dev" if not os.path.isdir(root_dir) or not os.path.isdir(dev_dir): log.error("Error: The chroot or dev directory doesn't exist") exit(1) if not os.path.isdir(dev_dir + "/bus"): # first USB device to pass-through MS_BIND = 4096 # mount flags, from fs.h mount("/dev", dev_dir, "", MS_BIND) setup_cgroup(domid, pid) sys_dir = root_dir + "/sys" # sys_dir could already be mounted because of PCI pass-through if not os.path.isdir(sys_dir): try: os.mkdir(sys_dir, 0o755) except OSError: log.error("Failed to create sys dir in chroot") exit(1) if not os.path.isdir(sys_dir + "/devices"): mount("/sys", sys_dir, "sysfs") # add device to cgroup allow list allow_device(path, domid)
def attach(device, domid, pid, reset_only): path = dev_path(device) # reset device try: with open(path, "w") as f: # USBDEVFS_RESET _IO('U', 20) USBDEVFS_RESET = (ord('U') << 8) | 20 fcntl.ioctl(f.fileno(), USBDEVFS_RESET, 0) except IOError as e: # log and continue log.error("Failed to reset {}: {}".format(path, str(e))) if reset_only: return save_device_ids(device) # set device file uid/gid try: os.chown(path, pwd.getpwnam("qemu_base").pw_uid + domid, grp.getgrnam("qemu_base").gr_gid + domid) except OSError as e: log.error("Failed to chown device file {}: {}".format(path, str(e))) exit(1) root_dir = get_root_dir(domid) dev_dir = root_dir + "/dev" if not os.path.isdir(root_dir) or not os.path.isdir(dev_dir): log.error("Error: The chroot or dev directory doesn't exist") exit(1) if not os.path.isdir(dev_dir + "/bus"): # first USB device to pass-through MS_BIND = 4096 # mount flags, from fs.h mount("/dev", dev_dir, "", MS_BIND) setup_cgroup(domid, pid) sys_dir = root_dir + "/sys" # sys_dir could already be mounted because of PCI pass-through if not os.path.isdir(sys_dir): try: os.mkdir(sys_dir, 0755) except OSError: log.error("Failed to create sys dir in chroot") exit(1) if not os.path.isdir(sys_dir + "/devices"): mount("/sys", sys_dir, "sysfs") # add device to cgroup allow list allow_device(path, domid)
def get_ctl(path, mode): """get the string to control device access for cgroup :param path: the device file path :param mode: either "r" or "rw" :return: the string to control device access """ try: st = os.stat(path) except OSError as e: log.error("Failed to get stat of {}: {}".format(path, str(e))) raise t = "" if S_ISBLK(st.st_mode): t = "b" elif S_ISCHR(st.st_mode): t = "c" if t and mode in ("r", "rw"): return "{} {}:{} {}".format(t, os.major(st.st_rdev), os.minor( st.st_rdev), mode) raise RuntimeError("Failed to get control string of {}".format(path))
def get_ctl(path, mode): """get the string to control device access for cgroup :param path: the device file path :param mode: either "r" or "rw" :return: the string to control device access """ try: st = os.stat(path) except OSError as e: log.error("Failed to get stat of {}: {}".format(path, str(e))) raise t = "" if S_ISBLK(st.st_mode): t = "b" elif S_ISCHR(st.st_mode): t = "c" if t and mode in ("r", "rw"): return "{} {}:{} {}".format(t, os.major(st.st_rdev), os.minor(st.st_rdev), mode) raise RuntimeError("Failed to get control string of {}".format(path))
def parseArguments(args): parser = argparse.ArgumentParser() argServ = parser.add_argument("-s", "--service", required=True) argMode = parser.add_argument("-m", "--mode", choices=['client', 'server'], required=True) argExec = parser.add_argument("-e", "--executable", required=False) argWait = parser.add_argument("-w", "--wait", required=False, action="store_true") argUniq = parser.add_argument("-u", "--uniq", required=False, action="store_true") argBlk = parser.add_argument("-b", "--block", required=False, action="store_true") args = parser.parse_args(args) if args.mode == 'server': # more check for server side, for the script parameter and whether the file exists. if not args.executable or args.executable == "": logger.error("missing parameter --executable") raise argparse.ArgumentError( argExec, "missing parameter --executable when mode is 'server'") else: subargs = args.executable.split(' ') if not os.path.isfile(subargs[0]): logger.error("script file not found") raise argparse.ArgumentError(argExec, "script to execute is not found") return args
def init_id_maps(src_root, dst_root): """ Create mappings between (username and uid), and (group and gid) for the source and destination roots. """ with open(os.path.join(src_root, 'etc/passwd'), 'r') as f: for line in f: try: pwnam, _, uid, _ = line.split(':', 3) src_uid_map[int(uid)] = pwnam except ValueError as e: logger.error('Failed to parse: ' + line) logger.logException(e) with open(os.path.join(src_root, 'etc/group'), 'r') as f: for line in f: try: pwnam, _, gid, _ = line.split(':', 3) src_gid_map[int(gid)] = pwnam except ValueError as e: logger.error('Failed to parse: ' + line) logger.logException(e) with open(os.path.join(dst_root, 'etc/passwd'), 'r') as f: for line in f: try: pwnam, _, uid, _ = line.split(':', 3) dst_uid_map[pwnam] = int(uid) except ValueError as e: logger.error('Failed to parse: ' + line) logger.logException(e) with open(os.path.join(dst_root, 'etc/group'), 'r') as f: for line in f: try: pwnam, _, gid, _ = line.split(':', 3) dst_gid_map[pwnam] = int(gid) except ValueError as e: logger.error('Failed to parse: ' + line) logger.logException(e)
def umount(target): if ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True ).umount(target) < 0: # log and continue log.error("Failed to umount {}: {}". format(target, os.strerror(ctypes.get_errno())))
def log_exit(m): log.error(m) sys.exit(m)
def remap_netdevs(remap_list): # # rename everything sideways to safe faffing with temp renanes # for x in ( x for x in os.listdir("/sys/class/net/") if x[:3] == "eth" ): # util.runCmd2(['ip', 'link', 'set', x, 'name', 'side-'+x]) for cmd in remap_list: parse_arg(cmd) # Grab the current state from biosdevname current_eths = all_devices_all_names() current_state = [] for nic in current_eths: eth = current_eths[nic] if not ("BIOS device" in eth and "Kernel name" in eth and "Assigned MAC" in eth and "Bus Info" in eth and "all_ethN" in eth["BIOS device"] and "physical" in eth["BIOS device"]): LOG.error("Interface information for '%s' from biosdevname is " "incomplete; Discarding." % (eth.get("Kernel name", "Unknown"), )) try: current_state.append( MACPCI(eth["Assigned MAC"], eth["Bus Info"], kname=eth["Kernel name"], order=int(eth["BIOS device"]["all_ethN"][3:]), ppn=eth["BIOS device"]["physical"], label=eth.get("SMBIOS Label", ""))) except Exception as e: LOG.error("Can't generate current state for interface '%s' - " "%s" % (eth, e)) current_state.sort() LOG.debug("Current state = %s" % (niceformat(current_state), )) static_rules.generate(current_state) dynamic_rules.generate(current_state) static_eths = [x.tname for x in static_rules.rules] last_boot = [x for x in dynamic_rules.rules if x.tname not in static_eths] LOG.debug("StaticRules Formulae = %s" % (niceformat(static_rules.formulae), )) LOG.debug("StaticRules Rules = %s" % (niceformat(static_rules.rules), )) LOG.debug("DynamicRules Lastboot = %s" % (niceformat(last_boot), )) # Invoke the renaming logic try: transactions = rename(static_rules=static_rules.rules, cur_state=current_state, last_state=last_boot, old_state=[]) except Exception as e: LOG.critical("Problem from rename logic: %s. Giving up" % (e, )) return # Apply transactions, or explicitly state that there are none if len(transactions): for src, dst in transactions: ip_link_set_name(src, dst) else: LOG.info("No transactions. No need to rename any nics") # Regenerate dynamic configuration def macpci_as_list(x): return [str(x.mac), str(x.pci), x.tname] new_lastboot = map(macpci_as_list, current_state) dynamic_rules.lastboot = new_lastboot LOG.info("All done ordering the network devices")
os.rmdir(get_cg_dir(domid)) except OSError as e: # log and continue log.error("Failed to remove cgroup qemu-{}: {}".format( domid, str(e))) # umount /dev, /sys from chroot directory if they are mounted. root_dir = get_root_dir(domid) dev_dir = root_dir + "/dev" sys_dir = root_dir + "/sys" if os.path.isdir(dev_dir + "/bus"): umount(dev_dir) if os.path.isdir(sys_dir + "/devices"): umount(sys_dir) if __name__ == "__main__": log.logToSyslog(level=logging.DEBUG) arg = parse_arg() if "attach" == arg.command: attach(arg.device, arg.domid, arg.pid, arg.reset_only) elif "detach" == arg.command: detach(arg.device, arg.domid) elif "cleanup" == arg.command: cleanup(arg.domid) else: log.error("Unexpected command: {}".format(arg.command)) exit(1)
os.rmdir(get_cg_dir(domid)) except OSError as e: # log and continue log.error("Failed to remove cgroup qemu-{}: {}" .format(domid, str(e))) # umount /dev, /sys from chroot directory if they are mounted. root_dir = get_root_dir(domid) dev_dir = root_dir + "/dev" sys_dir = root_dir + "/sys" if os.path.isdir(dev_dir + "/bus"): umount(dev_dir) if os.path.isdir(sys_dir + "/devices"): umount(sys_dir) if __name__ == "__main__": log.logToSyslog(level=logging.DEBUG) arg = parse_arg() if "attach" == arg.command: attach(arg.device, arg.domid, arg.pid, arg.reset_only) elif "detach" == arg.command: detach(arg.device, arg.domid) elif "cleanup" == arg.command: cleanup(arg.domid) else: log.error("Unexpected command: {}".format(arg.command)) exit(1)
def logErr(self, logtxt): logger.error(self.logPrefix + logtxt)
def umount(target): if ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True).umount(target) < 0: # log and continue log.error("Failed to umount {}: {}".format( target, os.strerror(ctypes.get_errno())))
"missing parameter --executable when mode is 'server'") else: subargs = args.executable.split(' ') if not os.path.isfile(subargs[0]): logger.error("script file not found") raise argparse.ArgumentError(argExec, "script to execute is not found") return args if __name__ == "__main__": logger.logToSyslog(level=logging.DEBUG) args = parseArguments(sys.argv[1:]) service = args.service mode = args.mode executable = args.executable try: if mode == 'client': inst = ClientInstance(service, args.wait) else: # server inst = ServerInstance(service, executable, args.uniq, args.block) except Exception as err: logger.error("Unable to initialize '" + service + "' service, mode:" + mode + " err:" + str(err)) rasie inst.work()