diff --git a/lib/task_helpers/exports.rb b/lib/task_helpers/exports.rb index 8b0c1ec14ce..077206d1c90 100644 --- a/lib/task_helpers/exports.rb +++ b/lib/task_helpers/exports.rb @@ -10,6 +10,7 @@ def self.parse_options options = Trollop.options(EvmRakeHelper.extract_command_options) do opt :keep_spaces, 'Keep spaces in filenames', :type => :boolean, :short => 's', :default => false opt :directory, 'Directory to place exported files in', :type => :string, :required => true + opt :all, 'Export read-only objects', :type => :boolean, :default => false end error = validate_directory(options[:directory]) diff --git a/lib/task_helpers/exports/policies.rb b/lib/task_helpers/exports/policies.rb new file mode 100644 index 00000000000..6aea7d2953e --- /dev/null +++ b/lib/task_helpers/exports/policies.rb @@ -0,0 +1,20 @@ +module TaskHelpers + class Exports + class Policies + def export(options = {}) + export_dir = options[:directory] + + policies = if options[:all] + MiqPolicy.order(:id).all + else + MiqPolicy.order(:id).where(:read_only => [false, nil]) + end + + policies.each do |p| + fname = Exports.safe_filename(p.description, options[:keep_spaces]) + File.write("#{export_dir}/#{fname}.yaml", p.export_to_yaml) + end + end + end + end +end diff --git a/lib/task_helpers/exports/policy_sets.rb b/lib/task_helpers/exports/policy_sets.rb new file mode 100644 index 00000000000..47af5c005d8 --- /dev/null +++ b/lib/task_helpers/exports/policy_sets.rb @@ -0,0 +1,20 @@ +module TaskHelpers + class Exports + class PolicySets + def export(options = {}) + export_dir = options[:directory] + + policy_sets = if options[:all] + MiqPolicySet.order(:id).all + else + MiqPolicySet.order(:id).where(:read_only => [false, nil]) + end + + policy_sets.each do |p| + fname = Exports.safe_filename(p.description, options[:keep_spaces]) + File.write("#{export_dir}/#{fname}.yaml", p.export_to_yaml) + end + end + end + end +end diff --git a/lib/task_helpers/imports/policies.rb b/lib/task_helpers/imports/policies.rb new file mode 100644 index 00000000000..e61db121dca --- /dev/null +++ b/lib/task_helpers/imports/policies.rb @@ -0,0 +1,29 @@ +module TaskHelpers + class Imports + class Policies + def import(options = {}) + return unless options[:source] + + glob = File.file?(options[:source]) ? options[:source] : "#{options[:source]}/*.yaml" + Dir.glob(glob) do |fname| + begin + policies = YAML.load_file(fname) + import_policies(policies) + rescue => e + $stderr.puts "Error importing #{fname} : #{e.message}" + end + end + end + + private + + def import_policies(policies) + MiqPolicy.transaction do + policies.each do |policy| + MiqPolicy.import_from_hash(policy['MiqPolicy'], :save => true) + end + end + end + end + end +end diff --git a/lib/task_helpers/imports/policy_sets.rb b/lib/task_helpers/imports/policy_sets.rb new file mode 100644 index 00000000000..d224193e1f1 --- /dev/null +++ b/lib/task_helpers/imports/policy_sets.rb @@ -0,0 +1,29 @@ +module TaskHelpers + class Imports + class PolicySets + def import(options = {}) + return unless options[:source] + + glob = File.file?(options[:source]) ? options[:source] : "#{options[:source]}/*.yaml" + Dir.glob(glob) do |fname| + begin + policysets = YAML.load_file(fname) + import_policysets(policysets) + rescue => e + $stderr.puts "Error importing #{fname} : #{e.message}" + end + end + end + + private + + def import_policysets(policysets) + MiqPolicySet.transaction do + policysets.each do |policyset| + MiqPolicySet.import_from_hash(policyset['MiqPolicySet'], :save => true) + end + end + end + end + end +end diff --git a/lib/tasks/evm_export_import.rake b/lib/tasks/evm_export_import.rake index 3a0ee4f4923..b69dfda29bb 100644 --- a/lib/tasks/evm_export_import.rake +++ b/lib/tasks/evm_export_import.rake @@ -17,6 +17,22 @@ namespace :evm do exit # exit so that parameters to the first rake task are not run as rake tasks end + + desc 'Exports all policies to individual YAML files' + task :policies => :environment do + options = TaskHelpers::Exports.parse_options + TaskHelpers::Exports::Policies.new.export(options) + + exit # exit so that parameters to the first rake task are not run as rake tasks + end + + desc 'Exports all policy profiles to individual YAML files' + task :policyprofiles => :environment do + options = TaskHelpers::Exports.parse_options + TaskHelpers::Exports::PolicySets.new.export(options) + + exit # exit so that parameters to the first rake task are not run as rake tasks + end end namespace :import do @@ -35,5 +51,21 @@ namespace :evm do exit # exit so that parameters to the first rake task are not run as rake tasks end + + desc 'Imports all policies from individual YAML files' + task :policies => :environment do + options = TaskHelpers::Imports.parse_options + TaskHelpers::Imports::Policies.new.import(options) + + exit # exit so that parameters to the first rake task are not run as rake tasks + end + + desc 'Imports all policy profiles from individual YAML files' + task :policyprofiles => :environment do + options = TaskHelpers::Imports.parse_options + TaskHelpers::Imports::PolicySets.new.import(options) + + exit # exit so that parameters to the first rake task are not run as rake tasks + end end end diff --git a/spec/factories/miq_policy_set.rb b/spec/factories/miq_policy_set.rb index f507fd8e88c..8060225456b 100644 --- a/spec/factories/miq_policy_set.rb +++ b/spec/factories/miq_policy_set.rb @@ -2,4 +2,8 @@ factory :miq_policy_set do description "Test Policy Set" end + + factory :miq_policy_set_read_only, :parent => :miq_policy_set do + read_only true + end end diff --git a/spec/lib/task_helpers/exports/policies_spec.rb b/spec/lib/task_helpers/exports/policies_spec.rb new file mode 100644 index 00000000000..3dc299c6cc3 --- /dev/null +++ b/spec/lib/task_helpers/exports/policies_spec.rb @@ -0,0 +1,94 @@ +describe TaskHelpers::Exports::Policies do + let(:guid) { "a61314d5-67bd-435f-9c82-b82226e0a7fe" } + let(:guid2) { "ac7e2972-f2b2-4ebe-b29d-97eefaac7615" } + + let(:profile_export_attrs) do + [ + { + "MiqPolicy" => { + "name" => "a61314d5-67bd-435f-9c82-b82226e0a7fe", + "description" => "Test Compliance Policy", + "expression" => nil, + "towhat" => "Vm", + "guid" => "a61314d5-67bd-435f-9c82-b82226e0a7fe", + "created_by" => nil, + "updated_by" => nil, + "notes" => nil, + "active" => true, + "mode" => "compliance", + "read_only" => nil, + "MiqPolicyContent" => [], + "Condition" => [] + } + } + ] + end + + let(:policy_create_attrs) do + { + :description => "Test Compliance Policy", + :name => guid, + :guid => guid, + :mode => "compliance", + } + end + + let(:profile2_export_attrs) do + [ + { + "MiqPolicy" => { + "name" => "ac7e2972-f2b2-4ebe-b29d-97eefaac7615", + "description" => "Test Compliance Policy 2", + "expression" => nil, + "towhat" => "Host", + "guid" => "ac7e2972-f2b2-4ebe-b29d-97eefaac7615", + "created_by" => nil, + "updated_by" => nil, + "notes" => nil, + "active" => true, + "mode" => "control", + "read_only" => true, + "MiqPolicyContent" => [], + "Condition" => [] + } + } + ] + end + + let(:policy2_create_attrs) do + { + :description => "Test Compliance Policy 2", + :name => guid2, + :guid => guid2, + :towhat => "Host" + } + end + + let(:export_dir) do + Dir.mktmpdir('miq_exp_dir') + end + + before do + FactoryGirl.create(:miq_policy, policy_create_attrs) + FactoryGirl.create(:miq_policy_read_only, policy2_create_attrs) + end + + after do + FileUtils.remove_entry export_dir + end + + it 'exports user policies to a given directory' do + TaskHelpers::Exports::Policies.new.export(:directory => export_dir) + file_contents = File.read("#{export_dir}/Test_Compliance_Policy.yaml") + expect(YAML.safe_load(file_contents)).to eq(profile_export_attrs) + expect(Dir[File.join(export_dir, '**', '*')].count { |file| File.file?(file) }).to eq(1) + end + + it 'exports all policies to a given directory' do + TaskHelpers::Exports::Policies.new.export(:directory => export_dir, :all => true) + file_contents = File.read("#{export_dir}/Test_Compliance_Policy.yaml") + file_contents2 = File.read("#{export_dir}/Test_Compliance_Policy_2.yaml") + expect(YAML.safe_load(file_contents)).to eq(profile_export_attrs) + expect(YAML.safe_load(file_contents2)).to eq(profile2_export_attrs) + end +end diff --git a/spec/lib/task_helpers/exports/policy_sets_spec.rb b/spec/lib/task_helpers/exports/policy_sets_spec.rb new file mode 100644 index 00000000000..ec5f22f9fde --- /dev/null +++ b/spec/lib/task_helpers/exports/policy_sets_spec.rb @@ -0,0 +1,87 @@ +describe TaskHelpers::Exports::PolicySets do + let(:guid) { "a3734dcc-e25d-4164-ba95-1114568d491a" } + let(:guid2) { "328593c3-a0a2-4a31-8d58-1a3eeef0ce95" } + + let(:policy_set_export_attrs) do + [ + { + "MiqPolicySet" => { + "name" => "a3734dcc-e25d-4164-ba95-1114568d491a", + "description" => "Policy Set Export Test", + "set_type" => "MiqPolicySet", + "guid" => "a3734dcc-e25d-4164-ba95-1114568d491a", + "read_only" => nil, + "set_data" => nil, + "mode" => nil, + "owner_type" => nil, + "owner_id" => nil, + "userid" => nil, + "group_id" => nil, + "MiqPolicy" => [] + } + } + ] + end + + let(:policy_set_create_attrs) do + { + :description => "Policy Set Export Test", + :guid => guid, + :name => guid + } + end + + let(:policy2_set_export_attrs) do + [ + { + "MiqPolicySet" => { + "name" => "328593c3-a0a2-4a31-8d58-1a3eeef0ce95", + "description" => "Policy Set Export Test 2", + "set_type" => "MiqPolicySet", + "guid" => "328593c3-a0a2-4a31-8d58-1a3eeef0ce95", + "read_only" => true, + "set_data" => nil, + "mode" => nil, + "owner_type" => nil, + "owner_id" => nil, + "userid" => nil, + "group_id" => nil, + "MiqPolicy" => [] + } + } + ] + end + + let(:policy2_set_create_attrs) do + { + :description => "Policy Set Export Test 2", + :guid => guid2, + :name => guid2 + } + end + + let(:export_dir) do + Dir.mktmpdir('miq_exp_dir') + end + + before do + FactoryGirl.create(:miq_policy_set, policy_set_create_attrs) + FactoryGirl.create(:miq_policy_set_read_only, policy2_set_create_attrs) + end + + after do + FileUtils.remove_entry export_dir + end + + it 'exports user policy sets to a given directory' do + TaskHelpers::Exports::PolicySets.new.export(:directory => export_dir) + file_contents = File.read("#{export_dir}/Policy_Set_Export_Test.yaml") + expect(YAML.safe_load(file_contents)).to eq(policy_set_export_attrs) + end + + it 'exports all policy sets to a given directory' do + TaskHelpers::Exports::PolicySets.new.export(:directory => export_dir, :all => true) + file_contents = File.read("#{export_dir}/Policy_Set_Export_Test_2.yaml") + expect(YAML.safe_load(file_contents)).to eq(policy2_set_export_attrs) + end +end diff --git a/spec/lib/task_helpers/imports/data/policies/Bad_Policy_Import_Test.yml b/spec/lib/task_helpers/imports/data/policies/Bad_Policy_Import_Test.yml new file mode 100644 index 00000000000..4c7a54d7f0c --- /dev/null +++ b/spec/lib/task_helpers/imports/data/policies/Bad_Policy_Import_Test.yml @@ -0,0 +1,37 @@ +--- +- MiqPolicy: + name: 7562ca69-a00d-4017-be8f-d31d39a07deb + description: + expression: !ruby/object:MiqExpression + exp: + "=": + field: Vm-cpu_hot_add_enabled + value: 'false' + context_type: + towhat: Vm + guid: 7562ca69-a00d-4017-be8f-d31d39a07deb + created_by: admin + updated_by: admin + notes: + active: true + mode: compliance + read_only: + MiqPolicyContent: + - qualifier: failure + failure_sequence: 1 + failure_synchronous: true + MiqEventDefinition: + name: vm_compliance_check + description: VM Compliance Check + guid: dc3c6494-1a5e-11e7-9bb4-02426c6b2651 + event_type: Default + definition: + default: + enabled: + MiqAction: + name: compliance_failed + description: Mark as Non-Compliant + guid: db553dc6-1a5e-11e7-9bb4-02426c6b2651 + action_type: default + options: {} + Condition: [] diff --git a/spec/lib/task_helpers/imports/data/policies/Policy_Import_Test.yaml b/spec/lib/task_helpers/imports/data/policies/Policy_Import_Test.yaml new file mode 100644 index 00000000000..f74e30a2d86 --- /dev/null +++ b/spec/lib/task_helpers/imports/data/policies/Policy_Import_Test.yaml @@ -0,0 +1,37 @@ +--- +- MiqPolicy: + name: 7562ca69-a00d-4017-be8f-d31d39a07deb + description: Policy Import Test + expression: !ruby/object:MiqExpression + exp: + "=": + field: Vm-cpu_hot_add_enabled + value: 'false' + context_type: + towhat: Vm + guid: 7562ca69-a00d-4017-be8f-d31d39a07deb + created_by: admin + updated_by: admin + notes: + active: true + mode: compliance + read_only: + MiqPolicyContent: + - qualifier: failure + failure_sequence: 1 + failure_synchronous: true + MiqEventDefinition: + name: vm_compliance_check + description: VM Compliance Check + guid: dc3c6494-1a5e-11e7-9bb4-02426c6b2651 + event_type: Default + definition: + default: + enabled: + MiqAction: + name: compliance_failed + description: Mark as Non-Compliant + guid: db553dc6-1a5e-11e7-9bb4-02426c6b2651 + action_type: default + options: {} + Condition: [] diff --git a/spec/lib/task_helpers/imports/data/policies/Policy_Import_Test_2.yaml b/spec/lib/task_helpers/imports/data/policies/Policy_Import_Test_2.yaml new file mode 100644 index 00000000000..8258f9f5440 --- /dev/null +++ b/spec/lib/task_helpers/imports/data/policies/Policy_Import_Test_2.yaml @@ -0,0 +1,20 @@ +--- +- MiqPolicy: + name: b314df11-9790-47a1-8e12-14fa124cc862 + description: Policy Import Test 2 + expression: !ruby/object:MiqExpression + exp: + "=": + field: Host-admin_disabled + value: 'false' + context_type: + towhat: Host + guid: b314df11-9790-47a1-8e12-14fa124cc862 + created_by: admin + updated_by: admin + notes: + active: true + mode: control + read_only: + MiqPolicyContent: [] + Condition: [] diff --git a/spec/lib/task_helpers/imports/data/policy_sets/Bad_Policy_Profile_Import_Test.yml b/spec/lib/task_helpers/imports/data/policy_sets/Bad_Policy_Profile_Import_Test.yml new file mode 100644 index 00000000000..9668742c178 --- /dev/null +++ b/spec/lib/task_helpers/imports/data/policy_sets/Bad_Policy_Profile_Import_Test.yml @@ -0,0 +1,49 @@ +--- +- MiqPolicySet: + name: 869d8a1c-eef8-4075-8f10-fb2b4198c20d + description: + set_type: MiqPolicySet + guid: 869d8a1c-eef8-4075-8f10-fb2b4198c20d + read_only: + set_data: + mode: + owner_type: + owner_id: + userid: + group_id: + MiqPolicy: + - name: 7562ca69-a00d-4017-be8f-d31d39a07deb + description: Test Compliance Policy + expression: !ruby/object:MiqExpression + exp: + "=": + field: Vm-cpu_hot_add_enabled + value: 'false' + context_type: + towhat: Vm + guid: 7562ca69-a00d-4017-be8f-d31d39a07deb + created_by: admin + updated_by: admin + notes: + active: true + mode: compliance + read_only: + MiqPolicyContent: + - qualifier: failure + failure_sequence: 1 + failure_synchronous: true + MiqEventDefinition: + name: vm_compliance_check + description: VM Compliance Check + guid: dc3c6494-1a5e-11e7-9bb4-02426c6b2651 + event_type: Default + definition: + default: + enabled: + MiqAction: + name: compliance_failed + description: Mark as Non-Compliant + guid: db553dc6-1a5e-11e7-9bb4-02426c6b2651 + action_type: default + options: {} + Condition: [] diff --git a/spec/lib/task_helpers/imports/data/policy_sets/Policy_Profile_Import_Test.yaml b/spec/lib/task_helpers/imports/data/policy_sets/Policy_Profile_Import_Test.yaml new file mode 100644 index 00000000000..3331f73082b --- /dev/null +++ b/spec/lib/task_helpers/imports/data/policy_sets/Policy_Profile_Import_Test.yaml @@ -0,0 +1,49 @@ +--- +- MiqPolicySet: + name: 869d8a1c-eef8-4075-8f10-fb2b4198c20d + description: Policy Profile Import Test + set_type: MiqPolicySet + guid: 869d8a1c-eef8-4075-8f10-fb2b4198c20d + read_only: + set_data: + mode: + owner_type: + owner_id: + userid: + group_id: + MiqPolicy: + - name: 7562ca69-a00d-4017-be8f-d31d39a07deb + description: Test Compliance Policy + expression: !ruby/object:MiqExpression + exp: + "=": + field: Vm-cpu_hot_add_enabled + value: 'false' + context_type: + towhat: Vm + guid: 7562ca69-a00d-4017-be8f-d31d39a07deb + created_by: admin + updated_by: admin + notes: + active: true + mode: compliance + read_only: + MiqPolicyContent: + - qualifier: failure + failure_sequence: 1 + failure_synchronous: true + MiqEventDefinition: + name: vm_compliance_check + description: VM Compliance Check + guid: dc3c6494-1a5e-11e7-9bb4-02426c6b2651 + event_type: Default + definition: + default: + enabled: + MiqAction: + name: compliance_failed + description: Mark as Non-Compliant + guid: db553dc6-1a5e-11e7-9bb4-02426c6b2651 + action_type: default + options: {} + Condition: [] diff --git a/spec/lib/task_helpers/imports/data/policy_sets/Policy_Profile_Import_Test_2.yaml b/spec/lib/task_helpers/imports/data/policy_sets/Policy_Profile_Import_Test_2.yaml new file mode 100644 index 00000000000..afca82fb8af --- /dev/null +++ b/spec/lib/task_helpers/imports/data/policy_sets/Policy_Profile_Import_Test_2.yaml @@ -0,0 +1,32 @@ +--- +- MiqPolicySet: + name: b762f0cb-8a50-4464-8ded-1f1ce341f3a7 + description: Policy Profile Import Test 2 + set_type: MiqPolicySet + guid: b762f0cb-8a50-4464-8ded-1f1ce341f3a7 + read_only: + set_data: + mode: + owner_type: + owner_id: + userid: + group_id: + MiqPolicy: + - name: b314df11-9790-47a1-8e12-14fa124cc862 + description: Test Control Policy + expression: !ruby/object:MiqExpression + exp: + "=": + field: Host-admin_disabled + value: 'false' + context_type: + towhat: Host + guid: b314df11-9790-47a1-8e12-14fa124cc862 + created_by: admin + updated_by: admin + notes: + active: true + mode: control + read_only: + MiqPolicyContent: [] + Condition: [] diff --git a/spec/lib/task_helpers/imports/policies_spec.rb b/spec/lib/task_helpers/imports/policies_spec.rb new file mode 100644 index 00000000000..dcdf2fad6ba --- /dev/null +++ b/spec/lib/task_helpers/imports/policies_spec.rb @@ -0,0 +1,60 @@ +describe TaskHelpers::Imports::Policies do + let(:data_dir) { File.join(File.expand_path(__dir__), 'data', 'policies') } + let(:policy_file) { 'Policy_Import_Test.yaml' } + let(:bad_policy_file) { 'Bad_Policy_Import_Test.yml' } + let(:policy_one_guid) { "7562ca69-a00d-4017-be8f-d31d39a07deb" } + let(:policy_two_guid) { "b314df11-9790-47a1-8e12-14fa124cc862" } + + describe "#import" do + let(:options) { {:source => source} } + + describe "when the source is a directory" do + let(:source) { data_dir } + + it 'imports all .yaml files in a specified directory' do + expect do + TaskHelpers::Imports::Policies.new.import(options) + end.to_not output.to_stderr + assert_test_policy_one_present + assert_test_policy_two_present + end + end + + describe "when the source is a valid policy file" do + let(:source) { "#{data_dir}/#{policy_file}" } + + it 'imports a specified policy export file' do + expect do + TaskHelpers::Imports::Policies.new.import(options) + end.to_not output.to_stderr + + assert_test_policy_one_present + expect(MiqPolicy.find_by(:guid => policy_two_guid)).to be_nil + end + end + + describe "when the source is an invalid policy file" do + let(:source) { "#{data_dir}/#{bad_policy_file}" } + + it 'fails to import a specified policy file' do + expect do + TaskHelpers::Imports::Policies.new.import(options) + end.to output.to_stderr + end + end + end + + def assert_test_policy_one_present + p = MiqPolicy.find_by(:guid => policy_one_guid) + expect(p.description).to eq("Policy Import Test") + expect(p.mode).to eq("compliance") + expect(p.active).to be true + end + + def assert_test_policy_two_present + p = MiqPolicy.find_by(:guid => policy_two_guid) + expect(p.description).to eq("Policy Import Test 2") + expect(p.mode).to eq("control") + expect(p.active).to be true + end +end diff --git a/spec/lib/task_helpers/imports/policy_sets_spec.rb b/spec/lib/task_helpers/imports/policy_sets_spec.rb new file mode 100644 index 00000000000..c80389ad8b1 --- /dev/null +++ b/spec/lib/task_helpers/imports/policy_sets_spec.rb @@ -0,0 +1,64 @@ +describe TaskHelpers::Imports::PolicySets do + let(:data_dir) { File.join(File.expand_path(__dir__), 'data', 'policy_sets') } + let(:policy_set_file) { 'Policy_Profile_Import_Test.yaml' } + let(:bad_policy_set_file) { 'Bad_Policy_Profile_Import_Test.yml' } + let(:policy_set_one_guid) { "869d8a1c-eef8-4075-8f10-fb2b4198c20d" } + let(:policy_set_two_guid) { "b762f0cb-8a50-4464-8ded-1f1ce341f3a7" } + + describe "#import" do + let(:options) { {:source => source} } + + describe "when the source is a directory" do + let(:source) { data_dir } + it 'imports all .yaml files in a specified directory' do + expect do + TaskHelpers::Imports::PolicySets.new.import(options) + end.to_not output.to_stderr + + assert_test_policy_set_one_present + assert_test_policy_set_two_present + end + end + + describe "when the source is a valid policy set file" do + let(:source) { "#{data_dir}/#{policy_set_file}" } + + it 'should import a specified policy set export file' do + expect do + TaskHelpers::Imports::PolicySets.new.import(options) + end.to_not output.to_stderr + + assert_test_policy_set_one_present + expect(MiqPolicySet.find_by(:guid => policy_set_two_guid)).to be_nil + end + end + + describe "when the source is an invalid policy set file" do + let(:source) { "#{data_dir}/#{bad_policy_set_file}" } + + it 'should fail to import a specified policy set file' do + expect do + TaskHelpers::Imports::PolicySets.new.import(options) + end.to output.to_stderr + end + end + end + + def assert_test_policy_set_one_present + p = MiqPolicySet.find_by(:guid => policy_set_one_guid) + expect(p.description).to eq("Policy Profile Import Test") + b = p.miq_policies.first + expect(b.guid).to eq("7562ca69-a00d-4017-be8f-d31d39a07deb") + expect(b.description).to eq("Test Compliance Policy") + expect(b.active).to be true + end + + def assert_test_policy_set_two_present + p = MiqPolicySet.find_by(:guid => policy_set_two_guid) + expect(p.description).to eq("Policy Profile Import Test 2") + b = p.miq_policies.first + expect(b.guid).to eq("b314df11-9790-47a1-8e12-14fa124cc862") + expect(b.description).to eq("Test Control Policy") + expect(b.active).to be true + end +end