summaryrefslogtreecommitdiff
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
parent84b7e98b805b86e4d63e2ce38a490e86541ed0da (diff)
downloadhorizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.tar.gz
horizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.tar.bz2
horizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.tar.xz
horizon-7fbaf52dbd9a9afb406d4a631ff5a1625c90ef64.zip
hscript: Implement lvm_vg, add tests
-rw-r--r--hscript/disk.cc105
-rw-r--r--hscript/disk.hh17
-rw-r--r--hscript/script.cc45
-rw-r--r--tests/fixtures/0166-lvmvg-basic.installfile11
-rw-r--r--tests/fixtures/0167-lvmvg-invalid.installfile11
-rw-r--r--tests/fixtures/0168-lvmvg-duplicate.installfile12
-rw-r--r--tests/fixtures/0169-lvmvg-dash.installfile11
-rw-r--r--tests/fixtures/0170-lvmvg-without-vg.installfile11
-rw-r--r--tests/spec/validator_spec.rb28
9 files changed, 250 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)) {
diff --git a/tests/fixtures/0166-lvmvg-basic.installfile b/tests/fixtures/0166-lvmvg-basic.installfile
new file mode 100644
index 0000000..8d970bc
--- /dev/null
+++ b/tests/fixtures/0166-lvmvg-basic.installfile
@@ -0,0 +1,11 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+diskid /dev/sdb WDC
+disklabel /dev/sdb apm
+partition /dev/sdb 1 8M boot
+partition /dev/sdb 2 fill
+lvm_pv /dev/sdb2
+lvm_vg /dev/sdb2 MyVolGroup
diff --git a/tests/fixtures/0167-lvmvg-invalid.installfile b/tests/fixtures/0167-lvmvg-invalid.installfile
new file mode 100644
index 0000000..e139e41
--- /dev/null
+++ b/tests/fixtures/0167-lvmvg-invalid.installfile
@@ -0,0 +1,11 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+diskid /dev/sdb WDC
+disklabel /dev/sdb apm
+partition /dev/sdb 1 8M boot
+partition /dev/sdb 2 fill
+lvm_pv /dev/sdb2
+lvm_vg sdb2 MyVolGroup
diff --git a/tests/fixtures/0168-lvmvg-duplicate.installfile b/tests/fixtures/0168-lvmvg-duplicate.installfile
new file mode 100644
index 0000000..2a6bfd0
--- /dev/null
+++ b/tests/fixtures/0168-lvmvg-duplicate.installfile
@@ -0,0 +1,12 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+diskid /dev/sdb WDC
+disklabel /dev/sdb apm
+partition /dev/sdb 1 8M boot
+partition /dev/sdb 2 fill
+lvm_pv /dev/sdb2
+lvm_vg /dev/sdb2 MyVolGroup
+lvm_vg /dev/sdb2 MyVolGroup
diff --git a/tests/fixtures/0169-lvmvg-dash.installfile b/tests/fixtures/0169-lvmvg-dash.installfile
new file mode 100644
index 0000000..057a49c
--- /dev/null
+++ b/tests/fixtures/0169-lvmvg-dash.installfile
@@ -0,0 +1,11 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+diskid /dev/sdb WDC
+disklabel /dev/sdb apm
+partition /dev/sdb 1 8M boot
+partition /dev/sdb 2 fill
+lvm_pv /dev/sdb2
+lvm_vg /dev/sdb2 -MyVolGroup
diff --git a/tests/fixtures/0170-lvmvg-without-vg.installfile b/tests/fixtures/0170-lvmvg-without-vg.installfile
new file mode 100644
index 0000000..2cf015e
--- /dev/null
+++ b/tests/fixtures/0170-lvmvg-without-vg.installfile
@@ -0,0 +1,11 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+diskid /dev/sdb WDC
+disklabel /dev/sdb apm
+partition /dev/sdb 1 8M boot
+partition /dev/sdb 2 fill
+lvm_pv /dev/sdb2
+lvm_vg /dev/sdb2
diff --git a/tests/spec/validator_spec.rb b/tests/spec/validator_spec.rb
index 976f0f6..0443140 100644
--- a/tests/spec/validator_spec.rb
+++ b/tests/spec/validator_spec.rb
@@ -806,6 +806,34 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do
expect(last_command_started).to have_output(/error: .*lvm_pv.*exists/)
end
end
+ context "for 'lvm_vg' key" do
+ it "succeeds with a normal value" do
+ use_fixture '0166-lvmvg-basic.installfile'
+ run_validate
+ expect(last_command_started).to have_output(PARSER_SUCCESS)
+ expect(last_command_started).to have_output(VALIDATOR_SUCCESS)
+ end
+ it "fails with an invalid PV path" do
+ use_fixture '0167-lvmvg-invalid.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*lvm_vg.*path/)
+ end
+ it "fails with a duplicate volume group name" do
+ use_fixture '0168-lvmvg-duplicate.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*lvm_vg.*exists/)
+ end
+ it "fails with a dash as the first character of the VG name" do
+ use_fixture '0169-lvmvg-dash.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*lvm_vg.*name/)
+ end
+ it "requires a volume group name" do
+ use_fixture '0170-lvmvg-without-vg.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*lvm_vg.*expected/)
+ end
+ end
end
context "unique keys" do
# Runner.Validate.network.