From 7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Tue, 29 Oct 2019 10:51:41 -0500 Subject: hscript: Implement lvm_vg, add tests --- hscript/disk.cc | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hscript/disk.hh | 17 +++++++++ hscript/script.cc | 45 ++++++++++++++++++++++- 3 files changed, 166 insertions(+), 1 deletion(-) (limited to 'hscript') diff --git a/hscript/disk.cc b/hscript/disk.cc index 3de34e1..634e56f 100644 --- a/hscript/disk.cc +++ b/hscript/disk.cc @@ -441,6 +441,111 @@ bool LVMPhysical::execute(ScriptOptions) const { } +/*! Determine if a string is a valid LVM VG/LV name. + * @param name The name of which to test validity. + * @returns true if the string is a valid name, false otherwise. + * @note LVM LVs have additional restrictions; see is_valid_lvm_lv_name. + */ +bool is_valid_lvm_name(const std::string &name) { + if(name[0] == '.' && (name.length() == 1 || name[1] == '.')) { + /* . and .. are invalid */ + return false; + } + if(name[0] == '-') { + /* VG nor LV may start with - */ + return false; + } + + const std::string valid_syms("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+_.-"); + return (name.find_first_not_of(valid_syms) == std::string::npos); +} + +/*! Determine if a string is a valid LVM LV name. + * @param name The name of which to test validity. + * @returns true if the string is a valid LV name, false otherwise. + */ +bool is_valid_lvm_lv_name(const std::string &name) { + if(!is_valid_lvm_name(name)) { + /* Fail fast if we fail the general test. */ + return false; + } + + if(name == "snapshot" || name == "pvmove") { + /* Invalid full names. */ + return false; + } + + if(name.find("_cdata") != std::string::npos || + name.find("_cmeta") != std::string::npos || + name.find("_corig") != std::string::npos || + name.find("_mlog") != std::string::npos || + name.find("_mimage") != std::string::npos || + name.find("_pmspare") != std::string::npos || + name.find("_rimage") != std::string::npos || + name.find("_rmeta") != std::string::npos || + name.find("_tdata") != std::string::npos || + name.find("_tmeta") != std::string::npos || + name.find("_vorigin") != std::string::npos) { + /* Cannot occur anywhere in the name. */ + return false; + } + + return true; +} + + +Key *LVMGroup::parseFromData(const std::string &data, int lineno, int *errors, + int *) { + std::string::size_type space = data.find_first_of(' '); + if(space == std::string::npos || data.size() == space + 1) { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "lvm_vg: expected exactly two elements", + "syntax is lvm_vg [pv_block] [name-of-vg]"); + return nullptr; + } + + const std::string pv(data.substr(0, space)); + const std::string name(data.substr(space + 1)); + + if(pv.length() < 6 || pv.substr(0, 5) != "/dev/") { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "lvm_vg: expected absolute path to block device"); + return nullptr; + } + + if(!is_valid_lvm_name(name)) { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "lvm_vg: invalid volume group name"); + return nullptr; + } + + return new LVMGroup(lineno, pv, name); +} + +bool LVMGroup::validate(ScriptOptions) const { + /* validation occurs during parsing */ + return true; +} + +bool LVMGroup::test_pv(ScriptOptions) const { + const char *fstype = blkid_get_tag_value(nullptr, "TYPE", + this->pv().c_str()); + if(fstype == nullptr) { + /* inconclusive */ + return true; + } + + return (strcmp(fstype, "LVM2_member") == 0); +} + +bool LVMGroup::execute(ScriptOptions) const { + return false; +} + + Key *Mount::parseFromData(const std::string &data, int lineno, int *errors, int *warnings) { std::string dev, where, opt; diff --git a/hscript/disk.hh b/hscript/disk.hh index 53527f3..a9adba4 100644 --- a/hscript/disk.hh +++ b/hscript/disk.hh @@ -125,6 +125,23 @@ public: }; class LVMGroup : public Key { +private: + const std::string _pv; + const std::string _vgname; + + LVMGroup(int _line, const std::string &_p, const std::string &_v) : + Key(_line), _pv(_p), _vgname(_v) {} +public: + /*! Retrieve the physical volume where this volume group will reside. */ + const std::string pv() const { return this->_pv; } + /*! Retrieve the name of this volume group. */ + const std::string name() const { return this->_vgname; } + + static Key *parseFromData(const std::string &, int, int*, int*); + bool validate(ScriptOptions) const override; + /*! Determine if the PV passed is a real one. */ + bool test_pv(ScriptOptions) const; + bool execute(ScriptOptions) const override; }; class LVMVolume : public Key { diff --git a/hscript/script.cc b/hscript/script.cc index 3815d1c..9913b2a 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -672,7 +672,7 @@ bool add_default_repos(std::vector> &repos) { bool Script::validate() const { int failures = 0; std::set seen_diskids, seen_labels, seen_parts, seen_pvs, - seen_mounts; + seen_vg_names, seen_vg_pvs, seen_mounts; std::map seen_iface; /* REQ: Runner.Validate.network */ @@ -844,6 +844,49 @@ bool Script::validate() const { seen_pvs.insert(pv->value()); } + /* REQ: Runner.Validate.lvm_vg */ + for(auto &vg : this->internal->lvm_vgs) { + if(!vg->validate(this->opts)) { + failures++; + continue; + } + + if(seen_vg_names.find(vg->name()) != seen_vg_names.end()) { + failures++; + output_error("installfile:" + std::to_string(vg->lineno()), + "lvm_vg: duplicate volume group name specified", + vg->name() + " already given"); + } + seen_vg_names.insert(vg->name()); + + if(seen_vg_pvs.find(vg->pv()) != seen_vg_pvs.end()) { + failures++; + output_error("installfile:" + std::to_string(vg->lineno()), + "lvm_vg: a volume group already exists on " + + vg->pv()); + } + seen_vg_pvs.insert(vg->pv()); + + /* REQ: Runner.Validate.lvm_vg.PhysicalVolume */ + /* If we already know a PV is being created there, we know it's fine */ + if(seen_pvs.find(vg->pv()) == seen_pvs.end()) { + /* Okay, let's see if a PV already exists there... */ + if(opts.test(InstallEnvironment)) { + if(!vg->test_pv(opts)) { + failures++; + output_error("installfile:" + std::to_string(vg->lineno()), + "lvm_vg: a physical volume does not exist on " + + vg->pv()); + } + } else { + /* We can't tell if we aren't running on the target. */ + output_warning("installfile:" + std::to_string(vg->lineno()), + "lvm_vg: please ensure an LVM physical volume " + "already exists at " + vg->pv()); + } + } + } + /* REQ: Runner.Validate.mount */ for(auto &mount : this->internal->mounts) { if(!mount->validate(this->opts)) { -- cgit v1.2.3-60-g2f50