Revision 870d4598
Added by Ondřej Ezr over 5 years ago
app/controllers/api/v2/compute_resources_controller.rb | ||
---|---|---|
before_action :find_resource, :only => [:show, :update, :destroy, :available_images, :associate,
|
||
:available_clusters, :available_flavors, :available_folders,
|
||
:available_networks, :available_resource_pools, :available_security_groups, :available_storage_domains,
|
||
:available_zones, :available_storage_pods, :refresh_cache]
|
||
:available_zones, :available_storage_pods, :storage_domain, :storage_pod, :refresh_cache]
|
||
|
||
api :GET, "/compute_resources/", N_("List all compute resources")
|
||
param_group :taxonomy_scope, ::Api::V2::BaseController
|
||
... | ... | |
param :id, :identifier, :required => true
|
||
param :cluster_id, String
|
||
def available_networks
|
||
@available_networks = @compute_resource.available_networks(params[:cluster_id])
|
||
@available_networks = @compute_resource.available_networks(params[:cluster_id].presence)
|
||
@total = @available_networks&.size
|
||
render :available_networks, :layout => 'api/v2/layouts/index_layout'
|
||
end
|
||
... | ... | |
render :available_resource_pools, :layout => 'api/v2/layouts/index_layout'
|
||
end
|
||
|
||
api :GET, "/compute_resources/:id/storage_domains/:storage_domain_id", N_("List attributes for a given storage domain")
|
||
param :id, :identifier, :required => true
|
||
param :storage_domain_id, String, :required => true
|
||
def storage_domain
|
||
@storage_domain = @compute_resource.storage_domain(params[:storage_domain_id])
|
||
end
|
||
|
||
api :GET, "/compute_resources/:id/available_storage_domains", N_("List storage domains for a compute resource")
|
||
api :GET, "/compute_resources/:id/available_storage_domains/:storage_domain", N_("List attributes for a given storage domain")
|
||
api :GET, "/compute_resources/:id/available_clusters/:cluster_id/available_storage_domains", N_("List storage domains for a compute resource")
|
||
param :id, :identifier, :required => true
|
||
param :cluster_id, String
|
||
param :storage_domain, String
|
||
def available_storage_domains
|
||
@available_storage_domains = @compute_resource.available_storage_domains(params[:storage_domain])
|
||
if params[:storage_domain]
|
||
Foreman::Deprecation.api_deprecation_warning("use /compute_resources/:id/storage_domain/:storage_domain_id endpoind instead")
|
||
@available_storage_domains = [@compute_resource.storage_domain(params[:storage_domain])]
|
||
else
|
||
@available_storage_domains = @compute_resource.available_storage_domains(params[:cluster_id].presence)
|
||
end
|
||
@total = @available_storage_domains&.size
|
||
render :available_storage_domains, :layout => 'api/v2/layouts/index_layout'
|
||
end
|
||
|
||
api :GET, "/compute_resources/:id/storage_pods/:storage_pod_id", N_("List attributes for a given storage pod")
|
||
param :id, :identifier, :required => true
|
||
param :storage_pod_id, String, :required => true
|
||
def storage_pod
|
||
@storage_pod = @compute_resource.storage_pod(params[:storage_pod_id])
|
||
end
|
||
|
||
api :GET, "/compute_resources/:id/available_storage_pods", N_("List storage pods for a compute resource")
|
||
api :GET, "/compute_resources/:id/available_storage_pods/:storage_pod", N_("List attributes for a given storage pod")
|
||
api :GET, "/compute_resources/:id/available_clusters/:cluster_id/available_storage_pods", N_("List storage pods for a compute resource")
|
||
param :id, :identifier, :required => true
|
||
param :cluster_id, String
|
||
param :storage_pod, String
|
||
def available_storage_pods
|
||
@available_storage_pods = @compute_resource.available_storage_pods(params[:storage_pod])
|
||
if params[:storage_pod]
|
||
Foreman::Deprecation.api_deprecation_warning("use /compute_resources/:id/storage_pod/:storage_pod_id endpoind instead")
|
||
@available_storage_pods = [@compute_resource.storage_pod(params[:storage_pod])]
|
||
else
|
||
@available_storage_pods = @compute_resource.available_storage_pods(params[:cluster_id].presence)
|
||
end
|
||
@total = @available_storage_pods&.size
|
||
render :available_storage_pods, :layout => 'api/v2/layouts/index_layout'
|
||
end
|
||
... | ... | |
|
||
def action_permission
|
||
case params[:action]
|
||
when 'available_images', 'available_clusters', 'available_flavors', 'available_folders', 'available_networks', 'available_resource_pools', 'available_security_groups', 'available_storage_domains', 'available_zones', 'associate', 'available_storage_pods', 'refresh_cache'
|
||
when 'available_images', 'available_clusters', 'available_flavors', 'available_folders', 'available_networks', 'available_resource_pools', 'available_security_groups', 'available_storage_domains', 'storage_domain', 'available_zones', 'associate', 'available_storage_pods', 'storage_pod', 'refresh_cache'
|
||
:view
|
||
else
|
||
super
|
app/helpers/compute_resources_vms_helper.rb | ||
---|---|---|
options
|
||
end
|
||
|
||
def libvirt_networks(compute)
|
||
networks = compute.networks
|
||
def libvirt_networks(compute_resource)
|
||
networks = compute_resource.networks
|
||
select = []
|
||
select << [_('Physical (Bridge)'), :bridge]
|
||
select << [_('Virtual (NAT)'), :network] if networks.any?
|
||
select
|
||
end
|
||
|
||
def vsphere_networks(compute_resource)
|
||
networks = compute_resource.networks
|
||
def vsphere_networks(compute_resource, cluster_id = nil)
|
||
networks = compute_resource.networks(cluster_id: cluster_id)
|
||
networks.map do |net|
|
||
net_id = net.id
|
||
net_name = net.name
|
||
... | ... | |
display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer), :class => 'btn btn-danger')
|
||
end
|
||
|
||
def vsphere_scsi_controllers(compute)
|
||
def vsphere_scsi_controllers(compute_resource)
|
||
scsi_controllers = {}
|
||
compute.scsi_controller_types.each { |type| scsi_controllers[type[:key]] = type[:title] }
|
||
compute_resource.scsi_controller_types.each { |type| scsi_controllers[type[:key]] = type[:title] }
|
||
scsi_controllers
|
||
end
|
||
|
app/models/compute_resource.rb | ||
---|---|---|
false
|
||
end
|
||
|
||
def storage_domain(storage_domain)
|
||
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
|
||
def storage_pod(storage_pod)
|
||
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
|
||
def available_zones
|
||
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
... | ... | |
[]
|
||
end
|
||
|
||
def available_networks
|
||
def available_networks(cluster_id = nil)
|
||
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
|
||
... | ... | |
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
|
||
def available_storage_domains(storage_domain = nil)
|
||
def available_storage_domains(cluster_id = nil)
|
||
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
|
||
def available_storage_pods(storage_pod = nil)
|
||
def available_storage_pods(cluster_id = nil)
|
||
raise ::Foreman::Exception.new(N_("Not implemented for %s"), provider_friendly_name)
|
||
end
|
||
|
app/models/compute_resources/foreman/model/ovirt.rb | ||
---|---|---|
networks({:cluster_id => cluster_id})
|
||
end
|
||
|
||
def available_storage_domains(storage_domain = nil)
|
||
def available_storage_domains(cluster_id = nil)
|
||
storage_domains
|
||
end
|
||
|
app/models/compute_resources/foreman/model/vmware.rb | ||
---|---|---|
dc_clusters.map(&:full_path).sort
|
||
end
|
||
|
||
# Params:
|
||
# +name+ identifier of the datastore - its name unique in given vCenter
|
||
def datastore(name)
|
||
cache.cache(:"datastore-#{name}") do
|
||
dc.datastores.get(name)
|
||
end
|
||
end
|
||
|
||
# ==== Options
|
||
#
|
||
# * +:cluster_id+ - Limits the datastores in response to the ones available to defined cluster
|
||
def datastores(opts = {})
|
||
if opts[:storage_domain]
|
||
cache.cache(:"datastores-#{opts[:storage_domain]}") do
|
||
name_sort(dc.datastores.get(opts[:storage_domain]))
|
||
end
|
||
else
|
||
cache.cache(:datastores) do
|
||
name_sort(dc.datastores.all(:accessible => true))
|
||
cache.cache(cachekey_with_cluster(:datastores, opts[:cluster_id])) do
|
||
name_sort(dc.datastores(cluster: opts[:cluster_id]).all(:accessible => true))
|
||
end
|
||
end
|
||
|
||
def storage_pod(name)
|
||
cache.cache(:"storage_pod-#{name}") do
|
||
begin
|
||
dc.storage_pods.get(name)
|
||
rescue RbVmomi::VIM::InvalidArgument
|
||
{} # Return an empty storage pod hash if vsphere does not support the feature
|
||
end
|
||
end
|
||
end
|
||
|
||
##
|
||
# Lists storage_pods for datastore/cluster.
|
||
# TODO: fog-vsphere doesn't support cluster base filtering, so the cluser_id is useless for now.
|
||
# ==== Options
|
||
#
|
||
# * +:cluster_id+ - Limits the datastores in response to the ones available to defined cluster
|
||
def storage_pods(opts = {})
|
||
if opts[:storage_pod]
|
||
cache.cache(:"storage_pods-#{opts[:storage_pod]}") do
|
||
begin
|
||
dc.storage_pods.get(opts[:storage_pod])
|
||
rescue RbVmomi::VIM::InvalidArgument
|
||
{} # Return an empty storage pod hash if vsphere does not support the feature
|
||
end
|
||
end
|
||
else
|
||
cache.cache(:storage_pods) do
|
||
begin
|
||
name_sort(dc.storage_pods.all())
|
||
rescue RbVmomi::VIM::InvalidArgument
|
||
[] # Return an empty set of storage pods if vsphere does not support the feature
|
||
end
|
||
cache.cache(cachekey_with_cluster(:storage_pods, opts[:cluster_id])) do
|
||
begin
|
||
name_sort(dc.storage_pods.all(cluster: opts[:cluster_id]))
|
||
rescue RbVmomi::VIM::InvalidArgument
|
||
[] # Return an empty set of storage pods if vsphere does not support the feature
|
||
end
|
||
end
|
||
end
|
||
|
||
def available_storage_pods(storage_pod = nil)
|
||
storage_pods({:storage_pod => storage_pod})
|
||
def available_storage_pods(cluster_id = nil)
|
||
storage_pods(cluster_id: cluster_id)
|
||
end
|
||
|
||
def folders
|
||
... | ... | |
end
|
||
|
||
def networks(opts = {})
|
||
cache.cache(:networks) do
|
||
name_sort(dc.networks.all(:accessible => true))
|
||
cache_key = opts[:cluster_id].nil? ? :networks : :"networks-#{opts[:cluster_id]}"
|
||
cache.cache(cache_key) do
|
||
name_sort(dc.networks(cluster: opts[:cluster_id]).all(:accessible => true))
|
||
end
|
||
end
|
||
|
||
... | ... | |
end
|
||
|
||
def available_networks(cluster_id = nil)
|
||
networks
|
||
networks(cluster_id: cluster_id)
|
||
end
|
||
|
||
def available_storage_domains(storage_domain = nil)
|
||
datastores({:storage_domain => storage_domain})
|
||
def storage_domain(storage_domain)
|
||
datastore(storage_domain)
|
||
end
|
||
|
||
def available_storage_domains(cluster_id = nil)
|
||
datastores(cluster_id: cluster_id)
|
||
end
|
||
|
||
def available_resource_pools(opts = {})
|
||
... | ... | |
Foreman::Logging.exception('Failed to parse user-data template', e)
|
||
raise Foreman::Exception.new('The user-data template must be valid YAML for VM customization to work.')
|
||
end
|
||
|
||
def cachekey_with_cluster(key, cluster_id = nil)
|
||
cluster_id.nil? ? key.to_sym : "#{key}-#{cluster_id}".to_sym
|
||
end
|
||
end
|
||
end
|
app/registries/foreman/access_permissions.rb | ||
---|---|---|
:"api/v2/compute_resources" => [:index, :show, :available_images, :available_clusters, :available_folders,
|
||
:available_flavors, :available_networks, :available_resource_pools,
|
||
:available_security_groups, :available_storage_domains, :available_zones,
|
||
:available_storage_pods, :refresh_cache]
|
||
:available_storage_pods, :storage_pod, :storage_domain, :refresh_cache]
|
||
}
|
||
map.permission :create_compute_resources, {:compute_resources => [:new, :create].push(*ajax_actions),
|
||
:"api/v2/compute_resources" => [:create]
|
app/views/api/v2/compute_resources/storage_domain.rabl | ||
---|---|---|
object @storage_domain
|
||
|
||
attribute :name, :id, :capacity, :freespace, :uncommitted
|
app/views/api/v2/compute_resources/storage_pod.rabl | ||
---|---|---|
object @storage_pod
|
||
|
||
attribute :name, :id, :capacity, :freespace
|
app/views/compute_resources_vms/form/vmware/_base.html.erb | ||
---|---|---|
end %>
|
||
<%= selectable_f f, :cluster, compute_resource.clusters, { :include_blank => _('Please select a cluster') },
|
||
:class => "col-md-2", :disabled => !new_vm,
|
||
:label => _('Cluster'), :onchange => 'tfm.computeResource.vmware.getResourcePools(this)',
|
||
:label => _('Cluster'), :onchange => 'tfm.computeResource.vmware.onClusterChange(this)',
|
||
:help_inline => :indicator,
|
||
:data => {:url => resource_pools_compute_resource_path(compute_resource)} %>
|
||
:data => {:poolsurl => resource_pools_compute_resource_path(compute_resource), :networksurl => available_networks_api_compute_resource_path(compute_resource)} %>
|
||
<%= vsphere_resource_pools(f, compute_resource, !new_vm) %>
|
||
<%= select_f f, :path, compute_resource.folders, :path, :to_label , {}, { :label => _("Folder"), :class => "col-md-2", :disabled => !new_vm } %>
|
||
<%= select_f f, :guest_id, compute_resource.guest_types, :first, :last, {}, { :label => _("Guest OS"), :class => "col-md-2", :disabled => !new_vm } %>
|
||
... | ... | |
storagePodsUrl: available_storage_pods_api_compute_resource_path(compute_resource)
|
||
},
|
||
volumes: f.object.volumes.map { |volume| volume.attributes.merge(:size_gb => volume.size_gb).deep_transform_keys { |key| key.to_s.camelize(:lower).to_sym }.reject { |k,v| k == :Size } },
|
||
controllers: f.object.scsi_controllers
|
||
controllers: f.object.scsi_controllers,
|
||
cluster: f.object.cluster.to_s
|
||
}.to_json) %>
|
app/views/compute_resources_vms/form/vmware/_network.html.erb | ||
---|---|---|
:class => "col-md-3 vmware_type",
|
||
:label => _('NIC type'), :label_size => "col-md-3", :disabled => !new_vm
|
||
%>
|
||
<% cluster_id = params.fetch(:host, {}).fetch(:compute_attributes, {}).fetch(:cluster, nil).presence %>
|
||
<%= select_f f, :network, vsphere_networks(compute_resource), :first, :last, { },
|
||
:class => "col-md-3 vmware_network",
|
||
:label => _('Network'), :label_size => "col-md-3", :disabled => !new_vm
|
config/routes/api/v2.rb | ||
---|---|---|
get :available_networks, :on => :member
|
||
get :available_security_groups, :on => :member
|
||
get :available_storage_domains, :on => :member
|
||
get 'storage_domains/(:storage_domain_id)', :to => 'compute_resources#storage_domain', :on => :member
|
||
get 'available_storage_domains/(:storage_domain)', :to => 'compute_resources#available_storage_domains', :on => :member
|
||
get :available_storage_pods, :on => :member
|
||
get 'storage_pods/(:storage_pod_id)', :to => 'compute_resources#storage_pod', :on => :member
|
||
get 'available_storage_pods/(:storage_pod)', :to => 'compute_resources#available_storage_pods', :on => :member
|
||
get 'available_clusters/(:cluster_id)/available_networks', :to => 'compute_resources#available_networks', :on => :member
|
||
get 'available_clusters/(:cluster_id)/available_resource_pools', :to => 'compute_resources#available_resource_pools', :on => :member
|
||
get 'available_clusters/(:cluster_id)/available_storage_domains', :to => 'compute_resources#available_storage_domains', :on => :member
|
||
get 'available_clusters/(:cluster_id)/available_storage_pods', :to => 'compute_resources#available_storage_pods', :on => :member
|
||
get :available_zones, :on => :member
|
||
put :associate, :on => :member
|
||
put :refresh_cache, :on => :member
|
test/controllers/api/v2/compute_resources_controller_test.rb | ||
---|---|---|
end
|
||
end
|
||
|
||
test "should get specific vmware storage domain" do
|
||
storage_domain = Object.new
|
||
storage_domain.stubs(:name).returns('test_vmware_cluster')
|
||
storage_domain.stubs(:id).returns('my11-test35-uuid99')
|
||
test "should get specific vmware storage domain - the deprecated way" do
|
||
storage_domain = OpenStruct.new(id: 'my11-test35-uuid99', name: 'test_vmware_datastore')
|
||
|
||
Foreman::Model::Vmware.any_instance.expects(:available_storage_domains).with('test_vmware_cluster').returns([storage_domain])
|
||
Foreman::Model::Vmware.any_instance.expects(:storage_domain).with('test_vmware_datastore').returns(storage_domain)
|
||
|
||
get :available_storage_domains, params: { :id => compute_resources(:vmware).to_param, :storage_domain => 'test_vmware_cluster' }
|
||
Foreman::Deprecation.expects(:api_deprecation_warning)
|
||
|
||
get :available_storage_domains, params: { :id => compute_resources(:vmware).to_param, :storage_domain => 'test_vmware_datastore' }
|
||
assert_response :success
|
||
available_storage_domains = ActiveSupport::JSON.decode(@response.body)
|
||
assert_equal storage_domain.id, available_storage_domains['results'].first.try(:[], 'id')
|
||
end
|
||
|
||
test "should get specific vmware storage pod" do
|
||
storage_pod = Object.new
|
||
storage_pod.stubs(:name).returns('test_vmware_pod')
|
||
storage_pod.stubs(:id).returns('group-p123456')
|
||
test "should get specific vmware storage domain" do
|
||
storage_domain = OpenStruct.new(id: 'my11-test35-uuid99', name: 'test_vmware_datastore')
|
||
|
||
Foreman::Model::Vmware.any_instance.expects(:storage_domain).with('test_vmware_datastore').returns(storage_domain)
|
||
|
||
get :storage_domain, params: { :id => compute_resources(:vmware).to_param, :storage_domain_id => 'test_vmware_datastore' }
|
||
assert_response :success
|
||
storage_domain_response = ActiveSupport::JSON.decode(@response.body)
|
||
assert_equal storage_domain.id, storage_domain_response.try(:[], 'id')
|
||
end
|
||
|
||
test "should get specific vmware storage pod - the deprecated way" do
|
||
storage_pod = OpenStruct.new(id: 'group-p123456', name: 'test_vmware_pod')
|
||
|
||
Foreman::Model::Vmware.any_instance.expects(:storage_pod).with('test_vmware_pod').returns(storage_pod)
|
||
|
||
Foreman::Model::Vmware.any_instance.expects(:available_storage_pods).with('test_vmware_pod').returns([storage_pod])
|
||
Foreman::Deprecation.expects(:api_deprecation_warning)
|
||
|
||
get :available_storage_pods, params: { :id => compute_resources(:vmware).to_param, :storage_pod => 'test_vmware_pod' }
|
||
assert_response :success
|
||
... | ... | |
assert_equal storage_pod.id, available_storage_pods['results'].first.try(:[], 'id')
|
||
end
|
||
|
||
test "should get specific vmware storage pod" do
|
||
storage_pod = OpenStruct.new(id: 'group-p123456', name: 'test_vmware_pod')
|
||
|
||
Foreman::Model::Vmware.any_instance.expects(:storage_pod).with('test_vmware_pod').returns(storage_pod)
|
||
|
||
get :storage_pod, params: { :id => compute_resources(:vmware).to_param, :storage_pod_id => 'test_vmware_pod' }
|
||
assert_response :success
|
||
storage_pod_response = ActiveSupport::JSON.decode(@response.body)
|
||
assert_equal storage_pod.id, storage_pod_response.try(:[], 'id')
|
||
end
|
||
|
||
test "should associate hosts that match" do
|
||
host_cr = FactoryBot.create(:host, :on_compute_resource)
|
||
host_bm = FactoryBot.create(:host)
|
webpack/assets/javascripts/compute_resource/ovirt.js | ||
---|---|---|
}
|
||
export function clusterSelected(item) {
|
||
const cluster = $(item).val();
|
||
const url = $(item).attr('data-url');
|
||
const url = $(item).data('url');
|
||
|
||
showSpinner();
|
||
$.ajax({
|
webpack/assets/javascripts/compute_resource/vmware.js | ||
---|---|---|
import $ from 'jquery';
|
||
import store from '../react_app/redux';
|
||
import { showSpinner, hideSpinner } from '../foreman_tools';
|
||
import { changeCluster } from '../react_app/redux/actions/hosts/storage/vmware';
|
||
|
||
export function getResourcePools(item) {
|
||
export function onClusterChange(item) {
|
||
const clusterId = $(item).val();
|
||
const resPoolsUrl = $(item).data('poolsurl');
|
||
const networksUrl = $(item).data('networksurl');
|
||
|
||
store.dispatch(changeCluster(clusterId));
|
||
|
||
fetchResourcePools(resPoolsUrl, clusterId);
|
||
fetchNetworks(networksUrl, clusterId);
|
||
}
|
||
|
||
function fetchResourcePools(url, clusterId) {
|
||
// eslint-disable-next-line camelcase
|
||
const data = { cluster_id: $(item).val() };
|
||
const url = $(item).data('url');
|
||
const data = { cluster_id: clusterId };
|
||
|
||
showSpinner();
|
||
const selectbox = $('select[id$="resource_pool"]');
|
||
|
||
selectbox.select2('destroy').empty();
|
||
$.ajax({
|
||
type: 'get',
|
||
url,
|
||
... | ... | |
hideSpinner();
|
||
},
|
||
success(request) {
|
||
selectbox.select2('destroy').empty();
|
||
request.forEach(({ name }) => {
|
||
$('<option>')
|
||
.text(name)
|
||
... | ... | |
},
|
||
});
|
||
}
|
||
|
||
function fetchNetworks(url, clusterId) {
|
||
const $networkOptions = $('select[id$=_network]');
|
||
|
||
showSpinner();
|
||
$.ajax({
|
||
type: 'get',
|
||
url,
|
||
data: { cluster_id: clusterId },
|
||
success(response) {
|
||
$networkOptions.empty();
|
||
|
||
$.each(response.results, (idx, value) => {
|
||
$networkOptions.append(new Option(value.name, value.id, false, false));
|
||
});
|
||
|
||
window.update_interface_table();
|
||
},
|
||
complete() {
|
||
hideSpinner();
|
||
},
|
||
});
|
||
}
|
webpack/assets/javascripts/react_app/components/hosts/storage/vmware/StorageContainer.fixtures.js | ||
---|---|---|
},
|
||
],
|
||
controllers: [{ type: 'VirtualLsiLogicController', key: 1000 }],
|
||
cluster: 'Foreman_Cluster',
|
||
};
|
||
|
||
export const hiddenFieldValue = {
|
||
... | ... | |
},
|
||
],
|
||
controllers: [{ type: 'VirtualLsiLogicController', key: 1000 }],
|
||
cluster: 'Foreman_Cluster',
|
||
};
|
||
|
||
export const state2 = {
|
||
... | ... | |
unitNumber: 0,
|
||
},
|
||
],
|
||
cluster: 'Foreman_Cluster',
|
||
};
|
||
|
||
export const clone = {
|
||
... | ... | |
key: 1001,
|
||
},
|
||
],
|
||
cluster: 'Foreman_Cluster',
|
||
};
|
||
|
||
export const emptyState = {
|
||
... | ... | |
},
|
||
volumes: [],
|
||
controllers: [],
|
||
cluster: null,
|
||
};
|
webpack/assets/javascripts/react_app/components/hosts/storage/vmware/index.js | ||
---|---|---|
import { pick } from 'lodash';
|
||
import React from 'react';
|
||
import { Button } from 'react-bootstrap';
|
||
import { Alert } from 'patternfly-react';
|
||
import { connect } from 'react-redux';
|
||
import PropTypes from 'prop-types';
|
||
|
||
... | ... | |
import { MaxDisksPerController } from './StorageContainer.consts';
|
||
import { translate as __ } from '../../../../../react_app/common/I18n';
|
||
import { noop } from '../../../../common/helpers';
|
||
import AlertBody from '../../../common/Alert/AlertBody';
|
||
import './StorageContainer.scss';
|
||
import { STATUS } from '../../../../constants';
|
||
|
||
... | ... | |
class StorageContainer extends React.Component {
|
||
componentDidMount() {
|
||
const {
|
||
data: { config, controllers, volumes },
|
||
data: { config, controllers, volumes, cluster },
|
||
initController,
|
||
fetchDatastores,
|
||
fetchStoragePods,
|
||
} = this.props;
|
||
|
||
initController(config, controllers, volumes);
|
||
fetchDatastores(config.datastoresUrl);
|
||
fetchStoragePods(config.storagePodsUrl);
|
||
initController(config, cluster, controllers, volumes);
|
||
}
|
||
|
||
getDatastoresStatus() {
|
||
... | ... | |
}
|
||
|
||
render() {
|
||
const { addController, controllers, volumes, config } = this.props;
|
||
const { addController, controllers, volumes, cluster, config } = this.props;
|
||
const paramsScope = config && config.paramsScope;
|
||
const enableAddControllerBtn =
|
||
config && config.addControllerEnabled && !config.vmExists;
|
||
|
||
if (!cluster) {
|
||
return (
|
||
<Alert type="info">
|
||
<AlertBody message={__('Please select a cluster')} />
|
||
</Alert>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="row vmware-storage-container">
|
||
<div className="storage-header">
|
||
... | ... | |
config: PropTypes.object.isRequired,
|
||
controllers: PropTypes.array.isRequired,
|
||
volumes: PropTypes.array.isRequired,
|
||
cluster: PropTypes.string,
|
||
}).isRequired,
|
||
controllers: PropTypes.array.isRequired,
|
||
config: PropTypes.object,
|
||
volumes: PropTypes.array,
|
||
cluster: PropTypes.string,
|
||
datastoresLoading: PropTypes.bool,
|
||
datastores: PropTypes.arrayOf(
|
||
PropTypes.shape({
|
||
... | ... | |
updateDisk: PropTypes.func,
|
||
removeController: PropTypes.func,
|
||
initController: PropTypes.func,
|
||
fetchDatastores: PropTypes.func,
|
||
fetchStoragePods: PropTypes.func,
|
||
};
|
||
|
||
StorageContainer.defaultProps = {
|
||
config: {},
|
||
cluster: '',
|
||
volumes: [],
|
||
datastoresLoading: false,
|
||
storagePodsLoading: false,
|
||
... | ... | |
updateDisk: noop,
|
||
removeController: noop,
|
||
initController: noop,
|
||
fetchDatastores: noop,
|
||
fetchStoragePods: noop,
|
||
};
|
||
|
||
const mapDispatchToProps = state => {
|
||
const {
|
||
controllers,
|
||
config,
|
||
volumes,
|
||
datastores,
|
||
datastoresLoading,
|
||
datastoresError,
|
||
storagePods,
|
||
storagePodsLoading,
|
||
storagePodsError,
|
||
} = state.hosts.storage.vmware;
|
||
|
||
return {
|
||
controllers,
|
||
volumes,
|
||
config,
|
||
datastores,
|
||
datastoresLoading,
|
||
datastoresError,
|
||
storagePods,
|
||
storagePodsLoading,
|
||
storagePodsError,
|
||
};
|
||
};
|
||
const mapStateToProps = state =>
|
||
pick(state.hosts.storage.vmware, [
|
||
'controllers',
|
||
'config',
|
||
'cluster',
|
||
'volumes',
|
||
'datastores',
|
||
'datastoresLoading',
|
||
'datastoresError',
|
||
'storagePods',
|
||
'storagePodsLoading',
|
||
'storagePodsError',
|
||
]);
|
||
|
||
export default connect(
|
||
mapDispatchToProps,
|
||
mapStateToProps,
|
||
VmWareActions
|
||
)(StorageContainer);
|
webpack/assets/javascripts/react_app/redux/actions/bookmarks/bookmarks.test.js | ||
---|---|---|
const expectedURL = '/api/bookmarks?search=controller%3Dhosts&per_page=100';
|
||
|
||
store.dispatch(actions.getBookmarks(url, controller));
|
||
expect(spy).toBeCalledWith(expectedURL);
|
||
expect(spy).toBeCalledWith(expectedURL, {}, {});
|
||
});
|
||
it('should open modal with current search query in action payload', () => {
|
||
const query = 'some search query';
|
webpack/assets/javascripts/react_app/redux/actions/common/index.js | ||
---|---|---|
successAction,
|
||
failedAction,
|
||
url,
|
||
item,
|
||
item = {},
|
||
}) => {
|
||
dispatch({ type: requestAction, payload: item });
|
||
return API.get(url)
|
||
return API.get(url, item.headers || {}, item.params || {})
|
||
.then(({ data }) =>
|
||
dispatch({ type: successAction, payload: { ...item, ...data } })
|
||
)
|
webpack/assets/javascripts/react_app/redux/actions/hosts/storage/vmware.fixtures.js | ||
---|---|---|
import {
|
||
VMWARE_CLUSTER_CHANGE,
|
||
STORAGE_VMWARE_INIT,
|
||
STORAGE_VMWARE_DATASTORES_REQUEST,
|
||
STORAGE_VMWARE_DATASTORES_SUCCESS,
|
||
STORAGE_VMWARE_DATASTORES_FAILURE,
|
||
STORAGE_VMWARE_STORAGEPODS_REQUEST,
|
||
STORAGE_VMWARE_STORAGEPODS_SUCCESS,
|
||
STORAGE_VMWARE_STORAGEPODS_FAILURE,
|
||
} from '../../../consts';
|
||
|
||
import {
|
||
defaultControllerAttributes,
|
||
getDefaultDiskAttributes,
|
||
} from './vmware.consts';
|
||
|
||
export const datastoresUrl = 'test.com/datastores';
|
||
export const storagePodsUrl = 'test.com/storage_pods';
|
||
|
||
const controllerTypes = {
|
||
VirtualBusLogicController: 'Bus Logic Parallel',
|
||
VirtualLsiLogicController: 'LSI Logic Parallel',
|
||
VirtualLsiLogicSASController: 'LSI Logic SAS',
|
||
ParaVirtualSCSIController: 'VMware Paravirtual',
|
||
};
|
||
|
||
const diskModeTypes = {
|
||
persistent: 'Persistent',
|
||
independent_persistent: 'Independent - Persistent',
|
||
independent_nonpersistent: 'Independent - Nonpersistent',
|
||
};
|
||
|
||
export const basicConfig = {
|
||
vmExists: false,
|
||
controllerTypes,
|
||
diskModeTypes,
|
||
paramsScope: 'host[storageParams]',
|
||
datastoresUrl,
|
||
storagePodsUrl,
|
||
};
|
||
|
||
export const initAction = {
|
||
type: STORAGE_VMWARE_INIT,
|
||
payload: {
|
||
config: basicConfig,
|
||
controllers: defaultControllerAttributes,
|
||
volumes: getDefaultDiskAttributes,
|
||
cluster: 'cluster',
|
||
},
|
||
};
|
||
|
||
export const changeClusterAction = {
|
||
type: VMWARE_CLUSTER_CHANGE,
|
||
payload: {
|
||
cluster: 'newCluster',
|
||
},
|
||
};
|
||
|
||
export const state1 = {
|
||
hosts: {
|
||
storage: {
|
||
vmware: {
|
||
config: basicConfig,
|
||
cluster: 'cluster',
|
||
},
|
||
},
|
||
},
|
||
};
|
||
|
||
export const fetchDatastoreParams = {
|
||
requestAction: STORAGE_VMWARE_DATASTORES_REQUEST,
|
||
successAction: STORAGE_VMWARE_DATASTORES_SUCCESS,
|
||
failedAction: STORAGE_VMWARE_DATASTORES_FAILURE,
|
||
url: datastoresUrl,
|
||
item: { params: { cluster_id: 'cluster' } },
|
||
};
|
||
|
||
export const fetchStoragePodsParams = {
|
||
requestAction: STORAGE_VMWARE_STORAGEPODS_REQUEST,
|
||
successAction: STORAGE_VMWARE_STORAGEPODS_SUCCESS,
|
||
failedAction: STORAGE_VMWARE_STORAGEPODS_FAILURE,
|
||
url: storagePodsUrl,
|
||
item: { params: { cluster_id: 'cluster' } },
|
||
};
|
webpack/assets/javascripts/react_app/redux/actions/hosts/storage/vmware.js | ||
---|---|---|
import {
|
||
VMWARE_CLUSTER_CHANGE,
|
||
STORAGE_VMWARE_ADD_CONTROLLER,
|
||
STORAGE_VMWARE_ADD_DISK,
|
||
STORAGE_VMWARE_REMOVE_CONTROLLER,
|
||
... | ... | |
},
|
||
});
|
||
|
||
export const initController = (config, controllers, volumes) => dispatch => {
|
||
export const initController = (
|
||
config,
|
||
cluster,
|
||
controllers,
|
||
volumes
|
||
) => dispatch => {
|
||
dispatch({
|
||
type: STORAGE_VMWARE_INIT,
|
||
payload: {
|
||
config,
|
||
controllers: controllers || defaultControllerAttributes,
|
||
volumes: volumes || getDefaultDiskAttributes,
|
||
cluster,
|
||
},
|
||
});
|
||
dispatch(fetchDatastores(config.datastoresUrl, cluster));
|
||
dispatch(fetchStoragePods(config.storagePodsUrl, cluster));
|
||
};
|
||
|
||
export const fetchDatastores = url => dispatch => {
|
||
ajaxRequestAction({
|
||
dispatch,
|
||
export const changeCluster = newCluster => (dispatch, getState) => {
|
||
const { config } = getState().hosts.storage.vmware;
|
||
|
||
dispatch({
|
||
type: VMWARE_CLUSTER_CHANGE,
|
||
payload: {
|
||
cluster: newCluster,
|
||
},
|
||
});
|
||
dispatch(fetchDatastores(config.datastoresUrl, newCluster));
|
||
dispatch(fetchStoragePods(config.storagePodsUrl, newCluster));
|
||
};
|
||
|
||
const fetchStorages = (url, cluster = null, actions = {}) => dispatch => {
|
||
if (cluster) {
|
||
ajaxRequestAction({
|
||
dispatch,
|
||
...actions,
|
||
url,
|
||
item: { params: { cluster_id: cluster } },
|
||
});
|
||
}
|
||
};
|
||
|
||
export const fetchDatastores = (url, cluster = null) =>
|
||
fetchStorages(url, cluster, {
|
||
requestAction: STORAGE_VMWARE_DATASTORES_REQUEST,
|
||
successAction: STORAGE_VMWARE_DATASTORES_SUCCESS,
|
||
failedAction: STORAGE_VMWARE_DATASTORES_FAILURE,
|
||
url,
|
||
});
|
||
};
|
||
|
||
export const fetchStoragePods = url => dispatch => {
|
||
ajaxRequestAction({
|
||
dispatch,
|
||
export const fetchStoragePods = (url, cluster = null) =>
|
||
fetchStorages(url, cluster, {
|
||
requestAction: STORAGE_VMWARE_STORAGEPODS_REQUEST,
|
||
successAction: STORAGE_VMWARE_STORAGEPODS_SUCCESS,
|
||
failedAction: STORAGE_VMWARE_STORAGEPODS_FAILURE,
|
||
url,
|
||
});
|
||
};
|
||
|
||
export const addController = data => ({
|
||
type: STORAGE_VMWARE_ADD_CONTROLLER,
|
webpack/assets/javascripts/react_app/redux/actions/hosts/storage/vmware.test.js | ||
---|---|---|
import { ajaxRequestAction } from '../../common';
|
||
import {
|
||
datastoresUrl,
|
||
storagePodsUrl,
|
||
basicConfig,
|
||
initAction,
|
||
changeClusterAction,
|
||
state1,
|
||
fetchDatastoreParams,
|
||
fetchStoragePodsParams,
|
||
} from './vmware.fixtures';
|
||
|
||
import * as actions from './vmware';
|
||
|
||
jest.mock('../../common');
|
||
|
||
afterEach(() => {
|
||
ajaxRequestAction.mockReset();
|
||
});
|
||
|
||
describe('vmware storage hosts actions', () => {
|
||
describe('initController', () => {
|
||
it('initializes the container', () => {
|
||
const dispatch = jest.fn();
|
||
const dispatcher = actions.initController(
|
||
basicConfig,
|
||
'cluster',
|
||
null,
|
||
null
|
||
);
|
||
|
||
dispatcher(dispatch);
|
||
|
||
expect(dispatch).toHaveBeenCalledTimes(3);
|
||
expect(dispatch).toHaveBeenCalledWith(initAction);
|
||
});
|
||
});
|
||
|
||
describe('changeCluster', () => {
|
||
it('changes the cluster and refetches the storages', () => {
|
||
const dispatch = jest.fn();
|
||
const dispatcher = actions.changeCluster('newCluster');
|
||
|
||
dispatcher(dispatch, () => state1);
|
||
|
||
expect(dispatch).toHaveBeenCalledTimes(3);
|
||
expect(dispatch).toHaveBeenCalledWith(changeClusterAction);
|
||
});
|
||
});
|
||
|
||
describe.each([
|
||
['fetchDatastores', datastoresUrl, fetchDatastoreParams],
|
||
['fetchStoragePods', storagePodsUrl, fetchStoragePodsParams],
|
||
])('%s', (actionName, url, fetchParams) => {
|
||
it('doesnt make the ajax request when cluster is not set', () => {
|
||
const dispatch = jest.fn();
|
||
const dispatcher = actions[actionName](url, null);
|
||
|
||
dispatcher(dispatch);
|
||
|
||
expect(ajaxRequestAction).not.toBeCalled();
|
||
});
|
||
|
||
it('makes the ajax request to the right url', () => {
|
||
const dispatch = jest.fn();
|
||
const dispatcher = actions[actionName](url, 'cluster');
|
||
|
||
dispatcher(dispatch);
|
||
|
||
expect(ajaxRequestAction).toBeCalledWith({
|
||
dispatch,
|
||
...fetchParams,
|
||
});
|
||
});
|
||
});
|
||
});
|
webpack/assets/javascripts/react_app/redux/consts.js | ||
---|---|---|
export const TOASTS_ADD = 'TOASTS_ADD';
|
||
export const TOASTS_CLEAR = 'TOASTS_CLEAR';
|
||
export const TOASTS_DELETE = 'TOASTS_DELETE';
|
||
export const VMWARE_CLUSTER_CHANGE = 'VMWARE_CLUSTER_CHANGE';
|
||
export const STORAGE_VMWARE_INIT = 'STORAGE_VMWARE_INIT';
|
||
export const STORAGE_VMWARE_ADD_CONTROLLER = 'STORAGE_VMWARE_ADD_CONTROLLER';
|
||
export const STORAGE_VMWARE_ADD_DISK = 'STORAGE_VMWARE_ADD_DISK';
|
webpack/assets/javascripts/react_app/redux/reducers/hosts/storage/__snapshots__/vmware.test.js.snap | ||
---|---|---|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||
|
||
exports[`vmware storage reducer STORAGE_VMWARE_ADD_CONTROLLER adds another controller 1`] = `
|
||
Object {
|
||
"controllers": Array [
|
||
Object {
|
||
"key": 1000,
|
||
"type": "ParaVirtualSCSIController",
|
||
},
|
||
Object {
|
||
"key": 1001,
|
||
"type": "ParaVirtualSCSIController",
|
||
},
|
||
],
|
||
"volumes": Array [
|
||
Object {
|
||
"controllerKey": 1000,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "5124c2d1-339b-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
Object {
|
||
"controllerKey": 1001,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "1547e1c0-309a-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
],
|
||
}
|
||
`;
|
||
|
||
exports[`vmware storage reducer STORAGE_VMWARE_ADD_CONTROLLER adds another controller to fill after removed one 1`] = `
|
||
Object {
|
||
"controllers": Array [
|
||
Object {
|
||
"key": 1001,
|
||
"type": "ParaVirtualSCSIController",
|
||
},
|
||
Object {
|
||
"key": 1000,
|
||
"type": "ParaVirtualSCSIController",
|
||
},
|
||
],
|
||
"volumes": Array [
|
||
Object {
|
||
"controllerKey": 1001,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "5124c2d1-339b-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
Object {
|
||
"controllerKey": 1000,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "1547e1c0-309a-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
],
|
||
}
|
||
`;
|
||
|
||
exports[`vmware storage reducer STORAGE_VMWARE_ADD_CONTROLLER adds controller to initialState 1`] = `
|
||
Object {
|
||
"controllers": Array [
|
||
Object {
|
||
"key": 1000,
|
||
"type": "ParaVirtualSCSIController",
|
||
},
|
||
],
|
||
"volumes": Array [
|
||
Object {
|
||
"controllerKey": 1000,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "1547e1c0-309a-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
],
|
||
}
|
||
`;
|
||
|
||
exports[`vmware storage reducer STORAGE_VMWARE_ADD_DISK adds volume 1`] = `
|
||
Object {
|
||
"controllers": Array [
|
||
Object {
|
||
"key": 1000,
|
||
"type": "ParaVirtualSCSIController",
|
||
},
|
||
],
|
||
"volumes": Array [
|
||
Object {
|
||
"controllerKey": 1000,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "5124c2d1-339b-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
Object {
|
||
"controllerKey": 1000,
|
||
"datastore": "",
|
||
"eagerZero": false,
|
||
"key": "1547e1c0-309a-11e9-98f5-5f761412a4c2",
|
||
"mode": "persistent",
|
||
"name": "Hard disk",
|
||
"sizeGb": 10,
|
||
"storagePod": "",
|
||
"thin": false,
|
||
},
|
||
],
|
||
}
|
||
`;
|
webpack/assets/javascripts/react_app/redux/reducers/hosts/storage/vmware.fixtures.js | ||
---|---|---|
import Immutable from 'seamless-immutable';
|
||
|
||
export const initialState = Immutable({
|
||
controllers: [],
|
||
volumes: [],
|
||
});
|
||
|
||
export const controllerAttributes = {
|
||
type: 'ParaVirtualSCSIController',
|
||
};
|
||
|
||
export const diskAttributes = {
|
||
datastore: '',
|
||
eagerZero: false,
|
||
mode: 'persistent',
|
||
name: 'Hard disk',
|
||
sizeGb: 10,
|
||
storagePod: '',
|
||
thin: false,
|
||
};
|
||
|
||
export const diskKey = '5124c2d1-339b-11e9-98f5-5f761412a4c2';
|
||
|
||
const _generateController = key =>
|
||
Immutable({
|
||
controllers: [
|
||
{
|
||
key,
|
||
type: 'ParaVirtualSCSIController',
|
||
},
|
||
],
|
||
volumes: [
|
||
{
|
||
controllerKey: key,
|
||
datastore: '',
|
||
eagerZero: false,
|
||
key: diskKey,
|
||
mode: 'persistent',
|
||
name: 'Hard disk',
|
||
sizeGb: 10,
|
||
storagePod: '',
|
||
thin: false,
|
||
},
|
||
],
|
||
});
|
||
|
||
export const stateWithController = _generateController(1000);
|
||
export const stateWithRemovedController = _generateController(1001);
|
webpack/assets/javascripts/react_app/redux/reducers/hosts/storage/vmware.js | ||
---|---|---|
import uuidV1 from 'uuid/v1';
|
||
|
||
import {
|
||
VMWARE_CLUSTER_CHANGE,
|
||
STORAGE_VMWARE_ADD_CONTROLLER,
|
||
STORAGE_VMWARE_ADD_DISK,
|
||
STORAGE_VMWARE_REMOVE_DISK,
|
||
... | ... | |
|
||
const initialState = Immutable({
|
||
controllers: [],
|
||
volumes: [],
|
||
});
|
||
|
||
const availableControllerKeys = [1000, 1001, 1002, 1003, 1004];
|
||
... | ... | |
|
||
export default (state = initialState, { type, payload }) => {
|
||
switch (type) {
|
||
case VMWARE_CLUSTER_CHANGE:
|
||
return state.set('cluster', payload.cluster);
|
||
case STORAGE_VMWARE_ADD_CONTROLLER:
|
||
const availableKey = getAvailableKey(state.controllers);
|
||
|
||
... | ... | |
controllers: payload.controllers,
|
||
paramsScope: payload.config.paramsScope,
|
||
datastores: [],
|
||
datastoresLoading: true,
|
||
datastoresLoading: false,
|
||
datastoresError: undefined,
|
||
storagePods: [],
|
||
storagePodsLoading: true,
|
||
storagePodsLoading: false,
|
||
storagePodsError: undefined,
|
||
volumes: payload.volumes.map(volume => ({ ...volume, key: uuidV1() })),
|
||
cluster: payload.cluster,
|
||
};
|
||
return initialState
|
||
.set('config', payload.config)
|
webpack/assets/javascripts/react_app/redux/reducers/hosts/storage/vmware.test.js | ||
---|---|---|
import uuidV1 from 'uuid/v1';
|
||
|
||
import * as types from '../../../consts';
|
||
|
||
import {
|
||
diskKey,
|
||
initialState,
|
||
controllerAttributes,
|
||
diskAttributes,
|
||
stateWithController,
|
||
stateWithRemovedController,
|
||
} from './vmware.fixtures';
|
||
|
||
import reducer from './vmware';
|
||
|
||
jest.mock('uuid/v1');
|
||
uuidV1.mockImplementation(() => '1547e1c0-309a-11e9-98f5-5f761412a4c2');
|
||
|
||
describe('vmware storage reducer', () => {
|
||
it('returns the initial state', () => {
|
||
expect(reducer(undefined, {})).toEqual(initialState);
|
||
});
|
||
|
||
it('handles cluster change', () => {
|
||
expect(
|
||
reducer(initialState, {
|
||
type: types.VMWARE_CLUSTER_CHANGE,
|
||
payload: { cluster: 'testCluster' },
|
||
})
|
||
).toEqual({ ...initialState, cluster: 'testCluster' });
|
||
});
|
||
|
||
describe('STORAGE_VMWARE_ADD_CONTROLLER', () => {
|
||
it('adds controller to initialState', () => {
|
||
expect(
|
||
reducer(initialState, {
|
||
type: types.STORAGE_VMWARE_ADD_CONTROLLER,
|
||
payload: {
|
||
controller: controllerAttributes,
|
||
volume: diskAttributes,
|
||
},
|
||
})
|
||
).toMatchSnapshot();
|
||
});
|
||
|
||
it('adds another controller', () => {
|
||
expect(
|
||
reducer(stateWithController, {
|
||
type: types.STORAGE_VMWARE_ADD_CONTROLLER,
|
||
payload: {
|
||
controller: controllerAttributes,
|
||
volume: diskAttributes,
|
||
},
|
||
})
|
||
).toMatchSnapshot();
|
||
});
|
||
|
||
it('adds another controller to fill after removed one', () => {
|
||
expect(
|
||
reducer(stateWithRemovedController, {
|
||
type: types.STORAGE_VMWARE_ADD_CONTROLLER,
|
||
payload: {
|
||
controller: controllerAttributes,
|
||
volume: diskAttributes,
|
||
},
|
||
})
|
||
).toMatchSnapshot();
|
||
});
|
||
});
|
||
|
||
describe('STORAGE_VMWARE_ADD_DISK', () => {
|
||
it('adds volume', () => {
|
||
expect(
|
||
reducer(stateWithController, {
|
||
type: types.STORAGE_VMWARE_ADD_DISK,
|
||
payload: {
|
||
controllerKey: 1000,
|
||
data: diskAttributes,
|
||
},
|
||
})
|
||
).toMatchSnapshot();
|
||
});
|
||
});
|
||
|
||
describe('STORAGE_VMWARE_UPDATE_DISK', () => {
|
||
it('update volume', () => {
|
||
const result = reducer(stateWithController, {
|
||
type: types.STORAGE_VMWARE_UPDATE_DISK,
|
||
payload: {
|
||
key: diskKey,
|
||
newValues: { sizeGb: 15 },
|
||
},
|
||
});
|
||
expect(result.volumes).toEqual([
|
||
{ ...stateWithController.volumes[0], sizeGb: 15 },
|
||
]);
|
||
});
|
||
});
|
||
});
|
Also available in: Unified diff
Fixes #2113 - VMware filter datastores