summaryrefslogtreecommitdiff
path: root/hscript
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-29 10:51:41 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-29 10:51:41 -0500
commit7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64 (patch)
tree007870da7872a8828d4bc4babfc49f9e01557992 /hscript
parent84b7e98b805b86e4d63e2ce38a490e86541ed0da (diff)
downloadhorizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.tar.gz
horizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.tar.bz2
horizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.tar.xz
horizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.zip
hscript: Implement lvm_vg, add tests
Diffstat (limited to 'hscript')
-rw-r--r--hscript/disk.cc105
-rw-r--r--hscript/disk.hh17
-rw-r--r--hscript/script.cc45
3 files changed, 166 insertions, 1 deletions
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<std::unique_ptr<Keys::Repository>> &repos) {
bool Script::validate() const {
int failures = 0;
std::set<std::string> seen_diskids, seen_labels, seen_parts, seen_pvs,
- seen_mounts;
+ seen_vg_names, seen_vg_pvs, seen_mounts;
std::map<const std::string, int> 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)) {