foreman_content
Advanced tools
| $(function () { | ||
| // Disable repository sync input based on checkbox | ||
| $('#content_repository_schedule').click(function () { | ||
| $('#sync_schedule_form select').attr('disabled', !$(this).is(':checked')); | ||
| }); | ||
| // try to guess architecture based on the URL | ||
| $('#content_repository_feed').focusout(function() { | ||
| url = $(this).val(); | ||
| if (url == undefined || url == '') | ||
| return; | ||
| $('#content_repository_architecture_id option').each (function ( index, option) { | ||
| if (url.match(option.text)) { | ||
| $('#content_repository_architecture_id').val(option.value); | ||
| return false; | ||
| } | ||
| }) | ||
| }); | ||
| }); |
| module Content::RepositoryCommon | ||
| extend ActiveSupport::Concern | ||
| REPO_PREFIX = '/pulp/repos/' | ||
| def full_path | ||
| pulp_url = URI.parse(Setting.pulp_url) | ||
| scheme = (unprotected ? 'http' : 'https') | ||
| port = (pulp_url.port == 443 || pulp_url.port == 80 ? "" : ":#{pulp_url.port}") | ||
| "#{scheme}://#{pulp_url.host}#{port}#{REPO_PREFIX}#{relative_path}" | ||
| end | ||
| end |
| <%= wizard_header 2, _("Select source type"), _("Select content"), _("Create") %> | ||
| <%= form_for @factory, :url => hash_for_new_content_view_path, :class => 'form-horizontal well', :method => :get do |f| %> | ||
| <%= f.hidden_field :originator_id, :value => @hostgroup.id %> | ||
| <%= f.hidden_field :originator_type, :value => @hostgroup.class.name %> | ||
| <%= select_f f, :parent_cv, @hostgroup.parent.try(:content_views) || [], "id", "name", | ||
| :label => _("Parent Content view") %> | ||
| <h6>Products</h6> | ||
| <% @hostgroup.products.each do |product| %> | ||
| <%= select_f f, :product_cv, product.content_views, "id", "name", {}, :label => product.name %> | ||
| <% end %> | ||
| <h6>Operating System</h6> | ||
| <%= select_f f, :os_cv, @hostgroup.os.content_views, "id", "name" %> | ||
| <%= next_or_cancel hash_for_new_content_view_path() %> | ||
| <% end %> |
| <% title @content_view.to_label %> | ||
| <%= title_actions(button_group( | ||
| link_to_if_authorized(_("Edit"), hash_for_edit_content_view_path(@content_view)), | ||
| display_delete_if_authorized(hash_for_content_view_path(@content_view), :class => 'btn-danger', :confirm => "Delete #{@content_view.name}?") | ||
| )) %> | ||
| <h3><%= _('Repositories') %></h3> | ||
| <table class="table table-bordered table-striped"> | ||
| <tr> | ||
| <th><%= sort :name, :as => s_("Name") %></th> | ||
| <th><%= _("State") %></th> | ||
| <th><%= _("Last sync status") %></th> | ||
| <th><%= _("Content type") %></th> | ||
| </tr> | ||
| <% @content_view.repository_clones.each do |repository| %> | ||
| <tr> | ||
| <%= render :partial => 'content/repositories/repository', :locals => { :repository => repository } %> | ||
| </tr> | ||
| <% end %> | ||
| </table> | ||
| <%= text_f f, :name %> | ||
| <%= text_f f, :feed, :class => 'span6' %> | ||
| <%= selectable_f f, :content_type, @repository.content_types %> | ||
| <%= select_f f, :architecture_id, ::Architecture.all, :id, :name, { :include_blank => N_("noarch")} %> | ||
| <%= checkbox_f f, :enabled %> | ||
| <%= checkbox_f f, :publish, { :disabled => !f.object.new_record?, | ||
| :help_inline => _('should this repository be directly accessible or as a feed for content views?') } %> | ||
| <%= select_f f, :gpg_key_id, Content::GpgKey.all, :id, :name, {:include_blank => true}, {:label => _("GPG Key")} %> | ||
| <%= f.hidden_field :type if f.object.new_record? %> |
| <td><%= link_to_if_authorized(h(repository.name), hash_for_repository_path(repository)) %></td> | ||
| <td><%= repository.try(:state) %></td> | ||
| <td><%= last_time(repository.kind_of?(Content::Repository) ? repository.last_sync : repository.last_published) %></td> | ||
| <td><%= repository.content_type %></td> |
| <%= checkbox_f f, :schedule, :label => _('Schedule Sync'), | ||
| :help_inline => _('Sync this repository periodically') %> | ||
| <div class='control-group'> | ||
| <div class='controls'> | ||
| <%= sync_schedule_selector(f) %> | ||
| </div> | ||
| </div> | ||
| class ChangeContentViewIndexName < ActiveRecord::Migration | ||
| OLD_NAME = :index_content_content_views_on_originator_id_and_originator_type | ||
| def up | ||
| rename_index :content_content_views, OLD_NAME, :content_view_id_type_index if index_name_exists? :content_content_views, OLD_NAME, nil | ||
| end | ||
| def down | ||
| rename_index :content_content_views, :content_view_id_type_index, OLD_NAME | ||
| end | ||
| end |
| class AddPublishBooleanToContentRepository < ActiveRecord::Migration | ||
| def change | ||
| add_column :content_repositories, :publish, :boolean | ||
| add_index :content_repositories, :publish, :name => :content_repo_publish_index | ||
| end | ||
| end |
| class ChangeRepositoryToPolymorphic < ActiveRecord::Migration | ||
| def up | ||
| add_column :content_repositories, :originator_type, :string | ||
| add_column :content_repositories, :originator_id, :integer | ||
| Content::Repository.reset_column_information | ||
| Content::Repository.all.each do |repo| | ||
| if repo.operatingsystem_id | ||
| repo.originator_type = 'Operatingsystem' | ||
| repo.originator_id = repo.operatingsystem_id | ||
| elsif repo.product_id | ||
| repo.originator_type = 'Content::Product' | ||
| repo.originator_id = repo.product_id | ||
| else | ||
| raise "invalid repo type, can't continue: #{repo.inspect}" | ||
| end | ||
| repo.save(:validate => false) | ||
| end | ||
| remove_column :content_repositories, :operatingsystem_id | ||
| remove_column :content_repositories, :product_id | ||
| end | ||
| def down | ||
| add_column :content_repositories, :product_id, :integer | ||
| add_column :content_repositories, :operatingsystem_id, :integer | ||
| Content::Repository.reset_column_information | ||
| Content::Repository.all.each do |repo| | ||
| if repo.originator_type && repo.originator_id | ||
| case repo.originator_type | ||
| when 'Operatingsystem' | ||
| repo.operatingsystem_id = repo.originator_id | ||
| when 'Content::Product' | ||
| repo.product_id = repo.originator_id | ||
| end | ||
| repo.save(:validate => false) | ||
| end | ||
| end | ||
| remove_column :content_repositories, :originator_type | ||
| remove_column :content_repositories, :originator_id | ||
| end | ||
| end |
| class AddPolymorphicRepoToContentViewRepositoryClone < ActiveRecord::Migration | ||
| def change | ||
| add_column :content_content_view_repository_clones, :repository_type, :string | ||
| Content::ContentViewRepositoryClone.reset_column_information | ||
| Content::ContentViewRepositoryClone.update_all(:repository_type => 'Content::RepositoryClone') | ||
| rename_column :content_content_view_repository_clones, :repository_clone_id, :repository_id | ||
| end | ||
| end |
@@ -1,12 +0,23 @@ | ||
| // This is a manifest file that'll be compiled into application.js, which will include all the files | ||
| // listed below. | ||
| // | ||
| // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, | ||
| // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. | ||
| // | ||
| // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the | ||
| // the compiled file. | ||
| // | ||
| // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD | ||
| // GO AFTER THE REQUIRES BELOW. | ||
| // | ||
| $(function () { | ||
| // Disable repository selection input based on checkbox | ||
| $('.repository_selection input:checkbox').click(function () { | ||
| var checked = $(this).is(':checked'); | ||
| $(this).parent().find($(':input')).each (function ( index, input) { | ||
| $(input).attr('disabled', !checked); | ||
| }); | ||
| }); | ||
| // toggle repository ID's to send based on the repo type | ||
| $('.cv-repo-selection').change(function () { | ||
| enable_selected_repository(this); | ||
| }); | ||
| $('.cv-repo-selection').change(); | ||
| }); | ||
| function enable_selected_repository(element) { | ||
| var select = $(element); | ||
| var id = select.attr('id').replace(/[^\d]/g, ''); | ||
| var clone = select.val() == 'clone'; | ||
| $('#clone-' + id).attr('disabled', !clone); | ||
| $('#latest-' + id).attr('disabled', clone); | ||
| }; |
@@ -8,12 +8,25 @@ module Content | ||
| @content_views = ContentView.search_for(params[:search], :order => params[:order]). | ||
| paginate(:page => params[:page]) | ||
| @counter = RepositoryClone.joins(:content_view_repository_clones).group(:content_view_id).count | ||
| paginate(:page => params[:page]) | ||
| @counter = ContentViewRepositoryClone.where(:content_view_id => @content_views.map(&:id)).group(:content_view_id).count | ||
| end | ||
| def show | ||
| end | ||
| def new | ||
| @hostgroup = Hostgroup.find_by_id(params[:hostgroup]) if params[:hostgroup] | ||
| @content_view = ContentViewFactory.create_product_content_view(params[:product]) if params[:product] | ||
| @content_view ||= ContentViewFactory.create_os_content_view(params[:operatingsystem]) if params[:operatingsystem] | ||
| @content_view ||= ContentViewFactory.create_composite_content_view(params[:content_content_view_factory]) if params[:content_content_view_factory] | ||
| options = if params[:product] | ||
| {:originator_id => params[:product], :originator_type => 'Content::Product'} | ||
| elsif params[:operatingsystem] | ||
| {:originator_id => params[:operatingsystem], :originator_type => 'Operatingsystem'} | ||
| elsif params[:hostgroup] | ||
| @hostgroup = Hostgroup.find_by_id(params[:hostgroup]) | ||
| {:originator_id => params[:hostgroup], :originator_type => 'Hostgroup'} | ||
| elsif params[:content_content_view_factory] | ||
| params[:content_content_view_factory] | ||
| elsif params[:type].blank? | ||
| process_error(:error_msg => _('Must provide a type')) | ||
| end | ||
| @factory = ContentViewFactory.new(options || {}) | ||
| @content_view = @factory.try(:content_view) if @factory.originator_id | ||
| end | ||
@@ -20,0 +33,0 @@ |
@@ -9,3 +9,3 @@ module Content | ||
| paginate(:page => params[:page]) | ||
| @counter = Repository::Product.group(:product_id).where(:product_id => @products.map(&:id)).count | ||
| @counter = Repository::Product.group(:originator_id).where(:originator_id => @products.map(&:id)).count | ||
| end | ||
@@ -12,0 +12,0 @@ |
| module Content | ||
| class RepositoriesController < ::ApplicationController | ||
| include Foreman::Controller::AutoCompleteSearch | ||
| before_filter :find_by_name, :only => %w{show edit update destroy sync} | ||
| before_filter :find_repo, :only => %w{show edit update destroy sync} | ||
| def index | ||
| @repositories = Repository.search_for(params[:search], :order => params[:order]). | ||
| paginate(:page => params[:page]).includes(:product, :operatingsystem) | ||
| paginate(:page => params[:page]) | ||
| end | ||
@@ -16,3 +16,3 @@ | ||
| when "product" | ||
| Repository::Product.new(:product_id => params[:product_id]) | ||
| Repository::Product.new(:originator_id => params[:product_id]) | ||
| else | ||
@@ -60,3 +60,9 @@ not_found | ||
| end | ||
| private | ||
| def find_repo | ||
| @repository = Content::Repository.find(params[:id]) | ||
| end | ||
| end | ||
| end |
| module Content | ||
| module ContentViewsHelper | ||
| def repositories(view) | ||
| if view.new_record? | ||
| view.source_repositories + view.repository_clones | ||
| def repositories | ||
| cv_repos = @content_view.repository_clones + @content_view.repository_sources | ||
| if @content_view.new_record? && cv_repos.empty? # new form | ||
| @factory.repositories | ||
| else | ||
| view.repository_clones | ||
| cv_repos # edit or new after validation failure | ||
| end | ||
@@ -20,3 +22,21 @@ end | ||
| def step? | ||
| if params[:type] | ||
| 'step1' | ||
| elsif @hostgroup | ||
| 'composite' | ||
| else | ||
| 'form' | ||
| end | ||
| end | ||
| def options_for_type_selection repo | ||
| options = [[_('Clone (snapshot)'), 'clone']] | ||
| options << [_('Latest'),'latest'] if repo.kind_of?(Content::Repository) && repo.publish? | ||
| selected = 'clone' if @factory # default | ||
| selected ||= @content_view.repository_clone_ids.include?(repo.id) ? 'clone' : 'latest' # on validation error | ||
| options_for_select(options, selected) | ||
| end | ||
| end | ||
| end |
@@ -9,3 +9,31 @@ module Content | ||
| def sync_schedule schedules | ||
| return _('None') if schedules.empty? | ||
| schedules.map{|s| s[:schedule]}.join(', ') | ||
| rescue => e | ||
| logger.warn _("Failed to fetch sync schedule: ") + e.to_s | ||
| _('unknown') | ||
| end | ||
| def time_selector f | ||
| html_options = { :class => 'span1', :disabled => f.object.schedule.blank? } | ||
| f.select(:hour, options_for_time(0..23), {}, html_options) + | ||
| f.select(:minute, options_for_time(0..59), {}, html_options) | ||
| end | ||
| def sync_schedule_selector f | ||
| content_tag :div, :id => 'sync_schedule_form', :class => 'input-prepend input-append' do | ||
| f.select(:interval, [%w(Daily D), %w(Weekly W)], {}, { :disabled => f.object.schedule.blank? , :class => 'span1' }) + | ||
| content_tag(:span, :class => 'add-on') { '@' } + | ||
| time_selector(f) | ||
| end | ||
| end | ||
| private | ||
| def options_for_time(options) | ||
| options.map { |i| [sprintf("%02d", i), i] } | ||
| end | ||
| end | ||
| end |
@@ -41,3 +41,3 @@ module Content::HostExtensions | ||
| where(:content_available_content_views => {:environment_id => environment_id}). | ||
| where(:originator_type => 'Hostgroup', :originator_id => hostgroup_id).pluck(:id) | ||
| where(:originator_type => 'Hostgroup', :originator_id => hostgroup_id).pluck('content_content_views.id') | ||
| end | ||
@@ -44,0 +44,0 @@ |
@@ -5,3 +5,3 @@ module Content::OperatingsystemExtensions | ||
| included do | ||
| has_many :repositories, :class_name => 'Content::Repository' | ||
| has_many :repositories, :class_name => 'Content::Repository', :as => :originator | ||
@@ -8,0 +8,0 @@ has_many :available_content_views, :dependent => :destroy, :class_name => 'Content::AvailableContentView' |
@@ -6,3 +6,3 @@ module Content::Orchestration::Pulp | ||
| included do | ||
| delegate :sync_status, :sync, :counters, :sync_history, :state, :to => :repo | ||
| delegate :sync_status, :sync, :sync_schedule, :counters, :sync_history, :state, :to => :repo | ||
| end | ||
@@ -9,0 +9,0 @@ |
@@ -8,2 +8,5 @@ module Content::Orchestration::Pulp::Sync | ||
| before_destroy :queue_pulp_destroy unless Rails.env.test? | ||
| attr_accessor :interval, :hour, :minute, :schedule | ||
| validates :interval, :inclusion => { :in => %w(D W) }, :if => :schedule_sync? | ||
| validates :hour, :minute, :numericality => true, :if => :schedule_sync? | ||
| end | ||
@@ -26,4 +29,10 @@ | ||
| :action => [self, :set_pulp_repo]) | ||
| logger.debug "Scheduling new Pulp Repository Sync" | ||
| queue.create(:name => _("Sync Pulp Repository %s") % self, :priority => 20, | ||
| :action => [self, :set_sync_pulp_repo]) | ||
| if schedule_sync? | ||
| logger.debug "Scheduling Pulp Repository sync scheduler" | ||
| queue.create(:name => _("Sync Schedule Pulp Repository %s") % self, :priority => 30, | ||
| :action => [self, :set_sync_schedule_pulp_repo]) | ||
| end | ||
| end | ||
@@ -43,3 +52,3 @@ | ||
| def set_pulp_repo | ||
| repo.create | ||
| publish ? repo.create_with_distributor : repo.create | ||
| end | ||
@@ -59,2 +68,10 @@ | ||
| def set_sync_schedule_pulp_repo | ||
| repo.sync_schedule = sync_schedule_time | ||
| end | ||
| def del_sync_schedule_pulp_repo | ||
| repo.sync_schedule = nil | ||
| end | ||
| def repo_options | ||
@@ -68,2 +85,4 @@ { | ||
| :protected => unprotected, | ||
| :relative_path => relative_path, | ||
| :auto_publish => publish | ||
| } | ||
@@ -75,2 +94,18 @@ end | ||
| end | ||
| end | ||
| def relative_path | ||
| "#{entity_name}/#{name}" | ||
| end | ||
| def schedule_sync? | ||
| schedule.present? && schedule != '0' | ||
| end | ||
| def sync_schedule_time | ||
| return if schedule.blank? | ||
| time = Time.parse("00:#{hour}:#{minute}").iso8601 | ||
| repetition = "R1" # no limit of hour many syncs | ||
| "#{repetition}/#{time}/P1#{interval}" | ||
| end | ||
| end |
@@ -9,3 +9,3 @@ module Content::RedhatExtensions | ||
| def medium_uri_with_content_uri host, url = nil | ||
| if url.nil? && (full_path = Content::Repository.available_for_host(host).kickstart.first.try(:full_path)) | ||
| if url.nil? && (full_path = kickstart_repo(host).try(:full_path)) | ||
| URI.parse(full_path) | ||
@@ -20,5 +20,12 @@ else | ||
| def repos host | ||
| host.attached_repositories.yum.map { |repo| format_repo(repo) } | ||
| yum_repos(host).map { |repo| format_repo(repo) } | ||
| end | ||
| def boot_files_uri(medium, architecture) | ||
| Redhat::PXEFILES.values.collect do |img| | ||
| medium_vars_to_uri("#{medium.path}/#{pxedir}/#{img}", architecture.name, self) | ||
| URI.parse(interpolate_medium_vars(url, arch, os)).normalize | ||
| end | ||
| end | ||
| private | ||
@@ -30,3 +37,3 @@ # convert a repository to a format that kickstart script can consume | ||
| :name => repo.to_label, | ||
| :description => repo.product.description, | ||
| :description => repo.description, | ||
| :enabled => repo.enabled, | ||
@@ -36,2 +43,11 @@ :gpgcheck => !!repo.gpg_key | ||
| end | ||
| def kickstart_repo host | ||
| host.attached_repositories.detect{|r| r.content_type == Content::Repository::KICKSTART_TYPE} | ||
| end | ||
| def yum_repos host | ||
| host.attached_repositories.select{|r| r.content_type == Content::Repository::YUM_TYPE} | ||
| end | ||
| end |
| class Content::ContentViewRepositoryClone < ActiveRecord::Base | ||
| belongs_to :content_view | ||
| belongs_to :repository_clone | ||
| validate :content_view_id, :repository_clone_id, :presence => true | ||
| belongs_to :repository, :polymorphic => true | ||
| validate :content_view_id, :repository_id, :repository_type, :presence => true | ||
| end |
| module Content | ||
| class ContentView < ActiveRecord::Base | ||
| has_ancestry :orphan_strategy => :rootify | ||
| attr_accessor :repository_ids_to_clone | ||
@@ -13,6 +14,11 @@ belongs_to :originator, :polymorphic => true | ||
| before_destroy :clean_unused_clone_repos | ||
| has_many :content_view_repository_clones, :dependent => :destroy | ||
| has_many :repository_clones, :through => :content_view_repository_clones, :class_name => 'Content::RepositoryClone' | ||
| has_many :repositories, :through => :repository_clones | ||
| has_many :repository_clones, :through => :content_view_repository_clones, | ||
| :source => :repository, :source_type => 'Content::RepositoryClone' | ||
| has_many :repository_sources, :through => :content_view_repository_clones, | ||
| :source => :repository, :source_type => 'Content::Repository' | ||
| scope :hostgroups, where(:originator_type => 'Hostgroup') | ||
@@ -22,6 +28,6 @@ scope :products, where(:originator_type => 'Content::Product') | ||
| after_save :clone_repos | ||
| before_destroy :clean_unused_clone_repos | ||
| after_create :clone_repos | ||
| validates_presence_of :name | ||
| validates_uniqueness_of :name, :scope => [:originator_id, :originator_type] | ||
@@ -43,4 +49,2 @@ # special relationships needed for search with polymorphic associations | ||
| attr_accessor :source_repositories | ||
| def to_label | ||
@@ -50,18 +54,20 @@ name || "#{originator.to_label} - #{DateTime.now.strftime("%m/%d/%Y")}".parameterize | ||
| private | ||
| def clone_repos | ||
| return unless @source_repositories | ||
| Repository.where(:id => @source_repositories).each do |repository| | ||
| repository.publish self | ||
| return unless repository_ids_to_clone | ||
| Repository.where(:id => repository_ids_to_clone).each do |repository| | ||
| repository.clone self | ||
| end | ||
| end | ||
| private | ||
| def clean_unused_clone_repos | ||
| current_repos = Content::ContentViewRepositoryClone.where(:content_view_id => id).pluck(:repository_clone_id) | ||
| used_repos = Content::ContentViewRepositoryClone. | ||
| where(:repository_clone_id => current_repos). | ||
| where(['content_view_id IS NOT ?', id]).pluck(:repository_clone_id) | ||
| current_repos = Content::ContentViewRepositoryClone.where(:content_view_id => id, :repository_type => 'Content::RepositoryClone').pluck(:repository_id) | ||
| used_repos = Content::ContentViewRepositoryClone. | ||
| where(:repository_id => current_repos). | ||
| where(['content_view_id IS NOT ?', id]).pluck(:repository_id) | ||
| repos_to_delete = current_repos - used_repos | ||
| logger.debug('All Cloned repositories are used elsewhere, nothing to do') if repos_to_delete.empty? | ||
| logger.debug("Clone Repos IDS: #{repos_to_delete.join(', ')} are unused - deleting...") if repos_to_delete.any? | ||
| Content::RepositoryClone.destroy(repos_to_delete) if repos_to_delete.any? | ||
@@ -68,0 +74,0 @@ end |
@@ -5,3 +5,3 @@ module Content | ||
| has_many :repositories | ||
| has_many :repositories, :as => :originator | ||
| has_many :repository_clones, :through => :repositories | ||
@@ -8,0 +8,0 @@ has_many :content_views, :as => :originator |
| module Content | ||
| class RepositoryClone < ActiveRecord::Base | ||
| include Content::Orchestration::Pulp::Clone | ||
| include Content::RepositoryCommon | ||
| REPO_PREFIX = '/pulp/repos/' | ||
| belongs_to :repository | ||
| has_many :content_view_repository_clones, :dependent => :destroy | ||
| has_many :content_view_repository_clones, :dependent => :destroy, :as => :repository | ||
| has_many :content_views, :through => :content_view_repository_clones | ||
@@ -15,3 +14,3 @@ before_destroy EnsureNotUsedBy.new(:content_views) | ||
| delegate :content_type, :architecture, :unprotected, :gpg_key, :product, :to => :repository | ||
| delegate :content_type, :architecture, :unprotected, :gpg_key, :product, :operatingsystem, :enabled, :to => :repository | ||
@@ -23,9 +22,5 @@ scope :for_content_views, lambda { |ids| | ||
| def full_path | ||
| pulp_url = URI.parse(Setting.pulp_url) | ||
| scheme = (unprotected ? 'http' : 'https') | ||
| port = (pulp_url.port == 443 || pulp_url.port == 80 ? "" : ":#{pulp_url.port}") | ||
| "#{scheme}://#{pulp_url.host}#{port}#{REPO_PREFIX}#{relative_path}" | ||
| end | ||
| default_scope { includes(:repository) } | ||
| end | ||
| end |
@@ -5,2 +5,3 @@ module Content | ||
| include Foreman::STI | ||
| include Content::RepositoryCommon | ||
@@ -12,8 +13,9 @@ YUM_TYPE = 'yum' | ||
| belongs_to :product, :class_name => 'Content::Product' | ||
| belongs_to :operatingsystem | ||
| belongs_to :originator, :polymorphic => true | ||
| belongs_to :gpg_key | ||
| belongs_to :architecture | ||
| has_many :repository_clones | ||
| has_many :content_view_repository_clones, :dependent => :destroy, :as => :repository | ||
| before_validation :set_originator_type | ||
| before_destroy EnsureNotUsedBy.new(:repository_clones) | ||
@@ -23,4 +25,7 @@ | ||
| validates_presence_of :originator_id, :originator_type | ||
| validates :name, :presence => true | ||
| validates_uniqueness_of :name, :scope => [:operatingsystem_id, :product_id] | ||
| validates_uniqueness_of :name, :scope => [:originator_type, :originator_id] | ||
| validates :feed, :presence => true, :uniqueness => true | ||
| validates_inclusion_of :content_type, | ||
@@ -33,4 +38,10 @@ :in => TYPES, | ||
| scoped_search :in => :architecture, :on => :name, :rename => :architecture, :complete_value => :true | ||
| scoped_search :in => :operatingsystem, :on => :name, :rename => :os, :complete_value => :true | ||
| scoped_search :in => :product, :on => :name, :rename => :product, :complete_value => :true | ||
| belongs_to :search_operatingsystems, :class_name => 'Operatingsystem', :foreign_key => :originator_id, | ||
| :conditions => '"content_repositories"."originator_type" = "Operatingsystem"' | ||
| belongs_to :search_products, :class_name => 'Content::Product', :foreign_key => :originator_id, | ||
| :conditions => '"content_repositories"."originator_type" = "Content::Product"' | ||
| scoped_search :in => :search_products, :on => :name, :rename => :product, | ||
| :complete_value => :true, :only_explicit => true | ||
| scoped_search :in => :search_operatingsystems, :on => :name, :rename => :operatingsystem, | ||
| :complete_value => :true, :only_explicit => true | ||
@@ -40,2 +51,7 @@ scope :kickstart, where(:content_type => KICKSTART_TYPE) | ||
| scope :for_content_views, lambda { |ids| | ||
| joins(:content_view_repository_clones). | ||
| where('content_content_view_repository_clones' => {:content_view_id => ids}) | ||
| } | ||
| def content_types | ||
@@ -47,3 +63,3 @@ TYPES | ||
| def to_label | ||
| "#{entity_name}-#{name}".parameterize | ||
| "#{entity_name}/#{name}" | ||
| end | ||
@@ -56,7 +72,7 @@ | ||
| def publish content_view | ||
| def clone content_view | ||
| repository_clones.create!( | ||
| :content_views => [content_view], | ||
| :name => self.name + "_clone", | ||
| :relative_path => "content_views/#{to_label}/#{Foreman.uuid}" | ||
| :relative_path => "content_views/#{entity_name}/#{name}/#{Foreman.uuid}" | ||
| ) | ||
@@ -63,0 +79,0 @@ end |
| module Content | ||
| class Repository::OperatingSystem < Repository | ||
| alias_method :operatingsystem, :originator | ||
| validates_presence_of :operatingsystem | ||
| def self.model_name | ||
@@ -13,3 +12,13 @@ Repository.model_name | ||
| end | ||
| def description | ||
| entity_name | ||
| end | ||
| private | ||
| def set_originator_type | ||
| self.originator_type ||= 'Operatingsystem' | ||
| end | ||
| end | ||
| end |
| module Content | ||
| class Repository::Product < Repository | ||
| alias_method :product, :originator | ||
| has_many :operatingsystem_repositories, :foreign_key => :repository_id, :dependent => :destroy, :uniq => true | ||
| has_many :operatingsystems, :through => :operatingsystem_repositories | ||
| delegate :description, :to => :product | ||
| validates_presence_of :product | ||
| def self.model_name | ||
@@ -20,3 +19,9 @@ Repository.model_name | ||
| end | ||
| private | ||
| def set_originator_type | ||
| self.originator_type = 'Content::Product' | ||
| end | ||
| end | ||
| end |
| module Content | ||
| class ContentViewFactory | ||
| include ActiveModel::Conversion | ||
| extend ActiveModel::Naming | ||
| include ActiveModel::Validations | ||
| extend ActiveModel::Naming | ||
| attr_accessor :type, :originator_id, :parent_cv, :product_cv, :os_cv | ||
| # create a content view of a single product | ||
| def self.create_product_content_view(product_id) | ||
| product = Product.find_by_id(product_id) | ||
| ContentView.new(:source_repositories => product.repositories, :originator => product) | ||
| attr_accessor :originator_type, :originator_id, :parent_cv, :product_cv, :os_cv, :source_repositories | ||
| validates_presence_of :originator_type, :originator_id | ||
| def initialize(attributes = {}) | ||
| attributes.each do |name, value| | ||
| instance_variable_set("@#{name}", value) if respond_to?("#{name}".to_sym) | ||
| end | ||
| end | ||
| # create a content view of an operating system | ||
| def self.create_os_content_view(operatingsystem_id) | ||
| os = Operatingsystem.find_by_id(operatingsystem_id) | ||
| ContentView.new(:source_repositories => os.repositories, :originator => os) | ||
| def persisted? | ||
| false | ||
| end | ||
| def content_view | ||
| case originator_type | ||
| when 'Hostgroup' | ||
| create_composite_content_view | ||
| else | ||
| ContentView.new(:repository_ids_to_clone => originator.repositories, :originator => originator) | ||
| end | ||
| end | ||
| def repositories | ||
| originator.repositories | ||
| end | ||
| private | ||
| def originator | ||
| originator ||= case originator_type | ||
| when 'Content::Product' | ||
| Product.find(originator_id) | ||
| when 'Operatingsystem' | ||
| Operatingsystem.find(originator_id) | ||
| when 'Hostgroup' | ||
| Hostgroup.find(originator_id) | ||
| else | ||
| raise _('Unknown type') | ||
| end | ||
| end | ||
| # create a composite content view of a hostgroup (the hostgroup may have a list of products) and a parent | ||
| # content view. The parent content view is one of the hostgroup parent content views. | ||
| # A hostgroup may have number of content views representing different versions of that hostgroup content. | ||
| def self.create_composite_content_view(options = {}) | ||
| factory = new(options) | ||
| hostgroup = Hostgroup.find_by_id(factory.originator_id) | ||
| clone_ids = Content::RepositoryClone.for_content_views([factory.product_cv, factory.os_cv]). | ||
| pluck(:id) | ||
| ContentView.new(:source_repositories=> [], :originator => hostgroup, | ||
| :repository_clone_ids => clone_ids, :parent_id => factory.parent_cv) | ||
| def create_composite_content_view | ||
| clone_ids = Content::RepositoryClone.for_content_views([product_cv, os_cv]).pluck(:id) | ||
| source_ids = Content::Repository.for_content_views([product_cv, os_cv]).pluck(:id) | ||
| ContentView.new(:originator => originator, | ||
| :repository_clone_ids => clone_ids, | ||
| :repository_source_ids => source_ids, | ||
| :parent_id => parent_cv) | ||
| end | ||
| def initialize(attributes = {}) | ||
| attributes.each do |name, value| | ||
| instance_variable_set("@#{name}", value) if respond_to?("#{name}".to_sym) | ||
| end | ||
| end | ||
| def persisted? | ||
| false | ||
| end | ||
| end | ||
| end |
| class Content::Pulp::RepositorySyncHistory | ||
| attr_reader :exception, :removed_count, :started, :completed, :summary, | ||
| attr_reader :exception, :removed_count, :started, :completed, | ||
| :error_message, :added_count, :updated_count, :details, :result | ||
@@ -15,3 +15,3 @@ | ||
| :errata => summary[:errata_time_total_sec], | ||
| :packages => summary[:packages][:time_total_sec] | ||
| :packages => (summary[:packages][:time_total_sec] rescue 0) | ||
| } | ||
@@ -36,2 +36,8 @@ end | ||
| private | ||
| def summary | ||
| @summary ||= Hash.new(0) | ||
| end | ||
| end |
@@ -129,2 +129,15 @@ class Content::Pulp::Repository | ||
| def sync_schedule=(schedule) | ||
| type = Runcible::Extensions::YumImporter::ID | ||
| if schedule.present? | ||
| Runcible::Resources::RepositorySchedule.create(pulp_id, type, schedule) | ||
| else | ||
| Runcible::Extensions::Repository.remove_schedules(pulp_id, type) | ||
| end | ||
| end | ||
| def sync_schedule | ||
| Runcible::Resources::RepositorySchedule.list(pulp_id, Runcible::Extensions::YumImporter::ID) | ||
| end | ||
| private | ||
@@ -131,0 +144,0 @@ |
@@ -0,1 +1,3 @@ | ||
| <%= wizard_header 2, _('Select source type'), _('Select content'), _('Create') %> | ||
| <%= form_for @content_view, :url => (@content_view.new_record? ? content_views_path : content_view_path(@content_view)) do |f| %> | ||
@@ -14,9 +16,2 @@ <%= base_errors_for @content_view %> | ||
| <% if @content_view.new_record? %> | ||
| <% @content_view.source_repositories.each do |repo| %> | ||
| <%= f.hidden_field :source_repositories, :multiple => true, :value => repo.id %> | ||
| <% end if @content_view.source_repositories %> | ||
| <% @content_view.repository_clones.each do |repo| %> | ||
| <%= f.hidden_field :repository_clone_ids, :multiple => true, :value => repo.id %> | ||
| <% end if @content_view.repository_clones %> | ||
| <%= f.hidden_field :originator_id, :value => @content_view.originator_id %> | ||
@@ -28,13 +23,21 @@ <%= f.hidden_field :originator_type, :value => @content_view.originator_type %> | ||
| <tr> | ||
| <th></th> | ||
| <th><%= _("Name") %></th> | ||
| <th><%= _("State") %></th> | ||
| <th><%= _("Last publish") %></th> | ||
| <th><%= f.object.kind_of?(Content::Repository) ? _('Last Sync') : _('Last Publish') %></th> | ||
| <th><%= _("Content type") %></th> | ||
| </tr> | ||
| <% repositories(@content_view).each do |repository| %> | ||
| <tr> | ||
| <td><%= repository.name %></td> | ||
| <td><%= repository.try(:state) %></td> | ||
| <td><%= last_time(repository.last_published) %></td> | ||
| <td><%= repository.content_type %></td> | ||
| <% repositories.each do |repository| %> | ||
| <tr class='repository_selection'> | ||
| <td> | ||
| <% if @content_view.new_record? %> | ||
| <%= check_box_tag nil, repository.id, true %> | ||
| <%= f.hidden_field :repository_ids_to_clone, :value => repository.id, :multiple => true, :id => "clone-#{repository.id}", | ||
| :disabled => true %> | ||
| <%= f.hidden_field :repository_source_ids, :value => repository.id, :multiple => true, :id => "latest-#{repository.id}", | ||
| :disabled => true %> | ||
| <%= select_tag(nil, options_for_type_selection(repository), {:class => 'cv-repo-selection span2', :id => "select-#{repository.id}"}) %> | ||
| <% end %> | ||
| </td> | ||
| <%= render :partial => 'content/repositories/repository', :locals => {:repository => repository} %> | ||
| </tr> | ||
@@ -41,0 +44,0 @@ <% end %> |
@@ -5,3 +5,3 @@ <% title _("Content Views") %> | ||
| display_link_if_authorized(_("Operating system view"), hash_for_new_content_view_path(:type=>'operatingsystem')), | ||
| display_link_if_authorized(_("Hostgroup view"), hash_for_new_content_view_path(:type=>'hostgroup'))) %> | ||
| display_link_if_authorized(_("Composite hostgroup view"), hash_for_new_content_view_path(:type=>'hostgroup'))) %> | ||
@@ -18,7 +18,8 @@ | ||
| <tr> | ||
| <td><%= link_to_if_authorized(h(content_view.name), hash_for_edit_content_view_path(content_view)) %></td> | ||
| <td><%= link_to_if_authorized(h(content_view.name), hash_for_content_view_path(content_view)) %></td> | ||
| <td><%= @counter[content_view.id] || '0' %></td> | ||
| <td><%= _("%s ago") % time_ago_in_words(content_view.created_at) %></td> | ||
| <td><%= action_buttons( | ||
| display_delete_if_authorized(hash_for_content_view_path(content_view), :confirm => "Delete #{content_view}?") | ||
| display_link_if_authorized(_("Edit"), hash_for_edit_content_view_path(content_view)), | ||
| display_delete_if_authorized(hash_for_content_view_path(content_view), :confirm => "Delete #{content_view}?") | ||
| )%></td> | ||
@@ -25,0 +26,0 @@ </tr> |
@@ -5,8 +5,2 @@ <%= javascript 'content/content.js' %> | ||
| <% if @content_view %> | ||
| <%= render :partial => 'form' %> | ||
| <% elsif @hostgroup %> | ||
| <%= render :partial => 'step2' %> | ||
| <% else %> | ||
| <%= render :partial => 'step1' %> | ||
| <% end %> | ||
| <%= render :partial => step? %> |
| <%= javascript 'content/content.js' %> | ||
| <%= form_for @product, :url => (@product.new_record? ? products_path : product_path(@product)) do |f| %> | ||
| <%= base_errors_for @product %> | ||
| <ul class="nav nav-tabs" data-tabs="tabs"> | ||
| <li class="active"><a href="#primary" data-toggle="tab"><%= _("Product") %></a></li> | ||
| </ul> | ||
| <div class="tab-content"> | ||
| <div class="tab-pane active" id="primary"> | ||
| <%= text_f f, :name %> | ||
| <%= textarea_f f, :description, :class => "input-xlarge" , :rows=> '3' %> | ||
| </div> | ||
| </div> | ||
| <%= text_f f, :name %> | ||
| <%= textarea_f f, :description, :class => "input-xlarge", :rows => '3' %> | ||
| <%= submit_or_cancel f %> | ||
| <% end %> |
@@ -1,2 +0,1 @@ | ||
| <%= javascript 'content/content.js' %> | ||
| <%= form_for @repository, :url => (@repository.new_record? ? repositories_path : repository_path(:id => @repository.id)) do |f| %> | ||
@@ -6,18 +5,14 @@ <%= base_errors_for @repository %> | ||
| <li class="active"><a href="#primary" data-toggle="tab"><%= _("Repository") %></a></li> | ||
| <li><a href="#sync_schedule" data-toggle="tab"><%= _("Sync Schedule") %></a></li> | ||
| </ul> | ||
| <div class="tab-content"> | ||
| <div class="tab-pane active" id="primary"> | ||
| <%= f.hidden_field :type %> | ||
| <%= select_f f, :operatingsystem_id, ::Redhat.all, :id, :to_label, {}, {:label => _("Operating System")} %> | ||
| <%= text_f f, :name %> | ||
| <%= text_f f, :feed, :class => 'span6' %> | ||
| <%= selectable_f f, :content_type, @repository.content_types %> | ||
| <%= select_f f, :architecture_id, ::Architecture.all, :id, :name, { :include_blank => N_("noarch")} %> | ||
| <%= checkbox_f f, :enabled %> | ||
| <%= select_f f, :gpg_key_id, Content::GpgKey.all, :id, :name, {:include_blank => true}, {:label => _("GPG Key")} %> | ||
| <%= select_f f, :originator_id, ::Redhat.all, :id, :to_label, {}, {:label => _("Operating System")} %> | ||
| <%= render :partial => 'fields', :locals => {:f => f} %> | ||
| </div> | ||
| <div class="tab-pane" id="sync_schedule"> | ||
| <%= render :partial => 'sync_schedule', :locals => { :f => f } %> | ||
| </div> | ||
| </div> | ||
| <%= submit_or_cancel f %> | ||
| <% end %> |
@@ -1,2 +0,1 @@ | ||
| <%= javascript 'content/content.js' %> | ||
| <%= form_for @repository, :url => (@repository.new_record? ? repositories_path : repository_path(:id => @repository.id)) do |f| %> | ||
@@ -7,15 +6,8 @@ <%= base_errors_for @repository %> | ||
| <li><a href="#operatingsystems" data-toggle="tab"><%= _("Operating systems") %></a></li> | ||
| <li><a href="#sync_schedule" data-toggle="tab"><%= _("Sync Schedule") %></a></li> | ||
| </ul> | ||
| <div class="tab-content"> | ||
| <div class="tab-pane active" id="primary"> | ||
| <%= f.hidden_field :type %> | ||
| <%= select_f f, :product_id, Content::Product.all, :id, :name, {}, {:label => _("Product")} %> | ||
| <%= text_f f, :name %> | ||
| <%= text_f f, :feed, :class => 'span6' %> | ||
| <%= selectable_f f, :content_type, @repository.content_types %> | ||
| <%= select_f f, :architecture_id, ::Architecture.all, :id, :name, { :include_blank => N_("noarch")} %> | ||
| <%= checkbox_f f, :enabled %> | ||
| <%= select_f f, :gpg_key_id, Content::GpgKey.all, :id, :name, {:include_blank => true}, {:label => _("GPG Key")} %> | ||
| <%= select_f f, :originator_id, Content::Product.all, :id, :name, { :prompt => _('Please Select') }, { :label => _("Product") } %> | ||
| <%= render :partial => 'fields', :locals => { :f => f } %> | ||
| </div> | ||
@@ -27,4 +19,8 @@ <div class="tab-pane" id="operatingsystems"> | ||
| </div> | ||
| <div class="tab-pane" id="sync_schedule"> | ||
| <%= render :partial => 'sync_schedule', :locals => { :f => f } %> | ||
| </div> | ||
| </div> | ||
| <%= submit_or_cancel f %> | ||
| <% end %> |
@@ -0,1 +1,3 @@ | ||
| <%= javascript 'content/repository.js' %> | ||
| <% title _("Edit Repository") %> | ||
@@ -2,0 +4,0 @@ <% if @repository.is_a? Content::Repository::OperatingSystem %> |
@@ -10,3 +10,2 @@ <% title _("Repositories") %> | ||
| <th><%= sort :name, :as => s_("Name") %></th> | ||
| <th><%= _("Product") %></th> | ||
| <th><%= _("State") %></th> | ||
@@ -17,18 +16,17 @@ <th><%= _("Last sync status") %></th> | ||
| </tr> | ||
| <% @repositories.each do |repository| %> | ||
| <tr> | ||
| <td><%= link_to_if_authorized(h(repository.name), hash_for_repository_path(repository)) %></td> | ||
| <td><%= repository.entity_name %></td> | ||
| <td><%= repository.try(:state) %></td> | ||
| <td><%= last_time(repository.last_sync) %></td> | ||
| <td><%= repository.content_type %></td> | ||
| <td align="right"> | ||
| <%= action_buttons( | ||
| display_link_if_authorized(_("Edit"), hash_for_edit_repository_path(repository)), | ||
| display_link_if_authorized(_("Synchronize"), hash_for_sync_repository_path(repository), :method => :put), | ||
| display_delete_if_authorized(hash_for_repository_path(repository), :confirm => "Delete #{repository.name}?") | ||
| ) %> | ||
| <% @repositories.group_by(&:originator).each do |originator, repositories| %> | ||
| <tr><td colspan = '5'><%= originator %></td></tr> | ||
| <% repositories.each do |repository| %> | ||
| <tr> | ||
| <%= render :partial => 'repository', :locals => { :repository => repository } %> | ||
| <td align="right"> | ||
| <%= action_buttons( | ||
| display_link_if_authorized(_("Edit"), hash_for_edit_repository_path(repository)), | ||
| display_link_if_authorized(_("Synchronize"), hash_for_sync_repository_path(repository), :method => :put), | ||
| display_delete_if_authorized(hash_for_repository_path(repository), :confirm => "Delete #{repository.name}?") | ||
| ) %> | ||
| </td> | ||
| </tr> | ||
| </td> | ||
| </tr> | ||
| <% end %> | ||
| <% end %> | ||
@@ -35,0 +33,0 @@ </table> |
@@ -0,1 +1,3 @@ | ||
| <%= javascript 'content/repository.js' %> | ||
| <% title _("New Repository") %> | ||
@@ -2,0 +4,0 @@ <% if @repository.is_a? Content::Repository::OperatingSystem %> |
@@ -44,2 +44,13 @@ <% javascript 'charts' %> | ||
| </tr> | ||
| <% if @repository.publish %> | ||
| <tr> | ||
| <td> <%= _("URL") %> </td> | ||
| <td> <%= @repository.full_path %> </td> | ||
| </tr> | ||
| <% end %> | ||
| <tr> | ||
| <td> <%= _('Sync Schedule') %> </td> | ||
| <td> <%= sync_schedule(@repository.sync_schedule) %> </td> | ||
| </tr> | ||
| </table> | ||
@@ -49,45 +60,48 @@ </div> | ||
| <div class="row-fluid"> | ||
| <div class="stats-well span4"> | ||
| <h4 class="ca" ><%= _('Last Update Metrics') -%></h4> | ||
| <div style="margin-top:50px;padding-bottom: 40px;"> | ||
| <%= flot_pie_chart("metrics" ,_("Last Update Metrics"), @repository.sync_history.last.try(:times), :class => "statistics-pie small")%> | ||
| <%# running sync %> | ||
| <% if @repository.sync_history.any? %> | ||
| <div class="stats-well span4"> | ||
| <h4 class="ca" ><%= _('Last Update Metrics') -%></h4> | ||
| <div style="margin-top:50px;padding-bottom: 40px;"> | ||
| <%= flot_pie_chart("metrics" ,_("Last Update Metrics"), @repository.sync_history.last.try(:times), :class => "statistics-pie small") %> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div class="stats-well span4"> | ||
| <h4 class="ca" ><%= _('Update Summary') -%></h4> | ||
| <%= flot_bar_chart("status" ,"", _("Number of packages"), @repository.sync_history.last.try(:metrics), :class => "statistics-bar")%> | ||
| </div> | ||
| <div class="stats-well span4"> | ||
| <h4 class="ca" ><%= _('Update Summary') -%></h4> | ||
| <%= flot_bar_chart("status" ,"", _("Number of packages"), @repository.sync_history.last.try(:metrics), :class => "statistics-bar")%> | ||
| </div> | ||
| <div class="span4"> | ||
| <table class="table table-bordered table-striped"> | ||
| <tr> | ||
| <th><%= _('Repository Counters') %></th> | ||
| <th></th> | ||
| </tr> | ||
| <% @repository.counters.each do |name, value| -%> | ||
| <div class="span4"> | ||
| <table class="table table-bordered table-striped"> | ||
| <tr> | ||
| <td> <%= name.to_s.humanize %> </td> | ||
| <td> <%= value %> </td> | ||
| <th><%= _('Repository Counters') %></th> | ||
| <th></th> | ||
| </tr> | ||
| <% end -%> | ||
| <tr> | ||
| <td> <%= _("Last synchronized") %> </td> | ||
| <td> <%= last_time @repository.last_sync %> </td> | ||
| </tr> | ||
| </table> | ||
| </div> | ||
| <div class="span4"> | ||
| <table class="table table-bordered table-striped"> | ||
| <tr> | ||
| <th><%= _('Last Sync') %></th> | ||
| <th></th> | ||
| </tr> | ||
| <% @repository.sync_history.last.status.each do |name, value| -%> | ||
| <% @repository.counters.each do |name, value| -%> | ||
| <tr> | ||
| <td> <%= name.to_s.humanize %> </td> | ||
| <td> <%= value %> </td> | ||
| </tr> | ||
| <% end -%> | ||
| <tr> | ||
| <td> <%= name.to_s.humanize %> </td> | ||
| <td> <%= value %> </td> | ||
| <td> <%= _("Last synchronized") %> </td> | ||
| <td> <%= last_time @repository.last_sync %> </td> | ||
| </tr> | ||
| <% end -%> | ||
| </table> | ||
| </div> | ||
| </table> | ||
| </div> | ||
| <div class="span4"> | ||
| <table class="table table-bordered table-striped"> | ||
| <tr> | ||
| <th><%= _('Last Sync') %></th> | ||
| <th></th> | ||
| </tr> | ||
| <% @repository.sync_history.last.status.each do |name, value| -%> | ||
| <tr> | ||
| <td> <%= name.to_s.humanize %> </td> | ||
| <td> <%= value %> </td> | ||
| </tr> | ||
| <% end -%> | ||
| </table> | ||
| </div> | ||
| <% end %> | ||
| </div> |
@@ -12,5 +12,5 @@ class CreateContentContentViews < ActiveRecord::Migration | ||
| add_index(:content_content_views, :ancestry,:name=>'content_view_ancestry_index') | ||
| add_index :content_content_views, [:originator_id, :originator_type] | ||
| add_index :content_content_views, [:originator_id, :originator_type], :name => 'content_view_id_type_index' | ||
| add_index :content_content_views, :originator_type | ||
| end | ||
| end |
| module Content | ||
| VERSION = "0.3" | ||
| VERSION = "0.4" | ||
| end |
+6
-2
@@ -32,2 +32,3 @@ # foreman\_content | ||
| * enable oauth authentication | ||
| * make sure you have enough disk space! lots of GB to /var/lib/pulp and /var/lib/mongo | ||
@@ -54,3 +55,8 @@ ## UI config | ||
| example, CentOS 6.4 + CentOS updates. | ||
| * Content View - a collection of cloned repositories | ||
| ## Create a Product | ||
| If you are planning to sync non OS packages (e.g. 3rd party yum repo), you should first create a product, e.g. Foreman 1.2-stable, afterwards, you can add repositories to it. | ||
| ## Syncing Repositories | ||
@@ -68,4 +74,2 @@ | ||
| * Enabled - true / false | ||
| * Unprotected - weather pulp should expose this repo via http and https or just | ||
| over https. | ||
| * Product - the product from above. | ||
@@ -72,0 +76,0 @@ * GPG key - not implemented |
| <%= wizard_header 2, _("Select source type"), _("Select content"), _("Create") %> | ||
| <%= form_for Content::ContentViewFactory.new, :url => hash_for_new_content_view_path, :class => 'form-horizontal well', :method => :get do |f| %> | ||
| <%= f.hidden_field :originator_id, :value => @hostgroup.id %> | ||
| <%= f.hidden_field :originator_type, :value => @hostgroup.class.name %> | ||
| <%= select_f f, :parent_cv, @hostgroup.parent.try(:content_views) || [], "id", "name", | ||
| :label => _("Parent Content view") %> | ||
| <h6>Products</h6> | ||
| <% @hostgroup.products.each do |product| %> | ||
| <%= select_f f, :product_cv, product.content_views, "id", "name", {}, :label => product.name %> | ||
| <% end %> | ||
| <h6>Operating System</h6> | ||
| <%= select_f f, :os_cv, @hostgroup.os.content_views, "id", "name" %> | ||
| <%= next_or_cancel hash_for_new_content_view_path() %> | ||
| <% end %> |