def stop(args, config): must_be_root() names = must_get_names(args) for name in names: try: ops.stop(name) try: box_config = ops.get_box_config(name) except Exception as e: report.die("Cannot load '%s' config, is it broken? %s" % (name, e)) if os.path.exists(ops.get_box_home(name, "heaver_root_generated")): imager = image.get_image_operator(config["image"]) if box_config["datamounts"]: for mountpoint, source in box_config["datamounts"]: imager.disassemble_instance(source) imager.disassemble_instance(box_config["root"]) except ops.InvocationError as e: report.die("LXC is broken, cannot stop container %s: %s" % (name, e)) except ops.ContainerNotFound as e: report.die("Container %s just disappeared, panic!" % name) try: running_flag = ops.get_box_home(name, "heaver_box_running") if os.path.exists(running_flag): os.unlink(running_flag) except Exception as e: pass # FIXME: should not happen, warn user/log? send_status_soft(args, config) print report.format(dict(status="OK", action="stop", id=name, message="Container %s stopped" % name))
def shutdown(args, config): must_be_root() names = ops.ls() for name in names: if ops.is_running(name): try: ops.stop(name) try: box_config = ops.get_box_config(name) except Exception as e: report.die("Cannot load '%s' config, is it broken? %s" % (name, e)) if os.path.exists(ops.get_box_home(name, "heaver_root_generated")): imager = image.get_image_operator(config["image"]) if box_config["datamounts"]: for mountpoint, source in box_config["datamounts"]: imager.disassemble_instance(source) imager.disassemble_instance(box_config["root"]) except ops.InvocationError as e: report.die("LXC is broken, cannot stop container %s: %s" % (name, e)) except ops.ContainerNotFound as e: # WTF? Should not happen (containers in ops.ls() already exists) report.die("Container %s just disappeared, panic!" % name) send_status_soft(args, config) print report.format(dict(status="OK", action="shutdown", id=name, message="Container %s stopped at shutdown" % name))
def list_(args, config): if args.name: all_boxes = ops.ls() if args.name not in all_boxes: report.die("No such container: '%s'" % args.name) boxes = {args.name: all_boxes[args.name]} else: boxes = ops.ls() print report.format(dict(status="OK", action="list", data=boxes, message=format_list(boxes)))
def create(operator, args): if not args.image: report.die("Image id must be specified") try: instance = operator.create_instance(args.image, args.clone) print report.format(dict(action="create", status="OK", data=[args.image, instance], message="Created instance of image %s: %s" % (args.image, instance))) except Exception as e: report.die("Failed to create instance for image %s: %s" % (args.image, e))
def destroy(operator, args): if not args.clone: report.die("Clone id must be specified") try: instance = operator.destroy_instance(args.clone) print report.format(dict(action="destroy", status="OK", data=[args.clone], message="Destroyed instance %s" % (args.clone,))) except Exception as e: report.die("Failed to destroy instance %s: %s" % (args.clone, e))
def remove(operator, args): if not args.image: report.die("Image id must be specified") try: instance = operator.delete_image(args.image) print report.format(dict(action="remove", status="OK", data=[args.image], message="Removed image %s" % (args.image,))) except Exception as e: report.die("Failed to remove image %s: %s" % (args.image, e))
def add(operator, args): if not args.image: report.die("Image id must be specified") if not args.tarball: report.die("Tarball with image must be specified") try: operator.add_image(args.tarball, args.image) print report.format(dict(action="add", status="OK", data=args.image, message="Added image %s from '%s'" % (args.image, args.tarball))) except Exception as e: report.die("Failed to add image %s from '%s': %s" % (args.image, args.tarball, e))
def destroy(operator, args): if not args.clone: report.die("Clone id must be specified") try: instance = operator.destroy_instance(args.clone) print report.format( dict(action="destroy", status="OK", data=[args.clone], message="Destroyed instance %s" % (args.clone, ))) except Exception as e: report.die("Failed to destroy instance %s: %s" % (args.clone, e))
def remove(operator, args): if not args.image: report.die("Image id must be specified") try: instance = operator.delete_image(args.image) print report.format( dict(action="remove", status="OK", data=[args.image], message="Removed image %s" % (args.image, ))) except Exception as e: report.die("Failed to remove image %s: %s" % (args.image, e))
def sync(operator, args): if (args.image and args.all) or (not args.image and not args.all): report.die("Either all or one concrete image may be syncronized") if args.image: images = [args.image] if args.all: images = operator.list_images() result = operator.sync_images(images) for image, status in result: print report.format(dict(action="sync", status=("FAIL" if status == "error" else "OK"), data=[image, status], message="%s\t%s" % (image, status)))
def create(operator, args): if not args.image: report.die("Image id must be specified") try: instance = operator.create_instance(args.image, args.clone) print report.format( dict(action="create", status="OK", data=[args.image, instance], message="Created instance of image %s: %s" % (args.image, instance))) except Exception as e: report.die("Failed to create instance for image %s: %s" % (args.image, e))
def send_status(args, config): if "upstream" not in config: report.die("Cannot send status to upstream - upstream isnt configured") c = client.Client(config["upstream"]) status = get_status(config) answer = c.update_host(status["hostname"], status) # update authorized_keys file if "public_key" in answer: key = answer["public_key"] key_path = config.get("authorized_keys", os.path.expanduser("~/.ssh/authorized_keys")) update_authorized_keys(key, key_path) print report.format(dict(status="OK", action="send-status", message="Host status sent to daemon"))
def add(operator, args): if not args.image: report.die("Image id must be specified") if not args.tarball: report.die("Tarball with image must be specified") try: operator.add_image(args.tarball, args.image) print report.format( dict(action="add", status="OK", data=args.image, message="Added image %s from '%s'" % (args.image, args.tarball))) except Exception as e: report.die("Failed to add image %s from '%s': %s" % (args.image, args.tarball, e))
def sync(operator, args): if (args.image and args.all) or (not args.image and not args.all): report.die("Either all or one concrete image may be syncronized") if args.image: images = [args.image] if args.all: images = operator.list_images() result = operator.sync_images(images) for image, status in result: print report.format( dict(action="sync", status=("FAIL" if status == "error" else "OK"), data=[image, status], message="%s\t%s" % (image, status)))
def make_tarball(args, config): must_be_root() if not args.name: report.die("No container chosen (tarball cannot be feed with --all)") name = args.name if name not in ops.ls(): report.die("No such container: '%s'" % name) if ops.is_running(name): report.die("Container '%s' must be stopped for tarballing" % name) tar_path = args.tarball if tar_path == "<generated>": # make temporary path for tarball import tempfile try: tar_fd, tar_path = tempfile.mkstemp(dir=HEAVER_TMPDIR, prefix="%s.tar." % name) tar_file = os.fdopen(tar_fd, "w") except Exception as e: # FIXME: proper exceptions? report.die("Cannot create tarball of container '%s': '%s'" % (name, e)) else: try: tar_file = open(tar_path, "wb") except Exception as e: # FIXME: proper exceptions? # cannot open file - no directory or no write access report.die("Cannot create tarball of container '%s': %s" % (name, e)) box_config = ops.get_box_config(name) imager = image.get_image_operator(config["image"]) imager.assemble_instance(box_config["root"]) try: ops.write_tarball(name, tar_file) except Exception as e: # FIXME: proper exceptions? tar_file.close() os.unlink(tar_path) report.die("Cannot create tarball of container '%s': %s" % (name, e)) tar_file.close() print report.format(dict(status="OK", action="tarball", data=tar_path, message="Tarball of container '%s' created at '%s'" % (name, tar_path)))
def start(args, config): must_be_root() names = must_get_names(args) # start for name in names: try: try: box_config = ops.get_box_config(name) except Exception as e: report.die("Cannot load '%s' config, is it broken? %s" % (name, e)) if os.path.exists(ops.get_box_home(name, "heaver_root_generated")): imager = image.get_image_operator(config["image"]) imager.assemble_instance(box_config["root"]) if box_config["datamounts"]: root = box_config["root"] for mountpoint, source in box_config["datamounts"]: imager.assemble_instance(source) # since 'mountpoint' starts with / we need to remove it mountpoint = mountpoint.lstrip("/") mount_path = os.path.join(root, mountpoint) if not os.path.exists(mount_path): os.makedirs(mount_path) ops.start(name) except ops.InvocationError as e: report.die("LXC is broken, cannot start container %s: %s" % (name, e)) except ops.ContainerNotFound as e: report.die("Container %s just disappeared, panic!" % name) try: running_flag = ops.get_box_home(name, "heaver_box_running") open(running_flag, "w").close() except Exception as e: pass # FIXME: should not happen, warn user/log? send_status_soft(args, config) print report.format(dict(status="OK", action="start", id=name, message="Container %s started" % name))
def list_images(operator, args): images = operator.list_images() for image in images: print report.format( dict(action="list", status="OK", data=[image], message=image))
def list_images(operator, args): images = operator.list_images() for image in images: print report.format(dict(action="list", status="OK", data=[image], message=image))
def create(args, config): must_be_root() if not args.name: report.die("No container chosen (create cannot be feed with --all)") if ops.exists(args.name): report.die("Container %s already exists" % args.name) if not args.root and not args.image: report.die("Either image or root must be given (-i or -r)") if "limits" not in config: report.die("No \"limits\" section in config") must_be_correct_limits_config(config["limits"]) if args.limit is not None and len(args.limit) > 0: for limit in args.limit: name, _delim, value = limit.partition("=") config["limits"][name] = value # recheck config must_be_correct_limits_config(config["limits"]) key = None if args.key: try: with open(args.key) as key_file: key = key_file.read() except Exception as e: report.die("Cannot read given key file: %s" % e) elif args.raw_key: key = args.raw_key root_generated = False data_mounts = [] if args.root: root = args.root else: imager = image.get_image_operator(config["image"]) try: images = [] for img_arg in args.image: images.append(img_arg.split(":")[0]) status = imager.sync_images(images) for img_name, img_status in status: if img_status == "error": report.die("Cannot sync image '%s'" % img_name) # find root image (it's without additional path) root_images = filter(lambda img: not ":" in img or img.endswith(":/"), args.image) if len(root_images) == 0: report.die("No root image specified") if len(root_images) > 1: report.die("Only one root image may be used") root_image = root_images[0].split(":")[0] root = imager.create_instance(root_image, args.name) data_images = list(args.image) data_images.remove(root_images[0]) for data_image in data_images: image_name, _semicolon, mountpoint = data_image.partition(":") data_root = imager.create_instance(image_name, args.name) data_mounts.append((mountpoint, data_root)) except Exception as e: import traceback tb = traceback.format_exc() report.die("Cannot acquire root with image id '%s': %s\n%s" % (args.image, e, tb)) root_generated = True if root[0] != "/": root = os.path.join(os.getcwd(), root) box_config = dict(root=root, networks=dict(), key=key, datamounts=data_mounts) # set up limits box_config["limits"] = config["limits"] box_config["raw"] = dict() if "cgroup" in config: for name, value in config["cgroup"].items(): if name == "cpuset.cpus" and value == "auto": value = get_balanced_cpu() box_config["raw"]["cgroup." + name] = value # set up network all_ips = [] if args.net is None: must_be_correct_net_config(config["net"]) used_ranges = utils.sync_open(config["net"]["used_ranges_path"], "a+") networks = must_load_net_config(config["net"]["networks"], used_ranges.read().strip().split()) args.net = [networks.items()[0][1]["bridge"]] used_ranges.close() if args.net or args.raw_net: must_be_correct_net_config(config["net"]) used_ranges = utils.sync_open(config["net"]["used_ranges_path"], "a+") networks = must_load_net_config(config["net"]["networks"], used_ranges.read().strip().split()) bridge_count = 0 if args.raw_net is not None: bridge_count = len(args.raw_net) for idx, net in enumerate(args.raw_net): bridge, hwaddr, ips = must_parse_net_arg(net) for ip, mask in ips: all_ips.append(ip) net_config = dict(type="veth", ips=ips, link=bridge, flags="up") if hwaddr: net_config["hwaddr"] = hwaddr if idx == 0: net_config["gateway"] = "auto" box_config["networks"]["eth%d" % idx] = net_config if args.net is not None: for idx, net in enumerate(args.net): net_idx = idx + bridge_count name, _semicolon, ip_count_str = net.partition(":") if name not in networks: report.die("No such network: %s" % name) if ip_count_str != "": try: ip_count = int(ip_count_str) except: report.die("Ip count for network '%s' is not an int" % name) else: ip_count = 1 network = networks[name] hwaddr, ips = must_gen_net(network, ip_count) for ip, mask in ips: all_ips.append(ip) bridge = network["bridge"] if net_idx == 0: gateway = network.get("gateway") else: gateway = None net_config = dict(type="veth", ips=ips, link=bridge, flags="up", gateway=gateway, hwaddr=hwaddr) box_config["networks"]["eth%d" % net_idx] = net_config # overwrite config used_ranges.truncate(0) used_ranges.write("\n".join(networks.values()[0]["pool"].dump_used_ranges())) used_ranges.write("\n") # trailing newline used_ranges.close() try: ops.create(args.name, box_config) except ops.InvalidConfig as e: report.die("Internal error, invalid config: %r" % e) import traceback print traceback.format_exc() if all_ips: try: free_addresses(config["net"]["used_ranges_path"], all_ips) except e: report.die("Failed to free addresses, do it yourself: %s" % e) except ops.CreationError as e: report.die("Cannot create container: %r" % e) except Exception as e: import traceback report.die("!!! Unhandled exception !!!\n%s\n%s" % (traceback.format_exc(), e)) # if root was generated, remember it if root_generated: root_gen_path = ops.get_box_home(args.name, "heaver_root_generated") try: open(root_gen_path, "w").close() except Exception as e: pass #FIXME: warn user about it msg = dict(status="OK", action="create", id=args.name) if all_ips: msg["message"] = "Created container %s with addresses: %s" % (args.name, ", ".join(all_ips)) msg["data"] = dict(ips=all_ips) else: msg["message"] = "Created container %s" % args.name # send status to upstream if possible send_status_soft(args, config) print report.format(msg)
def status(args, config): status = get_status(config) print report.format(dict(status="OK", action="status", data=status, message=str(status)))
def destroy(args, config): must_be_root() names = must_get_names(args) if args.all and not args.force: msg = ["Selected containers: %s" % ", ".join(names), "Are you sure to destroy all these (probably innocent) containers? [y/N] "] decision = raw_input("\n".join(msg)) if not (decision.lower() == "y" or decision.lower() == "yes"): print "Okay" return for name in names: box_root = None if not ops.exists(name): print report.format(dict(status="OK", action="destroy", id=name, message="Container %s already destroyed" % name)) continue try: box_config = ops.get_box_config(name) except Exception as e: report.die("Cannot load '%s' config, is it broken? %s" % (name, e)) root_generated = os.path.exists(ops.get_box_home(name, "heaver_root_generated")) root = box_config.get("root") try: # Free addresses, associated with box ips = [] for network in box_config["networks"].values(): for ip, mask in network["ips"]: ips.append(ip) free_addresses(config["net"]["used_ranges_path"], ips) except Exception as e: print "Cannot release assigned to box addresses:", e import traceback print traceback.format_exc() try: ops.destroy(name) except ops.ContainerBusy as e: report.die("Container busy: %s" % e) except ops.ContainerNotFound: pass except ops.InvocationError as e: report.die("LXC is broken, cannot destroy container %s: %s" % (name, e)) # remove root, if it was generated if root_generated: imager = image.get_image_operator(config["image"]) try: imager.destroy_instance(root) except Exception as e: report.die("Cannot remove root of container '%s': %s" % (name, e)) if box_config["datamounts"]: for mountpoint, source in box_config["datamounts"]: try: imager.destroy_instance(source) except Exception as e: report.die("Cannot remove datamount '%s' in container'%s': %s" % (source, name, e)) send_status_soft(args, config) print report.format(dict(status="OK", action="destroy", id=name, message="Container %s destroyed" % name))