From 35e2a4aa6b523a27d9034db61a5d75666306eec2 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 | 8 +- spec/models/service_template_spec.rb | 185 +++++++++++++++++++ 5 files changed, 219 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 6905a37f07cc..92c2cd358af9 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 227ff13cccff..833a879b8cf1 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 initialize_copy(_orig) + super.tap { |obj| obj.update_attributes(:guid => nil, :created_at => Time.zone.now, :updated_at => Time.zone.now) } + 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 000000000000..ff90bbe2e225 --- /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 78cf14c81c2c..50c2c7b675db 100644 --- a/app/models/service_template_ansible_tower.rb +++ b/app/models/service_template_ansible_tower.rb @@ -24,7 +24,13 @@ 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| + if r.persisted? + r.destroy if r.reload.resource.blank? + elsif r.resource.blank? + r.destroy + end + 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 2ddb4910dec2..1224c0aedaa0 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)