def init(self): if self.username is not None and self.password is not None: return s = "array#" + self.head try: stype = self.node.oget(s, "type") except Exception: raise ex.excError("no array configuration for head %s" % self.head) if stype != "nexenta": raise ex.excError("array %s type is not nexanta" % self.head) try: self.username = self.node.oget(s, "username") except Exception: raise ex.excError("no username information for head %s" % self.head) try: self.password = self.node.oget(s, "password") except Exception: raise ex.excError("no password information for head %s" % self.head) self.port = self.node.oget(s, "port") try: secname, namespace, _ = split_path(self.password) self.password = factory("sec")( secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: raise ex.excError("error decoding password: %s" % exc) self.url = 'https://%(head)s:%(port)d/rest/nms/ <https://%(head)s:%(port)d/rest/nms/>' % dict( head=self.head, port=self.port)
def kind_environment_env(self, kind, mappings): env = {} if mappings is None: return env for mapping in mappings: try: var, val = mapping.split("=", 1) except Exception as exc: self.log.info("ignored %s environment mapping %s: %s", kind, mapping, exc) continue try: name, key = val.split("/", 1) except Exception as exc: self.log.info("ignored %s environment mapping %s: %s", kind, mapping, exc) continue var = var.upper() obj = factory(kind)(name, namespace=self.svc.namespace, volatile=True, node=self.svc.node) if not obj.exists(): self.log.info( "ignored %s environment mapping %s: config %s does not exist", kind, mapping, name) continue if key not in obj.data_keys(): self.log.info( "ignored %s environment mapping %s: key %s does not exist", kind, mapping, key) continue val = obj.decode_key(key) env[var] = val return env
def on_create(self): changes = [] has_ca = False if not self.oget("DEFAULT", "cn"): if self.namespace == "system": changes.append("cn=%s" % self.name) else: changes.append("cn=%s" % self.fullname) if self.namespace != "system": try: self.conf_get("DEFAULT", "validity") except ex.OptNotFound: changes.append("validity=%s" % DEFAULT_SACC_CERT_VALIDITY) grant = "guest:" + self.namespace changes.append("grant=%s" % grant) if not self.oget("DEFAULT", "ca"): capath = self.node.oget("cluster", "ca") if capath is None: capath = "system/sec/ca-" + self.node.cluster_name name, namespace, kind = split_path(capath) casec = factory("sec")(name, namespace="system", volatile=True, log=self.log) if casec.exists(): has_ca = True changes.append("ca=%s" % capath) else: print("no cluster CA defined. skip certificate generation.") if changes: self.set_multi(changes) if has_ca and "certificate" not in self.data_keys( ) and "private_key" in casec.data_keys(): self.gen_cert()
def ca(self): capath = self.oget("DEFAULT", "ca") name, namespace, kind = split_path(capath) return factory("sec")(name, namespace=namespace, volatile=True, node=self.node)
def delete_volume(self, name, namespace=None): volume = factory("vol")(name=name, namespace=namespace, node=self.node) if not volume.exists(): self.log.info("volume does not exist") return self.log.info("delete volume %s", volume.path) volume.action("delete", options={"wait": True, "unprovision": True, "time": "5m"})
def __init__(self, objects=[], node=None): self.objects = objects self.filtering = len(objects) > 0 if node: self.node = node else: self.node = Node() done = [] for s in self.node.conf_sections(cat="array"): try: name = self.node.oget(s, "name") except Exception: name = None if not name: name = s.split("#", 1)[-1] if name in done: continue if self.filtering and name not in self.objects: continue try: stype = self.node.oget(s, "type") except: continue if stype != "hds": continue try: bin = self.node.oget(s, 'bin') jre_path = self.node.oget(s, 'jre_path') url = self.node.oget(s, 'url') username = self.node.oget(s, 'username') password = self.node.oget(s, 'password') except Exception as exc: print("error parsing section %s: %s" % (s, exc), file=sys.stderr) continue try: secname, namespace, _ = split_path(password) password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: print("error decoding password: %s", exc, file=sys.stderr) continue self.arrays.append( Array(name, url, username, password, bin=bin, jre_path=jre_path, node=self.node)) done.append(name)
def action(self, nodename, thr=None, **kwargs): options = self.parse_options(kwargs) if options.kind == "node": obj = shared.NODE elif options.kind: obj = factory(options.kind)(name="dummy", node=shared.NODE, volatile=True) else: raise HTTP(400, "A kind must be specified.") return obj.kwdict.KEYS.dump()
def __init__(self, objects=[], node=None): self.objects = objects if len(objects) > 0: self.filtering = True else: self.filtering = False self.arrays = [] if node: self.node = node else: self.node = Node() done = [] for s in self.node.conf_sections(cat="array"): name = s.split("#", 1)[-1] if name in done: continue if self.filtering and name not in self.objects: continue try: stype = self.node.oget(s, "type") except: continue if stype != "centera": continue try: server = self.node.oget(s, "server") username = self.node.oget(s, "username") password = self.node.oget(s, "password") jcass_dir = self.node.oget(s, "jcass_dir") java_bin = self.node.oget(s, "java_bin") except: print("error parsing section", s, file=sys.stderr) try: secname, namespace, _ = split_path(password) password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: print("error decoding password: %s", exc, file=sys.stderr) continue self.arrays.append( Centera(name, server=server, username=username, password=password, java_bin=java_bin, jcass_dir=jcass_dir, node=self.node)) done.append(name)
def __init__(self, objects=[], node=None): self.objects = objects if len(objects) > 0: self.filtering = True else: self.filtering = False self.arrays = [] if node: self.node = node else: self.node = Node() done = [] for s in self.node.conf_sections(cat="array"): name = s.split("#", 1)[-1] if name in done: continue if self.filtering and name not in self.objects: continue try: stype = self.node.oget(s, "type") except: continue if stype != "emcvnx": continue try: method = self.node.oget(s, "method") scope = self.node.oget(s, "scope") spa = self.node.oget(s, "spa") spb = self.node.oget(s, "spb") username = self.node.oget(s, "username") password = self.node.oget(s, "password") except Exception as exc: print("error parsing section %s: %s" % (s, exc), file=sys.stderr) continue if method == "credentials": if username is None or password is None: print("error parsing section %s: username and password are mandatory" % s, file=sys.stderr) continue try: secname, namespace, _ = split_path(password) password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: print("error decoding password: %s", exc, file=sys.stderr) continue self.arrays.append(EmcVnx(name, method, scope, spa, spb, username=username, password=password, node=self.node)) done.append(name)
def test_raises_on_dup_destinations(mocker, osvc_path_tests, volume_mounts): mocker.patch.object(Volume, 'status', return_value=rcStatus.UP) svc1 = svc.Svc('svc1') pool = Pool(name="dir1", node=Node()) for vol_name in ['vol1', 'vol2']: pool.configure_volume(factory("vol")(name=vol_name)) svc1 += Volume(rid="#" + vol_name, name=vol_name) container = Container(rid='#dck1', volume_mounts=volume_mounts) svc1 += container with pytest.raises(ex.excError, match=r'same destination mount point'): container.volume_options()
def postinstall(self, key=None): """ Refresh installed keys """ for path in self.node.svcs_selector("*/svc/*", namespace=self.namespace, local=True): name, _, _ = split_path(path) svc = factory("svc")(name, namespace=self.namespace, volatile=True, node=self.node, log=self.log) for vol in svc.get_resources("volume"): if vol.has_data(self.kind, self.path, key) and vol._status() == rcStatus.UP: vol._install_data(self.kind)
def __init__(self, objects=[], node=None): self.objects = objects self.filtering = len(objects) > 0 if node: self.node = node else: self.node = Node() done = [] for s in self.node.conf_sections(cat="array"): name = s.split("#", 1)[-1] if name in done: continue try: stype = self.node.oget(s, "type") except: continue if stype != "eva": continue try: manager = self.node.oget(s, 'manager') username = self.node.oget(s, 'username') password = self.node.oget(s, 'password') sssubin = self.node.oget(s, 'bin') except Exception as exc: print("error parsing section %s: %s" % (s, exc), file=sys.stderr) pass try: secname, namespace, _ = split_path(password) password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: print("error decoding password: %s", exc, file=sys.stderr) continue out, err = sssu('ls system', manager, username, password, sssubin=sssubin) _in = False for line in out.split('\n'): if 'Systems avail' in line: _in = True continue if not _in: continue name = line.strip() if self.filtering and name not in self.objects: continue self.arrays.append(Eva(name, manager, username, password, sssubin=sssubin)) done.append(name)
def __init__(self, objects=[], node=None): self.objects = objects self.filtering = len(objects) > 0 self.timeout = 10 if node: self.node = node else: self.node = Node() done = [] for s in self.node.conf_sections(cat="array"): try: name = self.node.oget(s, 'name') except Exception: name = None if not name: name = s.split("#", 1)[-1] if name in done: continue if self.filtering and name not in self.objects: continue try: stype = self.node.oget(s, "type") except: continue if stype != "dorado": continue timeout = self.node.oget(s, "timeout") try: username = self.node.oget(s, "username") password = self.node.oget(s, "password") api = self.node.oget(s, "api") except: print("error parsing section", s, file=sys.stderr) continue try: secname, namespace, _ = split_path(password) password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: print("error decoding password: %s", exc, file=sys.stderr) continue self.arrays.append( Dorado(name, api, username, password, timeout, node=self.node)) done.append(name)
def create_volume(self, name, namespace=None, size=None, access="rwo", fmt=False, nodes=None, shared=False): volume = factory("vol")(name=name, namespace=namespace, node=self.node) if volume.exists(): self.log.info("volume %s already exists", name) return volume if nodes is None: nodes = "" self.log.info("create volume %s (pool name: %s, pool type: %s, " "access: %s, size: %s, format: %s, nodes: %s, shared: %s)", volume.path, self.name, self.type, access, size, fmt, nodes, shared) self.configure_volume(volume, fmt=fmt, size=convert_size(size), access=access, nodes=nodes, shared=shared) volume.action("provision", options={"wait": True, "time": "5m"})
def _data_status(self, kind): for data in self.data_data(kind): name, _, kind = split_path(data["obj"]) obj = factory(kind)(name, namespace=self.svc.namespace, volatile=True, node=self.svc.node) if not obj.exists(): self.status_log( "referenced %s %s does not exist: " "expected data %s can not be installed in the volume" % (kind, name, data["key"]), "warn") continue keys = obj.resolve_key(data["key"]) if not keys and not is_glob(data["key"]): self.status_log( "%s %s has no key %s. " "expected data can not be installed in the volume" % (kind, name, data["key"]), "warn")
def test_mount_options_values_when_source_is_volume( mocker, vol_options, container_options, expected_options): mocker.patch.object(Volume, 'status', return_value=rcStatus.UP) vol_name = 'vol-' + vol_options + '-' + container_options Pool(name="dir1", node=Node()).configure_volume(factory("vol")(name=vol_name), access=vol_options) svc1 = svc.Svc('svc1') vol = Volume(rid="#" + vol_name, name=vol_name, access=vol_options) container = Container( rid='#dck1', volume_mounts=[vol_name + '/src:/dst:' + container_options]) svc1 += vol svc1 += container assert container.volume_options() == [ vol.mount_point + '/src:/dst:' + expected_options ]
def __init__(self, objects=[], node=None): self.objects = objects self.filtering = len(objects) > 0 self.arrays = [] if node: self.node = node else: self.node = Node() done = [] for s in self.node.conf_sections(cat="array"): name = s.split("#", 1)[-1] if name in done: continue if self.filtering and name not in self.objects: continue try: stype = self.node.oget(s, "type") except: continue if stype != "netapp": continue kwargs = {"node": self.node} for key in ("server", "username", "key"): try: kwargs[key] = self.node.oget(s, key) except: print("missing parameter: %s", s) if "server" not in kwargs or "username" not in kwargs or "key" not in kwargs: continue try: secname, namespace, _ = split_path(kwargs["password"]) kwargs["password"] = factory("sec")( secname, namespace=namespace, volatile=True).decode_key("password") except Exception as exc: print("error decoding password: %s", exc, file=sys.stderr) continue self.arrays.append(Netapp(s, **kwargs))
def _install_data(self, kind): for data in self.data_data(kind): name, _, kind = split_path(data["obj"]) obj = factory(kind)(name, namespace=self.svc.namespace, volatile=True, node=self.svc.node) if not obj.exists(): self.log.warning( "referenced %s %s does not exist: " "expected data %s can not be installed in the volume", kind, name, data["key"]) continue keys = obj.resolve_key(data["key"]) if not keys and "*" not in data["key"] and "?" not in data["key"]: self.log.warning( "%s %s has no key %s. " "expected data can not be installed in the volume", kind, name, data["key"]) continue for key in obj.resolve_key(data["key"]): obj._install(key, data["path"])
def configure_volume(self, volume, size=None, fmt=True, access="rwo", shared=False, nodes=None, env=None): if self.template is None: raise ex.excError("pool#%s.template is not set" % self.name) if not is_service(self.template): raise ex.excError("%s template volume not found" % self.template) name = self.default_disk_name(volume) tname, tnamespace, tkind = split_path(self.template) if tkind != "vol": raise ex.excError("%s template kind is not vol") svc = factory(tkind)(tname, tnamespace, volatile=True, node=self.node) config = svc.print_config_data() try: del config["DEFAULT"]["disable"] except KeyError: pass if "DEFAULT" not in config: config["DEFAULT"] = {} if "env" not in config: config["env"] = {} config["DEFAULT"]["pool"] = self.name config["DEFAULT"]["access"] = access if access in ("rox", "rwx"): config["DEFAULT"]["topology"] = "flex" config["DEFAULT"]["flex_min"] = 0 if nodes: config["DEFAULT"]["nodes"] = nodes config["env"]["size"] = size if env: config["env"].update(env) self.node.install_svc_conf_from_data(volume.name, volume.namespace, volume.kind, config)
def create_volume(self): volume = factory("vol")(name=self.r.volname, namespace=self.r.svc.namespace, node=self.r.svc.node) if volume.exists(): self.r.log.info("volume %s already exists", self.r.volname) data = volume.print_status_data(mon_data=True) if not data or "cluster" not in data: return volume if not self.r.svc.node.get_pool(volume.pool): raise ex.excError("pool %s not found on this node" % volume.pool) if self.r.svc.options.leader and volume.topology == "failover" and \ (self.owned() or not self.claimed(volume)) and \ data["avail"] != "up" and data["cluster"]["avail"] == "up": self.r.log.info( "volume %s is up on peer, we are leader: take it over", self.r.volname) volume.action("takeover", options={"wait": True, "time": 60}) return volume elif not self.r.svc.options.leader: self.r.log.info( "volume %s does not exist, we are not leader: wait its propagation", self.r.volname) self.r.wait_for_fn( lambda: volume.exists(), 10, 1, "non leader instance waited for too long for the " "volume to appear") return volume pooltype = self.r.oget("type") self.r.log.info( "create new volume %s (pool name: %s, pool type: %s, " "access: %s, size: %s, format: %s, shared: %s)", self.r.volname, self.r.pool, pooltype, self.r.access, print_size(self.r.size, unit="B", compact=True), self.r.format, self.r.shared) pool = self.r.svc.node.find_pool(poolname=self.r.pool, pooltype=pooltype, access=self.r.access, size=self.r.size, fmt=self.r.format, shared=self.r.shared) if pool is None: raise ex.excError("could not find a pool matching criteria") pool.log = self.r.log try: nodes = self.r.svc._get("DEFAULT.nodes") except ex.OptNotFound: nodes = None env = {} for mapping in pool.volume_env: try: src, dst = mapping.split(":", 1) except Exception: continue args = src.split(".", 1) val = self.r.svc.oget(*args) if val is None: raise ex.excError("missing mapped key in %s: %s" % (self.r.svc.path, mapping)) if is_string(val) and ".." in val: raise ex.excError( "the '..' substring is forbidden in volume env keys: %s=%s" % (mapping, val)) env[dst] = val pool.configure_volume(volume, fmt=self.r.format, size=self.r.size, access=self.r.access, nodes=nodes, shared=self.r.shared, env=env) volume = factory("vol")(name=self.r.volname, namespace=self.r.svc.namespace, node=self.r.svc.node) return volume
def gen_cert(self): data = {} for key in ("cn", "c", "st", "l", "o", "ou", "email", "alt_names", "bits", "validity", "ca"): val = self.oget("DEFAULT", key) if val is not None: data[key] = val ca = data.get("ca") casec = None if ca is not None: casecname, canamespace, _ = split_path(ca) casec = factory("sec")(casecname, namespace=canamespace, log=self.log, volatile=True) if not casec.exists(): raise ex.excError("ca secret %s does not exist" % ca) for key in ("crt", "key", "csr"): data[key] = self.tempfilename() if "alt_names" in data: data["cnf"] = self.tempfilename() try: if casec: for key, kw in (("cacrt", "certificate"), ("cakey", "private_key")): if kw not in casec.data_keys(): continue data[key] = self.tempfilename() buff = bdecode(casec.decode_key(kw)) with open(data[key], "w") as ofile: ofile.write(buff) gen_cert(log=self.log, **data) self._add("private_key", value_from=data["key"]) if data.get("crt") is not None: self._add("certificate", value_from=data["crt"]) if data.get("csr") is not None: self._add("certificate_signing_request", value_from=data["csr"]) if data.get("cakey") is None: self._add("certificate_chain", value_from=data["crt"]) else: # merge cacrt and crt chain = self.tempfilename() try: with open(data["crt"], "r") as ofile: buff = ofile.read() with open(data["cacrt"], "r") as ofile: buff += ofile.read() with open(chain, "w") as ofile: ofile.write(buff) self._add("certificate_chain", value_from=chain) finally: try: os.unlink(chain) except Exception: pass self.add_key("fullpem", self._fullpem()) finally: for key in ("crt", "key", "cacrt", "cakey", "csr", "cnf"): if key not in data: continue try: os.unlink(data[key]) except Exception: pass
def volsvc(self): return factory("vol")(name=self.volname, namespace=self.svc.namespace, node=self.svc.node)
def rbac_create_obj(self, path, cd, all_ns, thr=None, **kwargs): errors = [] name, namespace, kind = split_path(path) grants = thr.user_grants(all_ns | set([namespace])) if namespace not in all_ns: if namespace == "system": errors.append( "%s: create the new namespace system requires the root cluster role" ) return errors elif "squatter" not in grants: errors.append( "%s: create the new namespace %s requires the squatter cluster role" % (path, namespace)) return errors elif namespace not in grants["admin"]: thr.usr.set_multi(["grant+=admin:%s" % namespace]) grants["admin"].add(namespace) thr.rbac_requires(roles=["admin"], namespaces=[namespace], grants=grants, **kwargs) try: orig_obj = factory(kind)(name, namespace=namespace, volatile=True, node=shared.NODE) except: orig_obj = None try: obj = factory(kind)(name, namespace=namespace, volatile=True, cd=cd, node=shared.NODE) except Exception as exc: errors.append("%s: unbuildable config: %s" % (path, exc)) return errors if kind == "vol": errors.append("%s: volume create requires the root privilege" % path) elif kind == "ccfg": errors.append( "%s: cluster config create requires the root privilege" % path) elif kind == "svc": groups = ["disk", "fs", "app", "share", "sync"] for r in obj.get_resources(groups): if r.rid == "sync#i0": continue errors.append("%s: resource %s requires the root privilege" % (path, r.rid)) for r in obj.get_resources("task"): if r.type not in ("task.podman", "task.docker"): errors.append( "%s: resource %s type %s requires the root privilege" % (path, r.rid, r.type)) for r in obj.get_resources("container"): if r.type not in ("container.podman", "container.docker"): errors.append( "%s: resource %s type %s requires the root privilege" % (path, r.rid, r.type)) for r in obj.get_resources("ip"): if r.type not in ("ip.cni"): errors.append( "%s: resource %s type %s requires the root privilege" % (path, r.rid, r.type)) for section, sdata in cd.items(): rtype = cd[section].get("type") errors += thr.rbac_create_data_section(path, section, rtype, sdata, grants, obj, orig_obj, all_ns, thr=thr) return errors