def encrypt(self, data, cluster_name=None, secret=None, encode=True): """ Encrypt and return data in a wrapping structure. """ if cluster_name is None: cluster_name = self.cluster_name if secret is None: cluster_key = self.cluster_key else: cluster_key = secret if cluster_key is None: return iv = self.gen_iv() try: data = json.dumps(data).encode() except (UnicodeDecodeError, TypeError): # already binary data pass message = { "clustername": cluster_name, "nodename": Env.nodename, "iv": bdecode(base64.urlsafe_b64encode(iv)), "data": bdecode( base64.urlsafe_b64encode( self._encrypt(data, cluster_key, iv) ) ), } if encode: return (json.dumps(message)+'\0').encode() return json.dumps(message)
def _fullpem(self): required = set(["private_key", "certificate_chain"]) if required & set(self.data_keys()) != required: self.gen_cert() buff = bdecode(self.decode_key("private_key")) buff += bdecode(self.decode_key("certificate_chain")) return buff
def get_secret(self, sp, secret): if want_context(): return if sp.context and not sp.context.get("secret"): return elif secret: return bdecode(secret) elif sp.server in self.cluster_drpnodes: node = self.get_node() return bdecode(self.prepare_key(node.oget("cluster", "secret", impersonate=sp.server))) else: return bdecode(self.cluster_key)
def action(self, nodename, thr=None, **kwargs): options = self.parse_options(kwargs) if not options.data and not options.template: return {"status": 0, "info": "no data"} if options.template is not None: if options.path: paths = [options.path] cmd = [ "create", "-s", options.path, "--template=%s" % options.template, "--env=-" ] else: paths = [p for p in options.data] cmd = ["create", "--template=%s" % options.template, "--env=-"] else: paths = [p for p in options.data] cmd = ["create", "--config=-"] validate_paths(paths) if options.namespace: cmd.append("--namespace=" + options.namespace) if options.restore: cmd.append("--restore") thr.log_request("create/update %s" % ",".join(paths), nodename, **kwargs) proc = thr.service_command(None, cmd, stdout=PIPE, stderr=PIPE, stdin=json.dumps(options.data)) if options.sync: out, err = proc.communicate() result = { "status": proc.returncode, "data": { "out": bdecode(out), "err": bdecode(err), "ret": proc.returncode, }, } else: thr.push_proc(proc) result = { "status": 0, "info": "started %s action %s" % (options.path, " ".join(cmd)), } if options.provision: for path in paths: thr.set_smon(path, global_expect="provisioned") return result
def check_io(): logged = 0 rlist, _, xlist = select.select([rout, rerr], [], [], LCALL_CHECK_IO_TIMEOUT) if xlist: return logged for io in rlist: buff = os.read(io, 32768) buff = bdecode(buff) if six.PY2: buff = buff.decode("utf8") if buff in ('', b''): continue buff = pending[io] + buff while True: l = buff.split("\n", 1) if len(l) == 1: pending[io] = l[0] break line, buff = l if logger: logger.log(log_level[io], "| " + line) elif log_level[io] < logging.ERROR: print(line) else: print(line, file=sys.stderr) logged += 1 return logged
def h2_daemon_request(self, data, server=None, node=None, with_result=True, silent=False, cluster_name=None, secret=None, timeout=None, sp=None, method="GET"): secret = self.get_secret(sp, secret) path = self.h2_path_from_data(data) headers = self.h2_headers(node=node, secret=secret, multiplexed=data.get("multiplexed"), af=sp.af) body = self.h2_body_from_data(data) headers.update({"Content-Length": str(len(body))}) conn = self.h2c(sp=sp, timeout=timeout) elapsed = 0 while True: try: conn.request(method, path, headers=headers, body=body) break except AssertionError as exc: raise ex.Error(str(exc)) except ConnectionResetError: return {"status": 1, "error": "%s %s connection reset"%(method, path)} except (ConnectionRefusedError, ssl.SSLError, socket.error) as exc: try: errno = exc.errno except AttributeError: errno = None if errno in RETRYABLE and \ (timeout == 0 or elapsed < timeout): # Resource temporarily unavailable (busy, overflow) # Retry after a delay, if the daemon is still # running and timeout is not exhausted time.sleep(PAUSE) elapsed += PAUSE continue return {"status": 1, "error": "%s"%exc, "errno": errno} resp = conn.get_response() data = resp.read() data = json.loads(bdecode(data)) return data
def post(self, uri, data=None): api = self.api+uri+"/" headers = {'Content-Type': 'application/json'} if data: data = json.dumps(data) r = requests.post(api, data=data, auth=self.auth, timeout=self.timeout, verify=VERIFY, headers=headers) return bdecode(r.content)
def read_slot(self, slot, fo=None): offset = self.slot_offset(slot) fo.seek(offset, os.SEEK_SET) fo.readinto(self.slot_buff) data = bdecode(self.slot_buff[:]) end = data.index("\0") return data[:end]
def _zone_list(zone, nameserver): request = { "method": "list", "parameters": { "zonename": zone, } } try: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(Env.paths.dnsuxsock) sock.send(bencode(json.dumps(request) + "\n")) response = "" while True: buff = sock.recv(4096) if not buff: break response += bdecode(buff) if response[-1] == "\n": break finally: sock.close() if not response: return try: return json.loads(response)["result"] except ValueError: raise ex.Error("invalid response format")
def _add_key(self, key, data): if not key: raise ex.Error("configuration key name can not be empty") if data is None: raise ex.Error("configuration value can not be empty") if not is_string(data): data = "base64:" + bdecode(base64.urlsafe_b64encode(data)) elif "\n" in data: data = "base64:" + bdecode(base64.urlsafe_b64encode(bencode(data))) else: data = "literal:" + data self.set_multi(["data.%s=%s" % (key, data)]) self.log.info("configuration key '%s' added (%s)", key, print_size(len(data), compact=True, unit="b")) # refresh if in use self.postinstall(key)
def meta_read_slot(self, slot, fo=None): offset = self.meta_slot_offset(slot) fo.seek(offset, os.SEEK_SET) fo.readinto(self.meta_slot_buff) try: return bdecode(self.meta_slot_buff[:mmap.PAGESIZE]) except Exception as exc: return None
def is_container(): p = '/proc/1/environ' if not os.path.exists(p): return False with open(p, 'r') as f: buff = f.read() if "container=lxc" in bdecode(buff): return True return False
def _pkcs12(self, password): required = set(["private_key", "certificate_chain"]) if required & set(self.data_keys()) != required: self.gen_cert() from subprocess import Popen, PIPE import tempfile _tmpcert = tempfile.NamedTemporaryFile() _tmpkey = tempfile.NamedTemporaryFile() tmpcert = _tmpcert.name tmpkey = _tmpkey.name _tmpcert.close() _tmpkey.close() if password is None: from getpass import getpass pwd = getpass("Password: "******"\n" elif password in ["/dev/stdin", "-"]: pwd = sys.stdin.readline() else: pwd = password + "\n" if six.PY3: pwd = bencode(pwd) try: with open(tmpkey, "w") as _tmpkey: os.chmod(tmpkey, 0o600) _tmpkey.write(bdecode(self.decode_key("private_key"))) with open(tmpcert, "w") as _tmpcert: os.chmod(tmpcert, 0o600) _tmpcert.write(bdecode(self.decode_key("certificate_chain"))) cmd = [ "openssl", "pkcs12", "-export", "-in", tmpcert, "-inkey", tmpkey, "-passout", "stdin" ] proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) out, err = proc.communicate(input=pwd) if err: print(err, file=sys.stderr) return out finally: if os.path.exists(tmpcert): os.unlink(tmpcert) if os.path.exists(tmpkey): os.unlink(tmpkey)
def post2(self, uri, data=None): api = self.api.replace("api/v1.0", "")+uri s = requests.Session() r = s.get(api) csrf_token = r.cookies['csrftoken'] data["csrfmiddlewaretoken"] = csrf_token if data: data = json.dumps(data) r = requests.post(api, data=data, auth=self.auth, timeout=self.timeout, verify=VERIFY) return bdecode(r.content)
def h2_daemon_stream_fetch(self, stream_id, conn): resps = [] for push in conn.get_pushes(stream_id): resps.append(push.get_response()) for resp in resps: # resp.read() can modify push.promises_headers, which get_pushes iterates # causing a RuntimeError => keep in a separate loop evt = resp.read() evt = json.loads(bdecode(evt)) yield evt
def do_it(self, send_cmd, receive_cmd, node): self.log.info(' '.join(send_cmd + ["|"] + receive_cmd)) p1 = Popen(send_cmd, stdout=PIPE) pi = Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=PIPE, stderr=PIPE) p2 = Popen(receive_cmd, stdin=pi.stdout, stdout=PIPE, stderr=PIPE) buff = p2.communicate() if p2.returncode == 0: stats_buff = pi.communicate()[1] stats = self.parse_dd(stats_buff) self.update_stats(stats, target=node) out = bdecode(buff[0]) err = bdecode(buff[1]) if p2.returncode != 0: if err is not None and len(err) > 0: self.log.error(err) raise ex.Error("sync failed") if out is not None and len(out) > 0: self.log.info(out)
def parse_dd(self, buff): """ Extract normalized speed and transfered data size from the dd output """ data = {} if not buff: return data words = bdecode(buff).split() if "bytes" in words: data["bytes"] = int(words[words.index("bytes") - 1]) if words[-1].endswith("/s"): data["speed"] = int(convert_speed("".join(words[-2:]))) return data
def justcall(argv=None, stdin=None, input=None): """ Call subprocess' Popen(argv, stdout=PIPE, stderr=PIPE, stdin=stdin) The 'close_fds' value is autodectected (true on unix, false on windows). Returns (stdout, stderr, returncode) """ if argv is None: argv = [Env.syspaths.false] if input: stdin = PIPE input = bencode(input) try: proc = Popen(argv, stdin=stdin, stdout=PIPE, stderr=PIPE, close_fds=close_fds) out, err = proc.communicate(input=input) return bdecode(out), bdecode(err), proc.returncode except OSError as exc: if exc.errno in (ENOENT, EACCES): return "", "", 1 raise
def _handle_client(self, conn, cr, cw): chunks = [] buff_size = 4096 while True: try: data = cr.readline() except socket.timeout as exc: break except socket.error as exc: self.log.info("%s", exc) break if len(data) == 0: #self.log.info("no more data") break self.log.debug("received %s", data) try: data = bdecode(data) data = json.loads(data) except Exception as exc: self.log.error(exc) data = None if self.stopped(): self.log.info("stop event received (handler thread)") break if data is None or not isinstance(data, dict): continue try: result = self.router(data) except Exception as exc: self.log.error("dns request: %s => handler error: %s", data, exc) return {"error": "unexpected backend error", "result": False} if result is not None: message = json.dumps(result) + "\n" try: cw.write(message) cw.flush() except socket.error as exc: if exc.errno != errno.EPIPE: raise self.log.info("client died (broken pipe)") break self.log.debug("replied %s", message) message_len = len(message) self.stats.sessions.tx += message_len
def action(self, nodename, thr=None, stream_id=None, **kwargs): logfile = os.path.join(Env.paths.pathlog, "node.log") ofile = thr._action_logs_open(logfile, 0, "node") request_headers = HTTPHeaderMap( thr.streams[stream_id]["request"].headers) try: content_type = bdecode(request_headers.get("accept").pop()) except: content_type = "application/json" thr.streams[stream_id]["content_type"] = content_type thr.streams[stream_id]["pushers"].append({ "o": self, "fn": "h2_push_logs", "args": [ofile, True], })
def get(self, uri, params=None, timeout=None): timeout = timeout or self.timeout ep = self.api + uri + "/" try: r = requests.get(ep, params=params, auth=self.auth, timeout=timeout, verify=VERIFY) except Exception as exc: raise ex.Error("GET %s %s => %s" % (ep, params, exc)) content = bdecode(r.content) if r.status_code != 200: raise ex.Error("GET %s %s => %d: %s" % (ep, params, r.status_code, content)) return content
def action(self, nodename, thr=None, **kwargs): options = self.parse_options(kwargs) try: return { "status": 0, "data": bdecode(shared.SERVICES[options.path].decode_key(options.key)) } except ex.Error as exc: return {"status": 1, "error": str(exc)} except Exception as exc: return { "status": 1, "error": str(exc), "traceback": traceback.format_exc() }
def action(self, nodename, thr=None, stream_id=None, **kwargs): options = self.parse_options(kwargs) thr.selector = options.selector if not thr.event_queue: thr.event_queue = queue.Queue() if options.full: data = thr.daemon_status() namespaces = thr.get_namespaces() fevent = { "nodename": Env.nodename, "ts": time.time(), "kind": "full", "data": thr.filter_daemon_status(data, namespaces=namespaces, selector=options.selector), } if thr.h2conn: _msg = fevent elif thr.encrypted: _msg = thr.encrypt(fevent) else: _msg = thr.msg_encode(fevent) thr.event_queue.put(_msg) if not thr in thr.parent.events_clients: thr.parent.events_clients.append(thr) if not stream_id in thr.events_stream_ids: thr.events_stream_ids.append(stream_id) if thr.h2conn: request_headers = HTTPHeaderMap( thr.streams[stream_id]["request"].headers) try: content_type = bdecode(request_headers.get("accept").pop()) except: content_type = "application/json" thr.streams[stream_id]["content_type"] = content_type thr.streams[stream_id]["pushers"].append({ "fn": "h2_push_action_events", }) else: thr.raw_push_action_events()
def get_src_dir_dev(self, dev): """Given a directory path, return its hosting device """ if dev in self.src_dir_devs_cache: return self.src_dir_devs_cache[dev] p = Popen(self.df_one_cmd + [dev], stdout=PIPE, stderr=STDOUT, close_fds=True) out, err = p.communicate() if p.returncode != 0: return out = bdecode(out).lstrip() lines = out.splitlines() if len(lines) == 2: out = lines[1] self.src_dir_devs_cache[dev] = out.split()[0] return self.src_dir_devs_cache[dev]
def action(self, nodename, thr=None, stream_id=None, **kwargs): options = self.parse_options(kwargs) svc = thr.get_service(options.path) if svc is None: raise ex.HTTP(404, "%s not found" % options.path) request_headers = HTTPHeaderMap( thr.streams[stream_id]["request"].headers) try: content_type = bdecode(request_headers.get("accept").pop()) except: content_type = "application/json" thr.streams[stream_id]["content_type"] = content_type logfile = os.path.join(svc.log_d, svc.name + ".log") ofile = thr._action_logs_open(logfile, 0, svc.path) thr.streams[stream_id]["pushers"].append({ "o": self, "fn": "h2_push_logs", "args": [ofile, True], })
def post(self, uri, data=None, timeout=None): timeout = timeout or self.timeout ep = self.api + uri + "/" headers = {'Content-Type': 'application/json'} if data: data = json.dumps(data) try: r = requests.post(ep, data=data, auth=self.auth, timeout=timeout, verify=VERIFY, headers=headers) except Exception as exc: raise ex.Error("POST %s %s => %s" % (ep, data, exc)) content = bdecode(r.content) if r.status_code != 200: raise ex.Error("POST %s %s => %d: %s" % (ep, data, r.status_code, content)) return content
def cni_cmd(self, _env, data): cmd = [self.cni_bin(data)] if not which(cmd[0]): raise ex.Error("%s not found" % cmd[0]) self.log_cmd(_env, data, cmd) env = {} env.update(Env.initial_env) env.update(_env) proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env) out, err = proc.communicate(input=bencode(json.dumps(data))) out = bdecode(out) try: data = json.loads(out) except ValueError: if proc.returncode == 0: # for example a del portmap outs nothing return raise ex.Error(err) if "code" in data: raise ex.Error(data.get("msg", "")) for line in format_str_flat_json(data).splitlines(): self.log.info(line) return data
def action(self, nodename, thr=None, **kwargs): options = self.parse_options(kwargs) name, namespace, kind = split_path(options.path) if thr.get_service(options.path) is None and options.action not in ("create", "deploy"): thr.log_request("service action (%s not installed)" % options.path, nodename, lvl="warning", **kwargs) raise ex.HTTP(404, "%s not found" % options.path) if not options.action and not options.cmd: thr.log_request("service action (no action set)", nodename, lvl="error", **kwargs) raise ex.HTTP(400, "action not set") for opt in ("node", "daemon", "svcs", "service", "s", "parm_svcs", "local", "id"): if opt in options.options: del options.options[opt] for opt, ropt in (("jsonpath_filter", "filter"),): if opt in options.options: options.options[ropt] = options.options[opt] del options.options[opt] options.options["local"] = True pmod = importlib.import_module("commands.{kind}.parser".format(kind=kind)) popt = pmod.OPT def find_opt(opt): for k, o in popt.items(): if o.dest == opt: return o if o.dest == "parm_" + opt: return o if options.cmd: cmd = [options.cmd] else: cmd = [options.action] for opt, val in options.options.items(): po = find_opt(opt) if po is None: continue if val == po.default: continue if val is None: continue opt = po._long_opts[0] if po._long_opts else po._short_opts[0] if po.action == "append": cmd += [opt + "=" + str(v) for v in val] elif po.action == "store_true" and val: cmd.append(opt) elif po.action == "store_false" and not val: cmd.append(opt) elif po.type == "string": opt += "=" + val cmd.append(opt) elif po.type == "integer": opt += "=" + str(val) cmd.append(opt) fullcmd = Env.om + ["svc", "-s", options.path] + cmd thr.log_request("run 'om %s %s'" % (options.path, " ".join(cmd)), nodename, **kwargs) if options.sync: proc = Popen(fullcmd, stdout=PIPE, stderr=PIPE, stdin=None, close_fds=True) out, err = proc.communicate() try: result = json.loads(out) except Exception: result = { "status": 0, "data": { "out": bdecode(out), "err": bdecode(err), "ret": proc.returncode, }, } else: import uuid session_id = str(uuid.uuid4()) env = {} env.update(os.environ) env["OSVC_PARENT_SESSION_UUID"] = session_id proc = Popen(fullcmd, stdin=None, close_fds=True, env=env) thr.push_proc(proc) result = { "status": 0, "data": { "pid": proc.pid, "session_id": session_id, }, "info": "started %s action %s" % (options.path, " ".join(cmd)), } return result
def decrypt(self, message, cluster_name=None, secret=None, sender_id=None, structured=True): """ Validate the message meta, decrypt and return the data. """ if cluster_name is None: cluster_names = self.cluster_names else: cluster_names = [cluster_name] message = bdecode(message).rstrip("\0\x00") try: message = json.loads(message) except ValueError: message_len = len(message) if message_len > 40: self.log.error("misformatted encrypted message from %s: %s", sender_id, message[:30]+"..."+message[-10:]) elif message_len > 0: self.log.error("misformatted encrypted message from %s", sender_id) return None, None, None msg_clustername = message.get("clustername") msg_nodename = message.get("nodename") if secret is None: if msg_nodename in self.cluster_drpnodes: cluster_key = self.get_secret(Storage(server=msg_nodename), None) else: cluster_key = self.cluster_key else: cluster_key = secret if cluster_name != "join" and \ msg_clustername not in set(["join"]) | self.cluster_names: self.log.warning("discard message from cluster %s, sender %s", msg_clustername, sender_id) return None, None, None if cluster_key is None: return None, None, None if msg_nodename is None: return None, None, None iv = message.get("iv") if iv is None: return None, None, None if self.blacklisted(sender_id): return None, None, None iv = base64.urlsafe_b64decode(to_bytes(iv)) data = base64.urlsafe_b64decode(to_bytes(message["data"])) try: data = self._decrypt(data, cluster_key, iv) except Exception as exc: self.log.error("decrypt message from %s: %s", msg_nodename, str(exc)) self.blacklist(sender_id) return None, None, None if sender_id: self.blacklist_clear(sender_id) if not structured: try: loaded = json.loads(bdecode(data)) except ValueError as exc: loaded = data if not isinstance(loaded, foreign.six.text_type): loaded = data return msg_clustername, msg_nodename, loaded try: return msg_clustername, msg_nodename, json.loads(bdecode(data)) except ValueError as exc: return msg_clustername, msg_nodename, data
def msg_decode(self, message): message = bdecode(message).rstrip("\0\x00") if len(message) == 0: return return json.loads(message)