diff --git a/lib/hammer_cli_csv/activation_keys.rb b/lib/hammer_cli_csv/activation_keys.rb index 90321b0..5f55fa3 100644 --- a/lib/hammer_cli_csv/activation_keys.rb +++ b/lib/hammer_cli_csv/activation_keys.rb @@ -9,68 +9,48 @@ class ActivationKeysCommand < BaseCommand LIMIT = 'Limit' ENVIRONMENT = 'Environment' CONTENTVIEW = 'Content View' - HOSTCOLLECTIONS = 'System Groups' + HOSTCOLLECTIONS = 'Host Collections' + SERVICELEVEL = "Service Level" + RELEASEVER = "Release Version" + AUTOATTACH = "Auto-Attach" SUBSCRIPTIONS = 'Subscriptions' def export CSV.open(option_file || '/dev/stdout', 'wb', {:force_quotes => false}) do |csv| csv << [NAME, ORGANIZATION, DESCRIPTION, LIMIT, ENVIRONMENT, CONTENTVIEW, - HOSTCOLLECTIONS, SUBSCRIPTIONS] - if @server_status['release'] == 'Headpin' - @headpin.get(:organizations).each do |organization| - next if option_organization && organization['name'] != option_organization - - @headpin.get("organizations/#{organization['label']}/activation_keys").each do |activationkey| - name = namify(activationkey['name']) - description = activationkey['description'] - limit = activationkey['usage_limit'].to_i < 0 ? 'Unlimited' : activationkey['usage_limit'] - environment = @headpin.environment(activationkey['environment_id'])['name'] - contentview = @headpin.content_view(activationkey['content_view_id'])['name'] - # TODO: https://bugzilla.redhat.com/show_bug.cgi?id=1160888 - # Act keys in SAM-1 do not include system groups - hostcollections = nil #???? export_column(activationkey, 'systemGroups', 'name') - subscriptions = CSV.generate do |column| - column << activationkey['pools'].collect do |subscription| - amount = subscription['calculatedAttributes']['compliance_type'] == 'Stackable' ? 1 : 'Automatic' - "#{amount}|#{subscription['productId']}|#{subscription['productName']}" - end + HOSTCOLLECTIONS, AUTOATTACH, SERVICELEVEL, RELEASEVER, SUBSCRIPTIONS] + @api.resource(:organizations).call(:index, { + :per_page => 999999 + })['results'].each do |organization| + next if option_organization && organization['name'] != option_organization + + @api.resource(:activation_keys).call(:index, { + 'per_page' => 999999, + 'organization_id' => organization['id'] + })['results'].each do |activationkey| + name = namify(activationkey['name']) + count = 1 + description = activationkey['description'] + limit = activationkey['unlimited_content_hosts'] ? 'Unlimited' : activationkey['max_content_hosts'] + environment = activationkey['environment']['label'] + contentview = activationkey['content_view']['name'] + hostcollections = export_column(activationkey, 'host_collections', 'name') + autoattach = activationkey['auto_attach'] ? 'Yes' : 'No' + servicelevel = activationkey['service_level'] + releasever = activationkey['release_version'] + subscriptions = CSV.generate do |column| + column << @api.resource(:subscriptions).call(:index, { + 'organization_id' => organization['id'], + 'activation_key_id' => activationkey['id'] + })['results'].collect do |subscription| + amount = subscription['amount'] == 0 ? 'Automatic' : subscription['amount'] + sku = subscription['product_id'].match(/\A[0-9]/) ? 'Custom' : subscription['product_id'] + "#{amount}|#{sku}|#{subscription['product_name']}" end - subscriptions.delete!("\n") - csv << [name, organization['label'], description, limit, environment, contentview, - hostcollections, subscriptions] - end - end - else - @api.resource(:organizations).call(:index, { - :per_page => 999999 - })['results'].each do |organization| - next if option_organization && organization['name'] != option_organization - - @api.resource(:activation_keys).call(:index, { - 'per_page' => 999999, - 'organization_id' => organization['id'] - })['results'].each do |activationkey| - name = namify(activationkey['name']) - count = 1 - description = activationkey['description'] - limit = activationkey['unlimited_content_hosts'] ? 'Unlimited' : activationkey['max_content_hosts'] - environment = activationkey['environment']['label'] - contentview = activationkey['content_view']['name'] - hostcollections = export_column(activationkey, 'systemGroups', 'name') - subscriptions = CSV.generate do |column| - column << @api.resource(:subscriptions).call(:index, { - 'organization_id' => organization['id'], - 'activation_key_id' => activationkey['id'] - })['results'].collect do |subscription| - amount = subscription['amount'] == 0 ? 'Automatic' : subscription['amount'] - sku = subscription['product_id'].match(/\A[0-9]/) ? 'Custom' : subscription['product_id'] - "#{amount}|#{sku}|#{subscription['product_name']}" - end - end - subscriptions.delete!("\n") - csv << [name, count, organization['name'], description, limit, environment, contentview, - hostcollections, subscriptions] end + subscriptions.delete!("\n") + csv << [name, count, organization['name'], description, limit, environment, contentview, + hostcollections, servicelevel, releasever, autoattach, subscriptions] end end end @@ -111,6 +91,9 @@ def create_activationkeys_from_csv(line) 'unlimited_content_hosts' => (line[LIMIT] == 'Unlimited') ? true : false, 'max_content_hosts' => (line[LIMIT] == 'Unlimited') ? nil : line[LIMIT].to_i } + params['auto_attach'] = (line[AUTOATTACH] == 'Yes' ? true : false) if params['auto_attach'] + params['service_level'] = line[SERVICELEVEL].nil? || line[SERVICELEVEL].empty? ? nil : line[SERVICELEVEL] + params['release_version'] = line[RELEASEVER].nil? || line[RELEASEVER].empty? ? nil : line[RELEASEVER] if !@existing[line[ORGANIZATION]].include? name print _("Creating activation key '%{name}'...") % {:name => name} if option_verbose? activationkey = @api.resource(:activation_keys).call(:create, params) diff --git a/lib/hammer_cli_csv/base.rb b/lib/hammer_cli_csv/base.rb index 9436c39..9dbea6c 100644 --- a/lib/hammer_cli_csv/base.rb +++ b/lib/hammer_cli_csv/base.rb @@ -748,7 +748,7 @@ def katello_subscription(organization, options = {}) results = @api.resource(:subscriptions).call(:index, { :per_page => 999999, 'organization_id' => foreman_organization(:name => organization), - 'search' => "name:\"#{options[:name]}\"" + 'search' => "name = \"#{options[:name]}\"" }) raise "No subscriptions match '#{options[:name]}'" if results['subtotal'] == 0 raise "Too many subscriptions match '#{options[:name]}'" if results['subtotal'] > 1 diff --git a/lib/hammer_cli_csv/compute_resources.rb b/lib/hammer_cli_csv/compute_resources.rb index 82210ea..b98e326 100644 --- a/lib/hammer_cli_csv/compute_resources.rb +++ b/lib/hammer_cli_csv/compute_resources.rb @@ -45,23 +45,21 @@ def import def create_compute_resources_from_csv(line) count(line[COUNT]).times do |number| name = namify(line[NAME], number) + params = { + 'compute_resource' => { + 'name' => name, + 'url' => line[URL], + 'provider' => line[PROVIDER] + } + } if !@existing.include? name print "Creating compute resource '#{name}'..." if option_verbose? - id = @api.resource(:compute_resources).call(:create, { - 'compute_resource' => { - 'name' => name, - 'url' => line[URL] - } - })['id'] + id = @api.resource(:compute_resources).call(:create, params)['id'] else print "Updating compute resource '#{name}'..." if option_verbose? - id = @api.resource(:compute_resources).call(:update, { - 'id' => @existing[name], - 'compute_resource' => { - 'name' => name, - 'url' => line[URL] - } - })['id'] + id = @existing[name] + params['id'] = id + @api.resource(:compute_resources).call(:update, params) end # Update associated resources diff --git a/lib/hammer_cli_csv/content_hosts.rb b/lib/hammer_cli_csv/content_hosts.rb index 1f1ca14..b5cb526 100644 --- a/lib/hammer_cli_csv/content_hosts.rb +++ b/lib/hammer_cli_csv/content_hosts.rb @@ -165,9 +165,11 @@ def import_locally if !@hypervisor_guests.empty? print(_('Updating hypervisor and guest associations...')) if option_verbose? @hypervisor_guests.each do |host_id, guest_ids| - @api.resource(:systems).call(:update, { + @api.resource(:hosts).call(:update, { 'id' => host_id, - 'guest_ids' => guest_ids + 'host' => { + 'guest_ids' => guest_ids + } }) end puts _('done') if option_verbose? @@ -178,21 +180,25 @@ def create_content_hosts_from_csv(line) return if option_organization && line[ORGANIZATION] != option_organization if !@existing[line[ORGANIZATION]] - @existing[line[ORGANIZATION]] = {} + @existing[line[ORGANIZATION]] = true # Fetching all content hosts is too slow and times out due to the complexity of the data # rendered in the json. # http://projects.theforeman.org/issues/6307 - total = @api.resource(:systems).call(:index, { + total = @api.resource(:hosts).call(:index, { 'organization_id' => foreman_organization(:name => line[ORGANIZATION]), 'per_page' => 1 })['total'].to_i (total / 20 + 2).to_i.times do |page| - @api.resource(:systems).call(:index, { + @api.resource(:hosts).call(:index, { 'organization_id' => foreman_organization(:name => line[ORGANIZATION]), 'page' => page + 1, 'per_page' => 20 })['results'].each do |host| - @existing[line[ORGANIZATION]][host['name']] = host['uuid'] if host + @existing[host['name']] = { + :host => host['id'], + :subscription => host['subscription']['id'], + :content => host['content']['id'] + } end end end @@ -200,42 +206,52 @@ def create_content_hosts_from_csv(line) count(line[COUNT]).times do |number| name = namify(line[NAME], number) - if !@existing[line[ORGANIZATION]].include? name + if !@existing.include? name print(_("Creating content host '%{name}'...") % {:name => name}) if option_verbose? - host_id = @api.resource(:systems).call(:create, { + host_id = @api.resource(:host_subscriptions).call(:register, { 'name' => name, 'organization_id' => foreman_organization(:name => line[ORGANIZATION]), - 'environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]), + 'lifecycle_environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]), 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW]), 'facts' => facts(name, line), 'installed_products' => products(line), 'service_level' => line[SLA], 'type' => 'system' - })['uuid'] - @existing[line[ORGANIZATION]][name] = host_id + })['id'] + @existing[name] = host_id else + # TODO: remove passing facet IDs to update + # Bug #13849 - updating a host's facet should not require the facet id to be included in facet params + # http://projects.theforeman.org/issues/13849 print(_("Updating content host '%{name}'...") % {:name => name}) if option_verbose? - host_id = @api.resource(:systems).call(:update, { - 'id' => @existing[line[ORGANIZATION]][name], - 'system' => { + host_id = @api.resource(:hosts).call(:update, { + 'id' => @existing[name][:host], + 'host' => { 'name' => name, - 'environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]), - 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW]), - 'facts' => facts(name, line), - 'installed_products' => products(line) - }, - 'facts' => facts(name, line), - 'installed_products' => products(line), # TODO: http://projects.theforeman.org/issues/9191, - 'service_level' => line[SLA] - })['uuid'] + 'content_facet_attributes' => { + 'id' => @existing[name][:content], + 'lifecycle_environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]), + 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW]) + }, + 'subscription_facet_attributes' => { + 'id' => @existing[name][:subscription], + 'facts' => facts(name, line), + # TODO: PUT /hosts subscription_facet_attributes missing "installed_products" + # http://projects.theforeman.org/issues/13854 + #'installed_products' => products(line), + 'service_level' => line[SLA] + } + } + })['host_id'] end if line[VIRTUAL] == 'Yes' && line[HOST] - raise "Content host '#{line[HOST]}' not found" if !@existing[line[ORGANIZATION]][line[HOST]] - @hypervisor_guests[@existing[line[ORGANIZATION]][line[HOST]]] ||= [] - @hypervisor_guests[@existing[line[ORGANIZATION]][line[HOST]]] << "#{line[ORGANIZATION]}/#{name}" + raise "Content host '#{line[HOST]}' not found" if !@existing[line[HOST]] + @hypervisor_guests[@existing[line[HOST]]] ||= [] + @hypervisor_guests[@existing[line[HOST]]] << @existing[name] end + update_host_facts(host_id, line) update_host_collections(host_id, line) update_subscriptions(host_id, line) @@ -262,12 +278,15 @@ def facts(name, line) facts end + def update_host_facts(host_id, line) + end + def update_host_collections(host_id, line) return nil if !line[HOSTCOLLECTIONS] CSV.parse_line(line[HOSTCOLLECTIONS]).each do |hostcollection_name| - @api.resource(:host_collections).call(:add_systems, { + @api.resource(:host_collections).call(:add_hosts, { 'id' => katello_hostcollection(line[ORGANIZATION], :name => hostcollection_name), - 'system_ids' => [host_id] + 'hosts_ids' => [host_id] }) end end @@ -299,29 +318,16 @@ def products(line) end def update_subscriptions(host_id, line) - existing_subscriptions = @api.resource(:systems).call(:subscriptions, { - 'per_page' => 999999, - 'id' => host_id + existing_subscriptions = @api.resource(:host_subscriptions).call(:index, { + 'host_id' => host_id })['results'] - if existing_subscriptions.length > 0 - @api.resource(:subscriptions).call(:destroy, { - 'system_id' => host_id, - 'id' => existing_subscriptions[0]['id'] + if existing_subscriptions.length != 0 + @api.resource(:host_subscriptions).call(:remove_subscriptions, { + 'host_id' => host_id, + 'subscriptions' => existing_subscriptions }) end - # existing_subscriptions = @api.resource(:subscriptions).call(:index, { - # 'organization_id' => foreman_organization(:name => line[ORGANIZATION]), - # 'per_page' => 999999, - # 'system_id' => host_id - # })['results'] - # if existing_subscriptions.length > 0 - # @api.resource(:subscriptions).call(:destroy, { - # 'system_id' => host_id, - # 'id' => existing_subscriptions[0]['id'] - # }) - # end - return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty? subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details| @@ -332,8 +338,8 @@ def update_subscriptions(host_id, line) } end - @api.resource(:subscriptions).call(:create, { - 'system_id' => host_id, + @api.resource(:host_subscriptions).call(:add_subscriptions, { + 'host_id' => host_id, 'subscriptions' => subscriptions }) end diff --git a/lib/hammer_cli_csv/content_views.rb b/lib/hammer_cli_csv/content_views.rb index bf98517..d373dfa 100644 --- a/lib/hammer_cli_csv/content_views.rb +++ b/lib/hammer_cli_csv/content_views.rb @@ -106,32 +106,26 @@ def create_contentviews_from_csv(line) 'description' => line[DESCRIPTION], 'composite' => is_composite } - if is_composite - options['component_ids'] = composite_ids - else - options['repository_ids'] = repository_ids - end contentview_id = @api.resource(:content_views).call(:create, options)['id'] @existing_contentviews[line[ORGANIZATION]][name] = contentview_id - publish = true else print _("Updating content view '%{name}'...") % {:name => name} if option_verbose? - options = { - 'id' => contentview_id, - 'description' => line[DESCRIPTION] - } - if is_composite - options['component_ids'] = composite_ids - else - options['repository_ids'] = repository_ids - end - contentview = @api.resource(:content_views).call(:update, options) - contentview_id = contentview['id'] - publish = contentview['versions'].empty? end + options = { + 'id' => contentview_id, + 'description' => line[DESCRIPTION] + } + if is_composite + options['component_ids'] = composite_ids + else + options['repository_ids'] = repository_ids + end + contentview = @api.resource(:content_views).call(:update, options) + contentview_id = contentview['id'] + # Content views cannot be used in composites unless a publish has occurred - publish_content_view(contentview_id, line) if publish + publish_content_view(contentview_id, line) if contentview['versions'].empty? promote_content_view(contentview_id, line) puts _('done') if option_verbose? diff --git a/lib/hammer_cli_csv/hosts.rb b/lib/hammer_cli_csv/hosts.rb index 3623ec5..91b9945 100644 --- a/lib/hammer_cli_csv/hosts.rb +++ b/lib/hammer_cli_csv/hosts.rb @@ -12,10 +12,21 @@ class HostsCommand < BaseCommand MACADDRESS = 'MAC Address' DOMAIN = 'Domain' PARTITIONTABLE = 'Partition Table' + SUBNET = 'Subnet' + REALM = 'Realm' + MEDIUM = 'Medium' + HOSTGROUP = 'Hostgroup' + COMPUTERESOURCE = 'Compute Resource' + COMPUTEPROFILE = 'Compute Profile' + IMAGE = 'Image' + ENABLED = 'Enabled' + MANAGED = 'Managed' def export CSV.open(option_file || '/dev/stdout', 'wb', {:force_quotes => true}) do |csv| - csv << [NAME, ORGANIZATION, LOCATION, ENVIRONMENT, OPERATINGSYSTEM, ARCHITECTURE, MACADDRESS, DOMAIN, PARTITIONTABLE] + csv << [NAME, ORGANIZATION, LOCATION, ENVIRONMENT, OPERATINGSYSTEM, ARCHITECTURE, + MACADDRESS, DOMAIN, PARTITIONTABLE, SUBNET, REALM, MEDIUM, HOSTGROUP, + COMPUTERESOURCE, COMPUTEPROFILE, IMAGE, ENABLED, MANAGED] search_options = {:per_page => 999999} search_options['search'] = "organization=\"#{option_organization}\"" if option_organization @api.resource(:hosts).call(:index, search_options)['results'].each do |host| @@ -31,8 +42,20 @@ def export mac = host['mac'] domain = host['domain_name'] ptable = host['ptable_name'] + subnet = host['subnet_name'] + realm = host['realm_name'] + medium = host['medium_name'] + hostgroup = host['hostgroup_name'] + compute_resource = host['compute_resource_name'] + compute_profile = host['compute_profile_name'] + image = host['image_name'] - csv << [name, organization, location, environment, operatingsystem, architecture, mac, domain, ptable] + enabled = host['enabled'] ? 'Yes' : 'No' + managed = host['managed'] ? 'Yes' : 'No' + + csv << [name, organization, location, environment, operatingsystem, architecture, + mac, domain, ptable, subnet, realm, medium, hostgroup, compute_resource, + compute_profile, image, enabled, managed] end end end diff --git a/lib/hammer_cli_csv/products.rb b/lib/hammer_cli_csv/products.rb index 1024cd4..19e933b 100644 --- a/lib/hammer_cli_csv/products.rb +++ b/lib/hammer_cli_csv/products.rb @@ -14,7 +14,8 @@ class ProductsCommand < BaseCommand def export CSV.open(option_file || '/dev/stdout', 'wb', {:force_quotes => false}) do |csv| - csv << [NAME, LABEL, ORGANIZATION, REPOSITORY, REPOSITORY_TYPE, REPOSITORY_URL] + csv << [NAME, LABEL, ORGANIZATION, DESCRIPTION, REPOSITORY, REPOSITORY_TYPE, + REPOSITORY_URL] @api.resource(:organizations).call(:index, { :per_page => 999999 })['results'].each do |organization| @@ -32,7 +33,8 @@ def export repository_type = repository['product_type'] == 'custom' ? 'Custom' : 'Red Hat' repository_type += " #{repository['content_type'].capitalize}" csv << [product['name'], product['label'], organization['name'], - repository['name'], repository_type, repository['url']] + product['description'], repository['name'], repository_type, + repository['url']] end end end @@ -77,6 +79,12 @@ def create_products_from_csv(line) count(line[COUNT]).times do |number| name = namify(line[NAME], number) + params = { + :name => name, + 'organization_id' => foreman_organization(:name => line[ORGANIZATION]) + } + params[:description] = line[DESCRIPTION] if !line[DESCRIPTION].nil? && + !line[DESCRIPTION].empty? product_id = @existing_products[line[ORGANIZATION]][name] if product_id.nil? print _("Creating product '%{name}'...") % {:name => name} if option_verbose? @@ -85,14 +93,12 @@ def create_products_from_csv(line) {:name => name, :organization => line[ORGANIZATION]} end - product_id = @api.resource(:products).call(:create, { - 'organization_id' => foreman_organization(:name => line[ORGANIZATION]), - 'name' => name - })['id'] + product_id = @api.resource(:products).call(:create, params)['id'] @existing_products[line[ORGANIZATION]][name] = product_id else - # Nothing to update for products print _("Updating product '%{name}'...") % {:name => name} if option_verbose? + params[:id] = product_id + @api.resource(:products).call(:update, params) end @existing_repositories[line[ORGANIZATION] + name] = {} diff --git a/test/data/settings.csv b/test/data/settings.csv index fff92ff..a17ea30 100644 --- a/test/data/settings.csv +++ b/test/data/settings.csv @@ -1,5 +1,2 @@ Name,Count,Value -administrator,1,root@example.com idle_timeout,1,60000 -remote_execution_global_proxy,1,true -require_ssl_smart_proxies,1,false \ No newline at end of file diff --git a/test/settings_test.rb b/test/settings_test.rb new file mode 100644 index 0000000..e91fd50 --- /dev/null +++ b/test/settings_test.rb @@ -0,0 +1,30 @@ +require File.join(File.dirname(__FILE__), 'csv_test_helper') + +describe 'job-templates' do + + extend CommandTestHelper + + context "import" do + it "update settings w/ Count column" do + set_user 'admin' + + name = "settings#{rand(10000)}" + + file = Tempfile.new('settings_test') + # rubocop:disable LineLength + file.write <<-FILE +Name,Count,Value +idle_timeout,1,60000 +FILE + file.rewind + + stdout,stderr = capture { + hammer.run(%W{csv settings --verbose --file #{file.path}}) + } + stderr.must_equal '' + lines = stdout.split("\n") + lines[0].must_equal "Updating setting 'idle_timeout'...done" + file.unlink + end + end +end