Skip to content

Commit

Permalink
Order virtual machine
Browse files Browse the repository at this point in the history
  • Loading branch information
fernandes committed Apr 11, 2016
1 parent 5e46683 commit 23b0f5c
Show file tree
Hide file tree
Showing 17 changed files with 419 additions and 320 deletions.
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -48,6 +48,7 @@ gem 'country_select', '~> 2.5'
gem 'devise', '~> 3.5'
gem 'softlayer', '~> 0.0'
gem 'json', '~> 1.8'
gem 'paloma', '~> 5.0'
gem 'reform', '~> 2.1'
gem 'responders', '~> 2.1'
gem 'sidekiq', '~> 4.1'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Expand Up @@ -196,6 +196,7 @@ GEM
nenv (~> 0.1)
shellany (~> 0.0)
orm_adapter (0.5.0)
paloma (5.0.0)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
Expand Down Expand Up @@ -386,6 +387,7 @@ DEPENDENCIES
jquery-rails
json (~> 1.8)
letter_opener
paloma (~> 5.0)
pry
pry-byebug (= 3.2.0)
pry-doc
Expand Down
15 changes: 15 additions & 0 deletions app/assets/javascripts/application.js
Expand Up @@ -13,6 +13,7 @@
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require paloma
//= require bootstrap-sprockets
//= require_tree .

Expand Down Expand Up @@ -43,3 +44,17 @@ $(document).on('click', '.panel-heading span.clickable', function(e){
$this.find('i').removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
}
})

var initializePaloma = function() {
Paloma.start();
}

$(document).on('page:load', function(){
if ($('.js-paloma-hook').data('id') != parseInt(Paloma.engine._request.id)) {
initializePaloma();
}
});

$(document).ready(function(){
initializePaloma();
});
178 changes: 178 additions & 0 deletions app/assets/javascripts/virtual_machines.js
@@ -0,0 +1,178 @@
var VirtualMachinesController = Paloma.controller('VirtualMachines');

VirtualMachinesController.prototype.new = function(){
var paloma = this;
paloma.formPosting = false;

$('#virtual_machine_hourly').change(function() {
if ($(this).is(':checked')) {
// show only hourly items
$('#virtual_machine_bandwidth').val(1800);
$('select option[data-recurringfee]').addClass('hidden');
$('select option[data-hourlyfee]').removeClass('hidden');
} else {
// show only monthly items
$('#virtual_machine_bandwidth').val(50367);
$('select option[data-hourlyfee]').addClass('hidden');
$('select option[data-recurringfee]').removeClass('hidden');
}
mountConflicts(this);
chooseAlternativeOption();
refreshSelectItems();
refreshPriceBox(paloma);
});
// hide options with location dependent true
$("[data-location-dependent-flag=1]").addClass('hidden');

$("[data-resource]").change(function(){
// if we de-select datacenter option
if($('[data-resource="datacenter"]').val() == ""){
// show all base prices
hideDependant();
} else {
// here we have a data center to process
// non dependent are data centers with base prices, they are not present
// on conflict hash, so we need to chack this way
var non_dependent_flag = $(this).find(':selected').data('standard-prices-flag');
if (non_dependent_flag) {
hideDependant();
mountConflicts(this);
} else {
// otherwise we need to process the datacenter based on conflict hash
resource = $(this).data('resource');
hideNonDependant();
mountConflicts(this);
}
}

// after we change the datacenter, we choose the alternative options to replace
// and refresh the conflicts for alternative options
chooseAlternativeOption();
refreshSelectItems();
refreshPriceBox(paloma);
});

// when every select (except for datacenter) is changed
// we need to show the options and hide for that price
$("select[data-resource!=datacenter]").change(function(){
selected = $(this).find("option:checked");
non_dependent_flag = selected.data('location-dependent-flag');
if (!non_dependent_flag) {
hideDependant();
} else {
hideNonDependant();
}
// hide conflicts for each price
hideConflicts(conflictHash.priceToPrice, selected.val());
chooseAlternativeOption();
refreshSelectItems();
refreshPriceBox(paloma);
});

loadDefaultOptions(this);
};

function scrollToPriceBoxAnchor() {
$('#price_box .category-name a').click(function(e) {
e.preventDefault();
anchorHref = this.getAttribute('href');
console.log('going to... ' + anchorHref);
$('html, body').animate({
scrollTop: $(anchorHref).offset().top - 90
}, 200);
});
};

function refreshPriceBox(paloma) {
// bind ajax to form
if (paloma.formPosting === false) {
paloma.formPosting = true;
form = $('form#new_virtual_machine');
formData = form.serialize();
jQuery.ajax({
type: "POST",
url: '/virtual_machines/price_box',
data: formData,
complete: function() {
paloma.formPosting = false;
scrollToPriceBoxAnchor();
}
});
}
}
function chooseAlternativeOption() {
// choose alternative options
$('select:not([data-resource="datacenter"])').each(function() {
option = $(this).find("option:checked");
desc = option.data('item-description');
if (desc !== undefined) {
new_option = $(this).find('option[class!="hidden"][data-item-description="'+desc+'"]');
if (new_option.size() === 0) {
// TODO: improve the error message
alert('removing option ' + desc);
$(this).val('');
}
$(this).val(new_option.val());
}
});
}

function loadDefaultOptions(paloma) {
// set default options after everything is loaded
paloma.params.defaultOptions.forEach(function(id) {
option = $('select option[value='+id+']')
select = option.closest('select');
select.val(option.val());
// select.trigger('change');
});
$('select option[data-hourlyfee]').addClass('hidden');
$('select option[data-recurringfee]').removeClass('hidden');
$('#virtual_machine_hourly').trigger('change');
mountConflicts(this);
chooseAlternativeOption();
refreshSelectItems();
refreshPriceBox(paloma);
}
function hideDependant() {
$("[data-location-dependent-flag=0]").removeClass('hidden');
$("[data-location-dependent-flag=1]").addClass('hidden');
}

function hideNonDependant() {
$("[data-location-dependent-flag=1]").removeClass('hidden');
}

function refreshSelectItems() {
// hide conflicts for other items
$('select[data-resource!=datacenter]').each(function() {
if ($(this).val()) {
hideConflicts(conflictHash.priceToPrice, $(this).val());
hideConflicts(conflictHash.priceToLocation, $(this).val());
}
});
}

function hideConflicts(conflictHash, value){
if(conflictHash.hasOwnProperty(value)){
conflictHash[value].forEach(function(id){
$('option[value="' + id + '"]').addClass('hidden');
});
}
}

function mountConflicts(select){
errors = [];
resource = $(select).data('resource');
$("select[data-resource]").each(function() {
resource = $(this).data('resource');
var datacenter_id = $(this).find(':selected').data('datacenter-id');
if (datacenter_id === undefined) {
datacenter_id = 957095;
}
if (resource === "datacenter") {
hideConflicts(conflictHash.locationToPrice, datacenter_id);
} else {
hideConflicts(conflictHash.priceToPrice, datacenter_id);
}
});
}
20 changes: 18 additions & 2 deletions app/controllers/virtual_machines_controller.rb
Expand Up @@ -6,18 +6,34 @@ def index
def new
@vm = VirtualMachine.new
@form = VirtualMachineForm.new(@vm)
@conflict_hash = StoreHash.generate_hash.camelize_keys.to_json
@conflict_hash = StoreHash.generate_hash
@prices = @vm.components_price
js defaultOptions: @vm.default_options
end

def create
@vm = VirtualMachine.new
@form = VirtualMachineForm.new(@vm)
@conflict_hash = StoreHash.generate_hash
@prices = @vm.components_price
js '#new', defaultOptions: @vm.default_options
if @form.validate(params[:virtual_machine])
@form.save do |hash|
order_template = VirtualMachine.new(hash).template_hash
container = Softlayer::Product::Order.verify_order(order_data: order_template)
Softlayer::Product::Order.place_order(order_data: container)
redirect_to root_path, notice: "Virtual Machine Created Successfully"
end
else
redirect_to login_path, alert: "Invalid Virtual Machine Attributes"
render :new
end
end

def price_box
@vm = VirtualMachine.new(params[:virtual_machine].to_hash)
@prices = @vm.components_price
respond_to do |format|
format.js { render action: "price_box" }
end
end
end
17 changes: 11 additions & 6 deletions app/forms/virtual_machine_form.rb
@@ -1,6 +1,7 @@
class VirtualMachineForm < Reform::Form
property :hostname
property :domain
property :hourly
property :datacenter
property :guest_core
property :ram
Expand All @@ -12,7 +13,6 @@ class VirtualMachineForm < Reform::Form
property :guest_disk4
property :bandwidth
property :port_speed
property :port_speed
property :sec_ip_addresses
property :pri_ipv6_addresses
property :static_ipv6_addresses
Expand All @@ -31,9 +31,14 @@ class VirtualMachineForm < Reform::Form

validates :hostname, presence: true
validates :domain, presence: true
validates :datacenter, presence: true
validates :processor, presence: true
validates :memory, presence: true
validates :operating_system, presence: true
validates :network, presence: true
validates :hourly, presence: true
validates :datacenter, presence: true, numericality: { only_integer: true }
validates :guest_core, presence: true, numericality: { only_integer: true }
validates :ram, presence: true, numericality: { only_integer: true }
validates :os, presence: true, numericality: { only_integer: true }
validates :guest_disk0, presence: true, numericality: { only_integer: true }
validates :bandwidth, presence: true, numericality: { only_integer: true }
validates :port_speed, presence: true, numericality: { only_integer: true }
validates :monitoring, presence: true, numericality: { only_integer: true }
validates :response, presence: true, numericality: { only_integer: true }
end
10 changes: 10 additions & 0 deletions app/helpers/form_helper.rb
@@ -0,0 +1,10 @@
module FormHelper
def error_tag(model, field)
errors = model.errors[field]
if errors.any?
render("shared/form_helper/error_tag", errors: errors)
else
nil
end
end
end
38 changes: 33 additions & 5 deletions app/models/store_hash.rb
Expand Up @@ -4,7 +4,7 @@ class StoreHash
def self.generate_hash
Rails.cache.fetch("softlayer/package-46-conflict-hash", expires_in: 12.hours) do
sh = self.new
sh.conflict_hash
sh.conflict_hash.camelize_keys.to_json
end
end

Expand All @@ -24,6 +24,7 @@ def generate_conflict_hash
process_item_conflicts(item) if item.conflict_count != "0"
process_location_conflicts(item) if item.location_conflict_count != "0"
process_price_location_conflicts(item)
process_capacity_conflicts(item)
end
hash
end
Expand Down Expand Up @@ -62,15 +63,42 @@ def process_price_location_conflicts(item)

# handle non base prices
non_base_prices = item.prices.select { |x| x.location_group_id != nil }
default_price = item.prices.select { |x| x.location_group_id.nil? }.first

non_base_prices.each do |price|
dcs_conflicts = region.datacenters_conflicts_for price.location_group_id
dcs_conflicts.each { |x| conflict_hash[:location_to_price][x] << price.id }
dcs_ids = dcs_ids - dcs_conflicts
dcs_conflicts.each do |x|
# do not add for non dependant datacenters
conflict_hash[:location_to_price][x] << default_price.id if region.standard?(x)
end
end
end

def process_capacity_conflicts(item)
item.prices.each do |price|
unless price.capacity_restriction_type.nil?
capacity_restriction_type = price.capacity_restriction_type
capacity_restriction_type = "(CORE|PRIVATE_CORE)" if price.capacity_restriction_type == "CORE"
# get restricted based on unit
restricted = items.select { |x| x.units.match capacity_restriction_type unless x.units.nil? }
# filter based on provided
items_to_remove = restricted.select do |x|
x.capacity.to_i > price.capacity_restriction_maximum.to_i or
x.capacity.to_i < price.capacity_restriction_minimum.to_i
end

# add conflicts for default prices
base_price = item.prices.select { |x| x.location_group_id.nil? }.first
dcs_ids.each { |x| conflict_hash[:location_to_price][x] << base_price.id }
prices_to_remove = []
items_to_remove.each do |item|
prices_to_remove.concat item.prices.map { |x| x.id }
end

prices_to_remove.each do |priceconf|
conflict_hash[:price_to_price][priceconf] = [] if conflict_hash[:price_to_price][priceconf].nil?
conflict_hash[:price_to_price][priceconf] << price.id
end
end
end
end

def items
Expand Down
6 changes: 6 additions & 0 deletions app/models/store_hash/region.rb
Expand Up @@ -4,6 +4,12 @@ def initialize
generate_dcs_groups_hash
end

def standard?(datacenter_id)
dc = datacenters.select { |x| x.id == datacenter_id }.first
return false if dc.groups.select { |x| x.location_group_type_id == 82 && x.id = 1 }.empty?
true
end

def datacenter_with_id(datacenter_id)
datacenters.select { |x| x.id == datacenter_id }.first
end
Expand Down

0 comments on commit 23b0f5c

Please sign in to comment.