From 955f49b080ec742878d9287c218ff364036118d1 Mon Sep 17 00:00:00 2001 From: d-m-u Date: Thu, 14 Feb 2019 14:30:12 -0500 Subject: [PATCH] Add method to copy service templates --- app/models/miq_provision_request_template.rb | 4 + app/models/service_template.rb | 6 + app/models/service_template/copy.rb | 17 ++ app/models/service_template_ansible_tower.rb | 5 +- spec/models/service_template_spec.rb | 185 +++++++++++++++++++ 5 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 app/models/service_template/copy.rb diff --git a/app/models/miq_provision_request_template.rb b/app/models/miq_provision_request_template.rb index 6905a37f07c..92c2cd358af 100644 --- a/app/models/miq_provision_request_template.rb +++ b/app/models/miq_provision_request_template.rb @@ -21,6 +21,10 @@ def request_task_class MiqProvision end + def service_template_resource_copy + dup.tap(&:save!) + end + def execute # Should not be called. raise _("Provision Request Templates do not support the execute method.") diff --git a/app/models/service_template.rb b/app/models/service_template.rb index 227ff13cccf..1c107d829b2 100644 --- a/app/models/service_template.rb +++ b/app/models/service_template.rb @@ -43,7 +43,9 @@ class ServiceTemplate < ApplicationRecord include ArchivedMixin include CiFeatureMixin include_concern 'Filter' + include_concern 'Copy' + validates :name, :presence => true belongs_to :tenant # # These relationships are used to specify children spawned from a parent service # has_many :child_services, :class_name => "ServiceTemplate", :foreign_key => :service_template_id @@ -435,6 +437,10 @@ def provision_workflow(user, dialog_options = nil, request_options = {}) end end + def dup + super.tap { |obj| obj.update_attributes(:guid => nil) } + end + def add_resource(rsc, options = {}) super adjust_service_type diff --git a/app/models/service_template/copy.rb b/app/models/service_template/copy.rb new file mode 100644 index 00000000000..ff90bbe2e22 --- /dev/null +++ b/app/models/service_template/copy.rb @@ -0,0 +1,17 @@ +module ServiceTemplate::Copy + extend ActiveSupport::Concern + + def template_copy(new_name = "Copy of " + name + Time.zone.now.to_s) + if template_valid? && type != 'ServiceTemplateAnsiblePlaybook' + ActiveRecord::Base.transaction do + dup.tap do |template| + template.update_attributes(:name => new_name, :display => false) + service_resources.each do |sr| + resource = sr.resource.respond_to?(:service_template_resource_copy) ? sr.resource.service_template_resource_copy : sr.resource + template.add_resource(resource, sr) + end + end.save! + end + end + end +end diff --git a/app/models/service_template_ansible_tower.rb b/app/models/service_template_ansible_tower.rb index 78cf14c81c2..5dec54294ae 100644 --- a/app/models/service_template_ansible_tower.rb +++ b/app/models/service_template_ansible_tower.rb @@ -24,7 +24,10 @@ def self.create_catalog_item(options, _auth_user = nil) def remove_invalid_resource # remove the resource from both memory and table - service_resources.to_a.delete_if { |r| r.destroy unless r.reload.resource.present? } + service_resources.to_a.delete_if do |r| + r.reload if r.persisted? + r.destroy if r.resource.blank? + end end def create_subtasks(_parent_service_task, _parent_service) diff --git a/spec/models/service_template_spec.rb b/spec/models/service_template_spec.rb index 2ddb4910dec..1224c0aedaa 100644 --- a/spec/models/service_template_spec.rb +++ b/spec/models/service_template_spec.rb @@ -268,6 +268,191 @@ end end + context "#template_copy" do + let(:service_template_ansible_tower) { FactoryBot.create(:service_template_ansible_tower, :name => "thing") } + let(:service_template_orchestration) { FactoryBot.create(:service_template_orchestration, :name => "thing2") } + before do + @st1 = FactoryBot.create(:service_template) + end + + context "with given name" do + it "without resource " do + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy("drew") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "drew")).not_to be(nil) + expect(ServiceTemplate.find_by(:name => "drew").display).to be(false) + expect(ServiceTemplate.find_by(:name => "drew").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (configuration script base)" do + @st1.add_resource(FactoryBot.create(:configuration_script_base)) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy("thing") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing").service_resources).not_to be(nil) + expect(@st1.service_resources.first.resource).not_to be(nil) + expect(ServiceTemplate.find_by(:name => "thing").service_resources.first.resource).to eq(@st1.service_resources.first.resource) + expect(ConfigurationScriptBase.count).to eq(1) + expect(ServiceTemplate.find_by(:name => "thing").display).to be(false) + expect(ServiceTemplate.find_by(:name => "thing").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (ext management system)" do + @st1.add_resource(FactoryBot.create(:ext_management_system)) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy("thing") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing").service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ExtManagementSystem.count).to eq(1) + expect(ServiceTemplate.find_by(:name => "thing").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by(:name => "thing").service_resources).not_to be(nil) + expect(ServiceTemplate.find_by(:name => "thing").display).to be(false) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + + it "with non-copyable resource (orchestration template)" do + @st1.add_resource(FactoryBot.create(:orchestration_template)) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy("thing") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing").service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(OrchestrationTemplate.count).to eq(1) + expect(ServiceTemplate.find_by(:name => "thing").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by(:name => "thing").service_resources).not_to be(nil) + expect(ServiceTemplate.find_by(:name => "thing").display).to be(false) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + + it "with copyable resource" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + @st1.add_resource(ptr) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy("thing1") + expect(ServiceTemplate.count).to eq(2) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing1").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by(:name => "thing1").display).to be(false) + expect(ServiceTemplate.find_by(:name => "thing1").service_resources).not_to be(nil) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + + it "with copyable resource copies sr options" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + @st1.add_resource(ptr) + @st1.service_resources.first.update_attributes(:scaling_min => 4) + expect(ServiceTemplate.count).to eq(1) + expect(@st1.service_resources.first.scaling_min).to eq(4) + @st1.template_copy("thing1") + expect(ServiceTemplate.count).to eq(2) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing1").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by(:name => "thing1").display).to be(false) + expect(ServiceTemplate.find_by(:name => "thing1").service_resources.first.scaling_min).to eq(4) + expect(ServiceTemplate.find_by(:name => "thing1").service_resources).not_to be(nil) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + + it "service template ansible tower with copyable resource" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + service_template_ansible_tower.add_resource(ptr) + expect(ServiceTemplate.count).to eq(2) + service_template_ansible_tower.template_copy("thing1") + expect(ServiceTemplate.count).to eq(3) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing1").guid).not_to eq(service_template_ansible_tower.guid) + expect(ServiceTemplate.find_by(:name => "thing1").display).to be(false) + expect(ServiceTemplate.find_by(:name => "thing1").service_resources).not_to be(nil) + expect(service_template_ansible_tower.service_resources.first.resource).not_to be(nil) + end + + it "service template orchestration with copyable resource" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + service_template_orchestration.add_resource(ptr) + expect(ServiceTemplate.count).to eq(2) + service_template_orchestration.template_copy("thing1") + expect(ServiceTemplate.count).to eq(3) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing1").guid).not_to eq(service_template_orchestration.guid) + expect(ServiceTemplate.find_by(:name => "thing1").display).to be(false) + expect(ServiceTemplate.find_by(:name => "thing1").service_resources).not_to be(nil) + expect(service_template_orchestration.service_resources.first.resource).not_to be(nil) + end + end + + context "without given name" do + it "without resource" do + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").display).to be(false) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").service_resources.count).to eq(0) + expect(@st1.service_resources.count).to eq(0) + end + + it "with non-copyable resource (configuration_script_base)" do + @st1.add_resource(FactoryBot.create(:configuration_script_base)) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.where("name ILIKE ?", "Copy of service%").first.service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ConfigurationScriptBase.count).to eq(1) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").display).to be(false) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (ext management system)" do + @st1.add_resource(FactoryBot.create(:ext_management_system)) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.where("name ILIKE ?", "Copy of service%").first.service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ExtManagementSystem.count).to eq(1) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").display).to be(false) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").service_resources).not_to be(nil) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + + it "with non-copyable resource (orchestration template)" do + @st1.add_resource(FactoryBot.create(:orchestration_template)) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.where("name ILIKE ?", "Copy of service%").first.service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(OrchestrationTemplate.count).to eq(1) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").display).to be(false) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").service_resources).not_to be(nil) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + + it "with copyable resource" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + @st1.add_resource(ptr) + expect(ServiceTemplate.count).to eq(1) + @st1.template_copy + expect(ServiceTemplate.count).to eq(2) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").display).to be(false) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").service_resources).not_to be(nil) + expect(@st1.service_resources.first.resource).not_to be(nil) + end + end + end + context "#composite?" do before do @st1 = FactoryBot.create(:service_template)