katello 3.16.0.rc5 → 3.16.1.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +39 -23
  3. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -1
  4. data/app/controllers/katello/api/v2/content_view_filters_controller.rb +5 -1
  5. data/app/controllers/katello/api/v2/host_tracer_controller.rb +0 -5
  6. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +15 -0
  7. data/app/controllers/katello/api/v2/repositories_controller.rb +10 -1
  8. data/app/controllers/katello/concerns/hosts_controller_extensions.rb +11 -5
  9. data/app/helpers/katello/content_view_helper.rb +15 -0
  10. data/app/lib/actions/katello/capsule_content/refresh_repos.rb +1 -1
  11. data/app/lib/actions/katello/capsule_content/sync.rb +3 -2
  12. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +17 -3
  13. data/app/lib/actions/katello/content_view/incremental_updates.rb +3 -1
  14. data/app/lib/actions/katello/content_view/publish.rb +55 -16
  15. data/app/lib/actions/katello/content_view_version/incremental_update.rb +84 -53
  16. data/app/lib/actions/katello/host/attach_subscriptions.rb +5 -1
  17. data/app/lib/actions/katello/product/destroy.rb +25 -4
  18. data/app/lib/actions/katello/repository/destroy.rb +5 -1
  19. data/app/lib/actions/katello/repository/multi_clone_contents.rb +62 -0
  20. data/app/lib/actions/katello/repository/multi_clone_to_version.rb +30 -0
  21. data/app/lib/actions/katello/repository/sync.rb +35 -25
  22. data/app/lib/actions/katello/repository/update.rb +11 -16
  23. data/app/lib/actions/katello/repository/verify_checksum.rb +28 -0
  24. data/app/lib/actions/katello/sync_plan/run.rb +1 -1
  25. data/app/lib/actions/pulp/orchestration/repository/sync.rb +2 -1
  26. data/app/lib/actions/pulp/repository/sync.rb +2 -1
  27. data/app/lib/actions/pulp3/abstract_async_task.rb +62 -58
  28. data/app/lib/actions/pulp3/capsule_content/refresh_content_guard.rb +17 -0
  29. data/app/lib/actions/pulp3/capsule_content/sync.rb +3 -1
  30. data/app/lib/actions/pulp3/{ContentGuard → content_guard}/refresh.rb +0 -0
  31. data/app/lib/actions/pulp3/content_migration.rb +4 -0
  32. data/app/lib/actions/pulp3/orchestration/repository/copy_all_units.rb +2 -4
  33. data/app/lib/actions/pulp3/orchestration/repository/multi_copy_all_units.rb +36 -0
  34. data/app/lib/actions/pulp3/orchestration/repository/sync.rb +3 -1
  35. data/app/lib/actions/pulp3/orchestration/repository/trigger_update_repo_cert_guard.rb +22 -0
  36. data/app/lib/actions/pulp3/repository/copy_content.rb +0 -1
  37. data/app/lib/actions/pulp3/repository/multi_copy_content.rb +28 -0
  38. data/app/lib/actions/pulp3/repository/multi_copy_units.rb +14 -7
  39. data/app/lib/actions/pulp3/repository/presenters/content_unit_presenter.rb +1 -1
  40. data/app/lib/actions/pulp3/repository/presenters/repair_presenter.rb +85 -0
  41. data/app/lib/actions/pulp3/repository/repair.rb +29 -0
  42. data/app/lib/actions/pulp3/repository/save_version.rb +20 -8
  43. data/app/lib/actions/pulp3/repository/save_versions.rb +47 -13
  44. data/app/lib/actions/pulp3/repository/sync.rb +1 -1
  45. data/app/lib/actions/pulp3/repository/update_cv_repository_cert_guard.rb +6 -2
  46. data/app/lib/actions/pulp3/repository/upload_file.rb +1 -1
  47. data/app/lib/katello/concerns/base_template_scope_extensions.rb +4 -0
  48. data/app/lib/katello/errors.rb +1 -15
  49. data/app/lib/katello/resources/cdn.rb +3 -2
  50. data/app/lib/katello/util/cdn_var_substitutor.rb +9 -6
  51. data/app/models/katello/concerns/smart_proxy_extensions.rb +14 -3
  52. data/app/models/katello/content_view.rb +18 -6
  53. data/app/models/katello/content_view_erratum_filter.rb +13 -0
  54. data/app/models/katello/content_view_filter.rb +4 -0
  55. data/app/models/katello/content_view_module_stream_filter.rb +30 -3
  56. data/app/models/katello/content_view_package_filter.rb +1 -1
  57. data/app/models/katello/host/content_facet.rb +1 -0
  58. data/app/models/katello/module_stream.rb +1 -1
  59. data/app/models/katello/ping.rb +1 -3
  60. data/app/models/katello/repository.rb +16 -0
  61. data/app/models/setting/content.rb +1 -1
  62. data/app/presenters/katello/sync_status_presenter.rb +4 -2
  63. data/app/services/cert/certs.rb +10 -2
  64. data/app/services/katello/pulp/repository/yum.rb +2 -1
  65. data/app/services/katello/pulp3/api/core.rb +4 -0
  66. data/app/services/katello/pulp3/erratum.rb +3 -1
  67. data/app/services/katello/pulp3/migration.rb +10 -5
  68. data/app/services/katello/pulp3/migration_plan.rb +6 -6
  69. data/app/services/katello/pulp3/repository.rb +13 -5
  70. data/app/services/katello/pulp3/repository/yum.rb +235 -35
  71. data/app/services/katello/pulp3/repository_mirror.rb +7 -2
  72. data/app/services/katello/pulp3/smart_proxy_mirror_repository.rb +1 -1
  73. data/app/services/katello/pulp3/task.rb +100 -0
  74. data/app/services/katello/pulp3/task_group.rb +79 -0
  75. data/app/services/katello/smart_proxy_helper.rb +13 -16
  76. data/app/views/katello/api/v2/content_view_filters/base.json.rabl +4 -0
  77. data/config/routes/api/rhsm.rb +1 -0
  78. data/config/routes/api/v2.rb +2 -0
  79. data/db/migrate/20200709021250_add_original_modules_to_content_view_module_stream_filter.rb +5 -0
  80. data/db/migrate/20200721142707_remove_duplicate_katello_pools_index.rb +5 -0
  81. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.routes.js +1 -13
  82. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/filters/filter-details.controller.js +17 -4
  83. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/filters/views/module-stream-filter-details.html +17 -0
  84. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/product-bulk-action.factory.js +1 -0
  85. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details.controller.js +6 -0
  86. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-details.html +7 -1
  87. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/repository.factory.js +1 -0
  88. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/products.controller.js +15 -0
  89. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/views/products.html +6 -0
  90. data/lib/katello/permission_creator.rb +2 -2
  91. data/lib/katello/plugin.rb +0 -1
  92. data/lib/katello/tasks/pulp3_post_migration_check.rake +2 -1
  93. data/lib/katello/tasks/reports.rake +16 -0
  94. data/lib/katello/version.rb +1 -1
  95. data/webpack/redux/actions/RedHatRepositories/helpers.js +6 -6
  96. metadata +34 -14
  97. data/app/lib/actions/katello/repository/update_cv_repo_cert_guard.rb +0 -17
@@ -11,6 +11,11 @@ module Katello
11
11
  @repo_service = repository_service
12
12
  end
13
13
 
14
+ def content_guard_href
15
+ content_guard_api = ::Katello::Pulp3::Api::ContentGuard.new(smart_proxy)
16
+ content_guard_api.list&.results&.first&.pulp_href
17
+ end
18
+
14
19
  def backend_object_name
15
20
  #Create repos in pulp3 instance with the name as this repo's pulp_id
16
21
  repo.pulp_id
@@ -78,6 +83,7 @@ module Katello
78
83
  base_path: path,
79
84
  name: "#{backend_object_name}"
80
85
  }
86
+ ret[:content_guard] = repo.unprotected ? nil : content_guard_href
81
87
  ret[:publication] = options[:publication] if options.key? :publication
82
88
  ret[:repository_version] = options[:repository_version] if options.key? :repository_version
83
89
  ret
@@ -85,10 +91,9 @@ module Katello
85
91
 
86
92
  def remote_options
87
93
  base_options = common_remote_options
94
+ base_options.merge(url: remote_feed_url)
88
95
  if (type_specific_options = repo_service.try(:mirror_remote_options))
89
96
  base_options.merge(type_specific_options)
90
- else
91
- base_options.merge(url: remote_feed_url)
92
97
  end
93
98
  end
94
99
 
@@ -10,7 +10,7 @@ module Katello
10
10
  repo_map = {}
11
11
 
12
12
  smart_proxy_helper = ::Katello::SmartProxyHelper.new(smart_proxy)
13
- katello_pulp_ids = smart_proxy_helper.repos_available_to_capsule.map(&:pulp_id)
13
+ katello_pulp_ids = smart_proxy_helper.combined_repos_available_to_capsule.map(&:pulp_id)
14
14
  pulp3_enabled_repo_types.each do |repo_type|
15
15
  api = repo_type.pulp3_service_class.api(smart_proxy)
16
16
  repo_map[api] = api.list_all.reject { |capsule_repo| katello_pulp_ids.include? capsule_repo.name }
@@ -0,0 +1,100 @@
1
+ module Katello
2
+ module Pulp3
3
+ class Task
4
+ # A call report Looks like: {"task":"/pulp/api/v3/tasks/5/"}
5
+ # {
6
+ # "pulp_href":"/pulp/api/v3/tasks/4/",
7
+ # "pulp_created":"2019-02-21T19:50:40.476767Z",
8
+ # "job_id":"d0359658-d926-47a2-b430-1b2092b3bd86",
9
+ # "state":"completed",
10
+ # "name":"pulp_file.app.tasks.publishing.publish",
11
+ # "started_at":"2019-02-21T19:50:40.556002Z",
12
+ # "finished_at":"2019-02-21T19:50:40.618397Z",
13
+ # "non_fatal_errors":[
14
+ #
15
+ # ],
16
+ # "error":null,
17
+ # "worker":"/pulp/api/v3/workers/1/",
18
+ # "parent":null,
19
+ # "spawned_tasks":[
20
+ #
21
+ # ],
22
+ # "progress_reports":[
23
+ #
24
+ # ],
25
+ # "created_resources":[
26
+ # "/pulp/api/v3/publications/1/"
27
+ # ]
28
+ # }
29
+
30
+ WAITING = 'waiting'.freeze
31
+ SKIPPED = 'skipped'.freeze
32
+ RUNNING = 'running'.freeze
33
+ COMPLETED = 'completed'.freeze
34
+ FAILED = 'failed'.freeze
35
+ CANCELED = 'canceled'.freeze
36
+
37
+ FINISHED_STATES = [COMPLETED, FAILED, CANCELED, SKIPPED].freeze
38
+
39
+ #needed for serialization in dynflow
40
+
41
+ delegate :[], :key?, :dig, :to_hash, :to => :task_data
42
+
43
+ def initialize(smart_proxy, data)
44
+ @smart_proxy = smart_proxy
45
+ if (href = data['task'])
46
+ @href = href
47
+ else
48
+ @pulp_data = data.with_indifferent_access
49
+ @href = @pulp_data['pulp_href']
50
+ Rails.logger.error("Got empty pulp_href on #{@pulp_data}") if @href.nil?
51
+ end
52
+ end
53
+
54
+ def task_data(force_refresh = false)
55
+ @pulp_data = nil if force_refresh
56
+ @pulp_data ||= tasks_api.read(@href).as_json.with_indifferent_access
57
+ end
58
+
59
+ def tasks_api
60
+ ::Katello::Pulp3::Api::Core.new(@smart_proxy).tasks_api
61
+ end
62
+
63
+ def task_group_href
64
+ task_data[:task_group]
65
+ end
66
+
67
+ def done?
68
+ task_data[:finish_time] || FINISHED_STATES.include?(task_data[:state])
69
+ end
70
+
71
+ def poll
72
+ task_data(true)
73
+ self
74
+ end
75
+
76
+ def started?
77
+ task_data[:start_time]
78
+ end
79
+
80
+ def error
81
+ if task_data[:state] == CANCELED
82
+ self.new(_("Task canceled"))
83
+ elsif task_data[:state] == FAILED
84
+ if task_data[:error][:description].blank?
85
+ _("Pulp task error")
86
+ else
87
+ task_data[:error][:description]
88
+ end
89
+ end
90
+ end
91
+
92
+ def cancel
93
+ data = PulpcoreClient::Task.new(state: 'canceled')
94
+ tasks_api.tasks_cancel(pulp_task['pulp_href'], data)
95
+ #the main task may have completed, so cancel spawned tasks too
96
+ task_data['spawned_tasks']&.each { |spawned| tasks_api.tasks_cancel(spawned['pulp_href'], data) }
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,79 @@
1
+ module Katello
2
+ module Pulp3
3
+ class TaskGroup
4
+ WAITING = 'waiting'.freeze
5
+ SKIPPED = 'skipped'.freeze
6
+ RUNNING = 'running'.freeze
7
+ COMPLETED = 'completed'.freeze
8
+ CANCELLED = 'canceled'.freeze
9
+ FAILED = 'failed'.freeze
10
+
11
+ IN_PROGRESS_STATES = [WAITING, RUNNING].freeze
12
+
13
+ #needed for serialization in dynflow
14
+ delegate :to_hash, :to => :task_group_data
15
+ delegate :dig, :to => :task_group_data
16
+
17
+ attr_accessor :href
18
+
19
+ # A call report Looks like: {"task":"/pulp/api/v3/tasks/5/"}
20
+ #{
21
+ # "pulp_href":"/pulp/api/v3/task-groups/d9841aaa-8a47-4e31-9018-10e4430766bf/",
22
+ # "description":"Migration Sub-tasks",
23
+ # "waiting":0,
24
+ # "skipped":0,
25
+ # "running":0,
26
+ # "completed":0,
27
+ # "canceled":0,
28
+ # "failed":1
29
+ # }
30
+
31
+ def self.new_from_href(smart_proxy, href)
32
+ group = self.new(smart_proxy, {'pulp_href' => href})
33
+ group.clear_task_group_data
34
+ group
35
+ end
36
+
37
+ def initialize(smart_proxy, data)
38
+ @smart_proxy = smart_proxy
39
+ @pulp_data = data.with_indifferent_access
40
+ @href = @pulp_data['pulp_href']
41
+ Rails.logger.error("Got empty pulp_href on #{@pulp_data}") if @href.nil?
42
+ end
43
+
44
+ def task_group_data
45
+ @pulp_data ||= tasks_groups_api.read(@href).as_json.with_indifferent_access
46
+ end
47
+
48
+ def tasks_groups_api
49
+ ::Katello::Pulp3::Api::Core.new(@smart_proxy).task_groups_api
50
+ end
51
+
52
+ def done?
53
+ task_group_data['all_tasks_dispatched'] = true && IN_PROGRESS_STATES.all? { |state| task_group_data[state] == 0 }
54
+ end
55
+
56
+ def poll
57
+ clear_task_group_data
58
+ task_group_data
59
+ end
60
+
61
+ def clear_task_group_data
62
+ @pulp_data = nil
63
+ end
64
+
65
+ def started?
66
+ [SKIPPED, RUNNING, COMPLETED, CANCELLED, FAILED].any? { |state| task_group_data[state] }
67
+ end
68
+
69
+ def error
70
+ if task_group_data[FAILED] > 0
71
+ "#{task_group_data[FAILED]} subtask(s) failed for task group #{@href}."
72
+ end
73
+ end
74
+
75
+ def cancel
76
+ end
77
+ end
78
+ end
79
+ end
@@ -13,34 +13,31 @@ module Katello
13
13
  @smart_proxy.pulp_master?
14
14
  end
15
15
 
16
- def repos_available_to_capsule(environment = nil, content_view = nil, repository = nil)
17
- ret = []
18
- if repository
19
- environment = repository.environment
20
- ret = [repository]
21
- else
22
- yum_repos = fetch_repos_available_to_capsule(environment, content_view) || []
23
- puppet_envs = fetch_puppet_environments_available_to_capsule(environment, content_view) || []
24
- ret = yum_repos + puppet_envs
25
- end
16
+ def lifecycle_environment_check(environment = nil, repository = nil)
17
+ environment = repository.environment if repository
26
18
 
27
19
  if environment && !self.smart_proxy.lifecycle_environments.include?(environment)
28
20
  fail _("Lifecycle environment '%{environment}' is not attached to this capsule.") % { :environment => environment.name }
29
21
  end
30
-
31
- ret
32
22
  end
33
23
 
34
- private
24
+ def combined_repos_available_to_capsule(environment = nil, content_view = nil, repository = nil)
25
+ lifecycle_environment_check(environment, repository)
26
+ if repository
27
+ [repository]
28
+ else
29
+ repositories_available_to_capsule(environment, content_view) + puppet_environments_available_to_capsule(environment, content_view)
30
+ end
31
+ end
35
32
 
36
- def fetch_repos_available_to_capsule(environments = nil, content_view = nil)
33
+ def repositories_available_to_capsule(environments, content_view)
37
34
  environments = @smart_proxy.lifecycle_environments if environments.nil?
38
35
  yum_repos = Katello::Repository.in_environment(environments)
39
36
  yum_repos = yum_repos.in_content_views([content_view]) if content_view
40
- yum_repos.select(&:node_syncable?)
37
+ yum_repos.smart_proxy_syncable
41
38
  end
42
39
 
43
- def fetch_puppet_environments_available_to_capsule(environments = nil, content_view = nil)
40
+ def puppet_environments_available_to_capsule(environments, content_view)
44
41
  environments = @smart_proxy.lifecycle_environments if environments.nil?
45
42
  puppet_environments = Katello::ContentViewPuppetEnvironment.in_environment(environments)
46
43
  puppet_environments = puppet_environments.in_content_view(content_view) if content_view
@@ -17,6 +17,10 @@ if @resource.respond_to?(:package_rules)
17
17
  attributes :original_packages
18
18
  end
19
19
 
20
+ if @resource.respond_to?(:module_stream_rules)
21
+ attributes :original_module_streams
22
+ end
23
+
20
24
  node :rules do |filter|
21
25
  if filter.respond_to?(:package_rules)
22
26
  filter.package_rules.map do |rule|
@@ -21,6 +21,7 @@ Katello::Engine.routes.draw do
21
21
  match '/owners/:organization_id/environments' => 'candlepin_proxies#rhsm_index', :via => :get
22
22
  match '/owners/:organization_id/pools' => 'candlepin_proxies#get', :via => :get, :as => :proxy_owner_pools_path
23
23
  match '/owners/:organization_id/servicelevels' => 'candlepin_proxies#get', :via => :get, :as => :proxy_owner_servicelevels_path
24
+ match '/owners/:organization_id/system_purpose' => 'candlepin_proxies#get', :via => :get, :as => :proxy_owner_system_purpose_path
24
25
  match '/environments/:environment_id/consumers' => 'candlepin_proxies#consumer_create', :via => :post
25
26
  match '/consumers/:id' => 'candlepin_proxies#consumer_show', :via => :get
26
27
  match '/consumers/:id' => 'candlepin_proxies#regenerate_identity_certificates', :via => :post
@@ -392,6 +392,7 @@ Katello::Engine.routes.draw do
392
392
  put :remove_docker_manifests, :action => :remove_content
393
393
  put :remove_content
394
394
  post :sync
395
+ post :verify_checksum
395
396
  post :export
396
397
  post :upload_content
397
398
  put :import_uploads
@@ -421,6 +422,7 @@ Katello::Engine.routes.draw do
421
422
  collection do
422
423
  match '/bulk/destroy' => 'products_bulk_actions#destroy_products', :via => :put
423
424
  match '/bulk/sync' => 'products_bulk_actions#sync_products', :via => :put
425
+ match '/bulk/verify_checksum' => 'products_bulk_actions#verify_checksum_products', :via => :put
424
426
  match '/bulk/sync_plan' => 'products_bulk_actions#update_sync_plans', :via => :put
425
427
  match '/bulk/http_proxy' => 'products_bulk_actions#update_http_proxy', :via => :put
426
428
  end
@@ -0,0 +1,5 @@
1
+ class AddOriginalModulesToContentViewModuleStreamFilter < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :katello_content_view_filters, :original_module_streams, :boolean, :default => false, :null => false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveDuplicateKatelloPoolsIndex < ActiveRecord::Migration[6.0]
2
+ def change
3
+ remove_index :katello_pools, name: 'index_pools_on_cp_id'
4
+ end
5
+ end
@@ -10,21 +10,9 @@
10
10
  */
11
11
  angular.module('Bastion.capsule-content').config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
12
12
  //Catch the url to prevent the router to perform redirect.
13
- $urlRouterProvider.when('/smart_proxies/:proxyId', ['$match', '$stateParams', function ($match, $stateParams) {
14
- $stateParams.pageName = 'smart_proxies/detail';
13
+ $urlRouterProvider.when('/smart_proxies/:proxyId', [function () {
15
14
  return true;
16
15
  }]);
17
-
18
- // Add rule to redirect links on the smart proxy detail page.
19
- // Changing state doesn't work there since there's no <ui-view> element there
20
- $urlRouterProvider.rule(function ($injector, $location) {
21
- var $stateParams = $injector.get('$stateParams'),
22
- $window = $injector.get('$window');
23
-
24
- if ($stateParams.pageName === 'smart_proxies/detail') {
25
- $window.location.href = $location.path();
26
- }
27
- });
28
16
  }]);
29
17
 
30
18
  /**
@@ -3,19 +3,32 @@
3
3
  * @name Bastion.content-views.controller:FilterDetailsController
4
4
  *
5
5
  * @requires $scope
6
+ * @requires $q
7
+ * @requires translate
8
+ * @requires Notification
6
9
  * @requires Filter
7
10
  *
8
11
  * @description
9
12
  * Handles fetching a filter.
10
13
  */
11
14
  angular.module('Bastion.content-views').controller('FilterDetailsController',
12
- ['$scope', 'Filter', function ($scope, Filter) {
13
-
15
+ ['$scope', '$q', 'translate', 'Notification', 'Filter', function ($scope, $q, translate, Notification, Filter) {
14
16
  $scope.filter = Filter.get({'content_view_id': $scope.$stateParams.contentViewId, filterId: $scope.$stateParams.filterId});
15
17
 
16
18
  $scope.updateFilter = function (filter) {
17
- filter.$update();
18
- };
19
+ var deferred = $q.defer();
19
20
 
21
+ filter.$update(function (response) {
22
+ deferred.resolve(response);
23
+ Notification.setSuccessMessage(translate('Filter Updated - ' + $scope.filter.name));
24
+ }, function (response) {
25
+ deferred.reject(response);
26
+ angular.forEach(response.data.errors, function (errorMessage) {
27
+ Notification.setErrorMessage(translate("An error occurred saving the Filter: ") + errorMessage);
28
+ });
29
+ });
30
+
31
+ return deferred.promise;
32
+ };
20
33
  }]
21
34
  );
@@ -1,4 +1,21 @@
1
1
  <div data-extend-template="layouts/partials/table.html">
2
+ <div data-block="filters">
3
+ <div class="checkbox">
4
+ <label>
5
+ <input type="checkbox"
6
+ ng-model="filter.original_module_streams"
7
+ ng-change="updateFilter(filter)"/>
8
+
9
+ <span ng-show="filter.inclusion" translate>
10
+ Include all Module Streams with no errata.
11
+ </span>
12
+
13
+ <span ng-show="!filter.inclusion" translate>
14
+ Exclude all Module Streams with no errata.
15
+ </span>
16
+ </label>
17
+ </div>
18
+ </div>
2
19
 
3
20
  <div data-block="list-actions">
4
21
  <button type="button" class="btn btn-primary"
@@ -13,6 +13,7 @@ angular.module('Bastion.products').factory('ProductBulkAction',
13
13
  return BastionResource('katello/api/v2/products/bulk/:action', {}, {
14
14
  removeProducts: {method: 'PUT', params: {action: 'destroy'}},
15
15
  syncProducts: {method: 'PUT', params: {action: 'sync'}},
16
+ verifyChecksumProducts: {method: 'PUT', params: {action: 'verify_checksum'}},
16
17
  updateProductSyncPlan: {method: 'PUT', params: {action: 'sync_plan'}},
17
18
  updateProductHttpProxy: {method: 'PUT', params: {action: 'http_proxy'}}
18
19
  });
@@ -70,6 +70,12 @@
70
70
  }, errorHandler);
71
71
  };
72
72
 
73
+ $scope.verifyChecksum = function (repository) {
74
+ Repository.verifyChecksum({id: repository.id}, function (task) {
75
+ $state.go('product.repository.tasks.details', {taskId: task.id});
76
+ }, errorHandler);
77
+ };
78
+
73
79
  $scope.republishRepository = function (repository) {
74
80
  Repository.republish({id: repository.id}, function (task) {
75
81
  $state.go('product.repository.tasks.details', {taskId: task.id});
@@ -32,6 +32,12 @@
32
32
  </a>
33
33
  </li>
34
34
 
35
+ <li role="menuitem" ng-hide="hideSyncButton(repository, true)" ng-class="{disabled: disableSyncLink()}">
36
+ <a ng-click="verifyChecksum(repository)" disable-link="disableSyncLink()" translate>
37
+ Verify Content Checksum
38
+ </a>
39
+ </li>
40
+
35
41
  <li role="menuitem" ng-hide="syncInProgress(repository.last_sync) || denied('edit_products', product)">
36
42
  <a ng-click="republishRepository(repository)" translate>
37
43
  Republish Repository Metadata
@@ -90,4 +96,4 @@
90
96
  <div data-block="content">
91
97
  <section ui-view></section>
92
98
  </div>
93
- </div>
99
+ </div>