diff --git a/api/src/com/cloud/api/commands/UpdateVpnCustomerGatewayCmd.java b/api/src/com/cloud/api/commands/UpdateVpnCustomerGatewayCmd.java index a3a9d8e249f..28f5a908429 100644 --- a/api/src/com/cloud/api/commands/UpdateVpnCustomerGatewayCmd.java +++ b/api/src/com/cloud/api/commands/UpdateVpnCustomerGatewayCmd.java @@ -25,6 +25,7 @@ import com.cloud.api.response.Site2SiteCustomerGatewayResponse; import com.cloud.event.EventTypes; import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.user.Account; +import com.cloud.user.UserContext; @Implementation(description="Update site to site vpn customer gateway", responseObject=Site2SiteCustomerGatewayResponse.class) public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @@ -42,9 +43,6 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @Parameter(name=ApiConstants.GATEWAY, type=CommandType.STRING, required=true, description="public ip address id of the customer gateway") private String gatewayIp; - @Parameter(name=ApiConstants.GUEST_IP, type=CommandType.STRING, required=true, description="guest ip of the customer gateway") - private String guestIp; - @Parameter(name=ApiConstants.CIDR_LIST, type=CommandType.STRING, required=true, description="guest cidr of the customer gateway") private String guestCidrList; @@ -60,6 +58,14 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @Parameter(name=ApiConstants.LIFETIME, type=CommandType.LONG, required=false, description="Lifetime of vpn connection to the customer gateway, in seconds") private Long lifetime; + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="the account associated with the gateway. Must be used with the domainId parameter.") + private String accountName; + + @IdentityMapper(entityTableName="domain") + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="the domain ID associated with the gateway. " + + "If used with the account parameter returns the gateway associated with the account for the specified domain.") + private Long domainId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -80,10 +86,6 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { return guestCidrList; } - public String getGuestIp() { - return guestIp; - } - public String getGatewayIp() { return gatewayIp; } @@ -112,7 +114,11 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; + Long accountId = finalyzeAccountId(accountName, domainId, null, true); + if (accountId == null) { + accountId = UserContext.current().getCaller().getId(); + } + return accountId; } @Override diff --git a/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index f4616d9696d..2cb41d74c8e 100755 --- a/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1038,7 +1038,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa try { Pair sshResult = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, - "/opt/cloud/bin/vpc_portforwarding " + args); + "/opt/cloud/bin/vpc_portforwarding.sh " + args); if (!sshResult.first()) { results[i++] = "Failed"; diff --git a/patches/systemvm/debian/config/opt/cloud/bin/vpc_guestnw.sh b/patches/systemvm/debian/config/opt/cloud/bin/vpc_guestnw.sh index 35394646854..61c5a7ecb02 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/vpc_guestnw.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/vpc_guestnw.sh @@ -115,7 +115,7 @@ desetup_usage() { create_guest_network() { logger -t cloud " $(basename $0): Create network on interface $dev, gateway $gw, network $ip/$mask " # setup ip configuration - sudo ip addr add dev $dev $ip/$mask + sudo ip addr add dev $dev $ip/$mask brd + sudo ip link set $dev up sudo arping -c 3 -I $dev -A -U -s $ip $ip # setup rules to allow dhcp/dns request @@ -143,7 +143,7 @@ destroy_guest_network() { sudo iptables -D INPUT -i $dev -p udp -m udp --dport 53 -j ACCEPT sudo iptables -t mangle -D PREROUTING -i $dev -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark sudo iptables -t nat -A POSTROUTING -s $subnet/$mask -o $dev -j SNAT --to-source $ip - destroy_acl_outbound_chain + destroy_acl_chain desetup_usage desetup_dnsmasq desetup_apache2 diff --git a/patches/systemvm/debian/config/opt/cloud/bin/vpc_ipassoc.sh b/patches/systemvm/debian/config/opt/cloud/bin/vpc_ipassoc.sh index 40c1e4d996a..8ef19e41f45 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/vpc_ipassoc.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/vpc_ipassoc.sh @@ -58,7 +58,7 @@ add_an_ip () { sudo ip link show $ethDev | grep "state DOWN" > /dev/null local old_state=$? - sudo ip addr add dev $ethDev $pubIp/$mask + sudo ip addr add dev $ethDev $pubIp/$mask brd + if [ $old_state -eq 0 ] then sudo ip link set $ethDev up @@ -76,7 +76,7 @@ add_an_ip () { remove_an_ip () { logger -t cloud "$(basename $0):Removing ip $pubIp on interface $ethDev" - local existingIpMask=$(sudo ip addr show dev $ethDev | grep "inet " | awk '{print $2}') + local existingIpMask=$(sudo ip addr show dev $ethDev | grep -v "inet6" | grep "inet " | awk '{print $2}') sudo ip addr del dev $ethDev $pubIp/$mask # reapply IPs in this interface @@ -86,7 +86,7 @@ remove_an_ip () { then continue fi - sudo ip addr add dev $ethDev $ipMask + sudo ip addr add dev $ethDev $ipMask brd + done remove_routing @@ -148,14 +148,14 @@ fi if [ "$Aflag" == "1" ] then - add_an_ip $publicIp && + add_an_ip unlock_exit $? $lock $locked fi if [ "$Dflag" == "1" ] then - remove_an_ip $publicIp && + remove_an_ip unlock_exit $? $lock $locked fi diff --git a/patches/systemvm/debian/config/opt/cloud/bin/vpc_snat.sh b/patches/systemvm/debian/config/opt/cloud/bin/vpc_snat.sh index ff88354dc49..3db9a2d1713 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/vpc_snat.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/vpc_snat.sh @@ -33,8 +33,6 @@ usage() { add_snat() { logger -t cloud "$(basename $0):Added SourceNAT $pubIp on interface $ethDev" vpccidr=$(getVPCcidr) - sudo iptables -D FORWARD -s $vpccidr ! -d $vpccidr -j ACCEPT - sudo iptables -A FORWARD -s $vpccidr ! -d $vpccidr -j ACCEPT sudo iptables -t nat -D POSTROUTING -j SNAT -o $ethDev --to-source $pubIp sudo iptables -t nat -A POSTROUTING -j SNAT -o $ethDev --to-source $pubIp return $? diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 46896c83f99..8c855af2ff2 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -6186,6 +6186,11 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag success = false; } + //release all static nats for the network + if (!_rulesMgr.applyStaticNatForNetwork(networkId, false, caller, true)) { + s_logger.warn("Failed to disable static nats as part of shutdownNetworkRules for network id " + networkId); + success = false; + } // Get all ip addresses, mark as releasing and release them on the backend Network network = getNetwork(networkId); diff --git a/server/src/com/cloud/network/rules/RulesManager.java b/server/src/com/cloud/network/rules/RulesManager.java index a188bd1e776..bff3a115cc1 100644 --- a/server/src/com/cloud/network/rules/RulesManager.java +++ b/server/src/com/cloud/network/rules/RulesManager.java @@ -74,4 +74,13 @@ public interface RulesManager extends RulesService { boolean disableStaticNat(long ipAddressId, Account caller, long callerUserId, boolean releaseIpIfElastic) throws ResourceUnavailableException; + /** + * @param networkId + * @param continueOnError + * @param caller + * @param forRevoke + * @return + */ + boolean applyStaticNatForNetwork(long networkId, boolean continueOnError, Account caller, boolean forRevoke); + } diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 658f4931745..140d0366ebd 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -1293,13 +1293,58 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { @Override public boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { - - List staticNats = new ArrayList(); IpAddress sourceIp = _ipAddressDao.findById(sourceIpId); + + List staticNats = createStaticNatForIp(sourceIp, caller, forRevoke); + if (staticNats != null && !staticNats.isEmpty()) { + try { + if (!_networkMgr.applyStaticNats(staticNats, continueOnError)) { + return false; + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to create static nat rule due to ", ex); + return false; + } + } + + return true; + } + + + @Override + public boolean applyStaticNatForNetwork(long networkId, boolean continueOnError, Account caller, boolean forRevoke) { + List staticNatIps = _ipAddressDao.listStaticNatPublicIps(networkId); + + List staticNats = new ArrayList(); + for (IpAddress staticNatIp : staticNatIps) { + staticNats.addAll(createStaticNatForIp(staticNatIp, caller, forRevoke)); + } + + if (staticNats != null && !staticNats.isEmpty()) { + if (forRevoke) { + s_logger.debug("Found " + staticNats.size() + " static nats to disable for network id " + networkId); + } + try { + if (!_networkMgr.applyStaticNats(staticNats, continueOnError)) { + return false; + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to create static nat rule due to ", ex); + return false; + } + } else { + s_logger.debug("Found 0 static nat rules to apply for network id " + networkId); + } + + return true; + } + + protected List createStaticNatForIp(IpAddress sourceIp, Account caller, boolean forRevoke) { + List staticNats = new ArrayList(); if (!sourceIp.isOneToOneNat()) { - s_logger.debug("Source ip id=" + sourceIpId + " is not one to one nat"); - return true; + s_logger.debug("Source ip id=" + sourceIp + " is not one to one nat"); + return staticNats; } Long networkId = sourceIp.getAssociatedWithNetworkId(); @@ -1330,19 +1375,9 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } StaticNatImpl staticNat = new StaticNatImpl(sourceIp.getAllocatedToAccountId(), sourceIp.getAllocatedInDomainId(), - networkId, sourceIpId, dstIp, forRevoke); + networkId, sourceIp.getId(), dstIp, forRevoke); staticNats.add(staticNat); - - try { - if (!_networkMgr.applyStaticNats(staticNats, continueOnError)) { - return false; - } - } catch (ResourceUnavailableException ex) { - s_logger.warn("Failed to create static nat rule due to ", ex); - return false; - } - - return true; + return staticNats; } @Override diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index fe0a5028d39..f63057c58d0 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -9009,8 +9009,8 @@ div.panel.ui-dialog div.list-view div.fixed-header { .tagger form label.error { position: absolute; color: #FF0000; - left: 42px; - top: 29px; + left: 44px; + top: 28px !important; /*[empty]background-color:;*/ } @@ -9634,6 +9634,229 @@ div.panel.ui-dialog div.list-view div.fixed-header { overflow: auto; } +/*Autoscaler*/ +.ui-dialog div.autoscaler { + overflow: auto; + max-height: 600px; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=securityGroups] { + display: block; + width: 370px; + float: left; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=diskOfferingId] { + display: inline-block; + width: 370px; + float: left; + position: relative; + margin-top: 1px; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=minInstance] { + display: block; + width: 50%; + float: left; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=maxInstance] { + display: inline-block; + width: 50%; + float: left; + left: -30px; + position: relative; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=interval] { + display: block; + width: 50%; + float: left; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=quietTime] { + display: inline-block; + width: 50%; + float: left; + left: -15px; + position: relative; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=snmpCommunity] { + display: block; + width: 50%; + float: left; +} + +.ui-dialog div.autoscaler div.form-container div.form-item[rel=snmpPort] { + display: inline-block; + width: 50%; + float: left; + left: -15px; + position: relative; +} + +.ui-dialog div.autoscaler div.form-container div.value select { + width: 88%; + float: left; +} + +div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container { + height: 55px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container { + height: 55px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy div.multi-edit { + margin-top: 0px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit { + margin-top: 0px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy-title { + color: #0055BB; + margin-left: -650px; + margin-top: 40px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy-title label { + font-size: 13px; + margin-left: 200px; + margin-right: 10px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy-title hr.policy-divider { + border-left: 1px none #38546D; + border-right: 1px none #16222C; + border-top: 1px none #38546D; + margin-bottom: 12px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy-title hr.policy-divider { + border-left: 1px none #38546D; + border-right: 1px none #16222C; + border-top: 1px none #38546D; + margin-bottom: 12px; +} + +div.ui-dialog div.autoscaler div.field-group.bottom-fields hr.policy-divider { + border-left: 1px none #38546D; + border-right: 1px none #16222C; + border-top: 1px none #38546D; + margin-top: 15px; + margin-bottom: -1px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy-title label { + font-size: 13px; + margin-left: 170px; + margin-right: 10px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy-title { + color: #0055BB; + margin-left: -620px; + margin-top: 10px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container div.form-item div.value input[type=text] { + margin-left: 195px; + width: 30%; + margin-top: 1px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container div.form-item div.name { + margin-left: 390px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container div.form-item div.value input[type=text] { + margin-left: 670px; + width: 30%; + margin-top: -16px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container div.form-item div.name { + margin-left: 390px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy div.multi-edit div.data div.data-body div.data-item { + margin-bottom: 0px; + margin-right: 22px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit div.data div.data-body div.data-item { + margin-bottom: 0px; + margin-right: 22px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy div.slide-label { + color: #A5A3A7; + font-size: 14px; + margin-bottom: 3px; + margin-left: 755px; + width: 12px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy div.slide-label { + color: #A5A3A7; + font-size: 14px; + margin-bottom: 3px; + margin-left: 755px; + width: 12px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy div.hide { + background: #FFFFFF url("../images/minus.png") no-repeat 38% 59%; + border: 1px solid #D0D0D0; + border-radius: 9px 9px 9px 9px; + cursor: pointer; + float: right; + height: 15px; + margin: -20px 45px 0 11px; + width: 14px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy div.hide { + background: #FFFFFF url("../images/minus.png") no-repeat 31% 54%; + border: 1px solid #D0D0D0; + border-radius: 9px 9px 9px 9px; + cursor: pointer; + float: right; + height: 15px; + margin: -20px 45px 0 11px; + width: 14px; +} + +div.ui-dialog div.autoscaler div.scale-up-policy div.expand { + background: #FFFFFF url("../images/sprites.png") repeat -541px -499px; + border: 1px solid #D0D0D0; + border-radius: 9px 9px 9px 9px; + cursor: pointer; + float: right; + height: 15px; + margin: -20px 45px 0 11px; + width: 14px; +} + +div.ui-dialog div.autoscaler div.scale-down-policy div.expand { + background: #FFFFFF url("../images/sprites.png") repeat -541px -499px; + border: 1px solid #D0D0D0; + border-radius: 9px 9px 9px 9px; + cursor: pointer; + float: right; + height: 15px; + margin: -20px 45px 0 11px; + width: 14px; +} + +div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-item div.name label { + font-size: 11px; +} + /*Action icons*/ .action.edit .icon { background-position: 1px -1px; diff --git a/ui/images/minus.png b/ui/images/minus.png new file mode 100644 index 00000000000..077db991d49 Binary files /dev/null and b/ui/images/minus.png differ diff --git a/ui/index.jsp b/ui/index.jsp index 3cb2b6c5bcb..c7418b76c33 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -1624,6 +1624,8 @@ + + diff --git a/ui/scripts/autoscaler.js b/ui/scripts/autoscaler.js new file mode 100644 index 00000000000..32f4bfaf7d8 --- /dev/null +++ b/ui/scripts/autoscaler.js @@ -0,0 +1,1038 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +(function($,cloudstack) { + var scaleUpData = []; + var totalScaleUpCondition = 0; + var scaleDownData = []; + var totalScaleDownCondition = 0; + + cloudStack.autoscaler = { + dataProvider: function(args) { + // Reset data + scaleUpData = []; + totalScaleUpCondition = 0; + scaleDownData = []; + totalScaleDownCondition = 0; + + var sampleData = null; + /* + var sampleData = { + templateNames: '58d3f4b2-e847-4f93-993d-1ab1505129b6', //(will set this value to dropdown) + serviceOfferingId: '4aa823f3-27ec-46af-9e07-b023d7a7a6f1', //(will set this value to dropdown) + minInstance: 1, + maxInstance: 10, + scaleUpPolicy: { + id: 12345, + duration: 1000, + conditions: [ + { + id: 1, + counterid: 'cpu', + relationaloperator: "GE", + threshold: 100 + }, + { + id: 2, + counterid: 'memory', + relationaloperator: "LT", + threshold: 200 + } + + ] + }, + scaleDownPolicy: { + id: 6789, + duration: 500, + conditions: [ + { + id: 1, + counterid: 'cpu', + relationaloperator: "LT", + threshold: 30 + }, + { + id: 2, + counterid: 'cpu', + relationaloperator: "LT", + threshold: 50 + } + + ] + }, + interval: 200, + quietTime: 300, + destroyVMgracePeriod: null, + securityGroups: null, // (will set this value to dropdown) + diskOfferingId: 'a21c9aa4-ef7e-41dd-91eb-70b2182816b0', // (will set this value to dropdown) + snmpCommunity: 1, + snmpPort: 225, + + isAdvanced: false // Set this to true if any advanced field data is present + }; + */ + + args.response.success({ data: sampleData }); + }, + + // -- + // Add the following object blocks: + // + // topFields: { } + // bottomFields: { }, + // scaleUpPolicy: { }, + // scaleDownPolicy: { } + // -- + // + forms: { + topFields: { + //** + //** Disabled due to UI issues + //** + // templateCategory: { + // label: 'Template', + // id: 'templatecategory', + // select: function(args) { + // args.response.success({ + // data: [ + // { id: 'all', description: _l('ui.listView.filters.all') }, + // { id: 'featured', description: _l('label.featured') }, + // { id: 'Community', description: _l('label.menu.community.templates') }, + // { id: 'self', description: _l('ui.listView.filters.mine') } + // ] + // }); + // } + // }, + //** + + templateNames: { + label: 'label.template', + id: 'templatename', + select: function(args) { + var templates; + var templateIdMap = {}; + $.ajax({ + url: createURL('listTemplates'), + data: { + templatefilter: 'featured', + zoneid: args.context.networks[0].zoneid + }, + async: false, + success: function(json) { + templates = json.listtemplatesresponse.template; + if (templates == null) + templates = []; + $(templates).each(function() { + templateIdMap[this.id] = 1; + }); + } + }); + + $.ajax({ + url: createURL('listTemplates'), + data: { + templatefilter: 'community', + zoneid: args.context.networks[0].zoneid + }, + async: false, + success: function(json) { + var items = json.listtemplatesresponse.template; + $(items).each(function() { + if(!(this.id in templateIdMap)) { + templates.push(this); + templateIdMap[this.id] = 1; + } + }); + } + }); + + $.ajax({ + url: createURL('listTemplates'), + data: { + templatefilter: 'selfexecutable', + zoneid: args.context.networks[0].zoneid + }, + async: false, + success: function(json) { + var items = json.listtemplatesresponse.template; + $(items).each(function() { + if(!(this.id in templateIdMap)) { + templates.push(this); + templateIdMap[this.id] = 1; + } + }); + } + }); + + args.response.success({ + data: $.map(templates, function(template) { + return { + id: template.id, + description: template.name + }; + }) + }); + } + }, + + serviceOfferingId: { + label: 'label.compute.offering', + select: function(args) { + $.ajax({ + url: createURL("listServiceOfferings&issystem=false"), + dataType: "json", + async: true, + success: function(json) { + var serviceofferings = json.listserviceofferingsresponse.serviceoffering; + args.response.success({ + data: $.map(serviceofferings, function(serviceoffering) { + return { + id: serviceoffering.id, + description: serviceoffering.name + }; + }) + }); + } + }); + } + }, + + minInstance: { + label: 'Min Instances', + defaultValue: '3', + validation: { required: true } + }, + + maxInstance: { + label: 'Max Instances', + defaultValue: '10', + validation: { required: true } + } + }, + + bottomFields: { + isAdvanced: { isBoolean: true, label: 'Show advanced settings' }, + interval: { + label: 'Polling Interval (in sec)', + defaultValue: '30', + validation: { required: true } + }, + + quietTime: { + label: 'Quiet Time (in sec)', + defaultValue: '300', + validation: { required: true } + }, + + destroyVMgracePeriod: { + label: 'Destroy VM Grace Period', + defaultValue: '30', + isHidden:true, + dependsOn:'isAdvanced', + validation: { required: true } + }, + securityGroups: { + label: 'label.menu.security.groups', + isHidden: true, + dependsOn: 'isAdvanced', + select: function(args) { + $.ajax({ + url: createURL("listSecurityGroups&listAll=true"), + dataType: "json", + async: true, + success: function(json) { + var securitygroups = json.listsecuritygroupsresponse.securitygroup; + var items = []; + items.push({id: "", description: ""}); + $(securitygroups).each(function(){ + items.push({id: this.id, description: this.name}); + }); + args.response.success({ data: items }); + } + }); + } + }, + + diskOfferingId: { + label: 'label.menu.disk.offerings', + isHidden: true, + dependsOn: 'isAdvanced', + select: function(args) { + $.ajax({ + url: createURL("listDiskOfferings&listAll=true"), + dataType: "json", + async: true, + success: function(json) { + var diskofferings = json.listdiskofferingsresponse.diskoffering; + var items = []; + items.push({id: "", description: ""}); + $(diskofferings).each(function(){ + items.push({id: this.id, description: this.name}); + }); + args.response.success({ data: items }); + } + }); + } + }, + + snmpCommunity: { + isHidden: true, + dependsOn: 'isAdvanced', + label: 'SNMP Community', + defaultValue: 'public', + validation: { required: true } + }, + + snmpPort: { + isHidden: true, + dependsOn: 'isAdvanced', + label: 'SNMP Port', + defaultValue: '161', + validation: { required: true } + }, + + username: { + isHidden: true, + dependsOn: 'isAdvanced', + label: 'User', + select: function(args) { + var items = []; + if(isAdmin() || isDomainAdmin()) { + $.ajax({ + url: createURL('listUsers'), + data: { + domainid: g_domainid, + account: g_account + }, + success: function(json) { + var users = json.listusersresponse.user; + $(users).each(function(){ + items.push({id: this.id, description: this.username}); + }); + args.response.success({ data: items }); + } + }); + } + else { //regular user doesn't have access to listUers API call. + items.push({id: "", description: ""}); + } + } + } + }, + scaleUpPolicy: { + title: 'ScaleUp Policy', + label: 'SCALE UP POLICY', + noSelect: true, + noHeaderActionsColumn: true, + ignoreEmptyFields: true, + fields: { + 'counterid': { + label: 'Counter', + select: function(args) { + $.ajax({ + url: createURL("listCounters"), + dataType: "json", + async: true, + success: function(json) { + var counters = json.counterresponse.counter; + + args.response.success({ + data: $.map(counters, function(counter) { + return { + name: counter.id, + description: counter.name + }; + }) + }); + } + }); + } + }, + 'relationaloperator': { + label: 'Operator', + select: function(args) { + args.response.success({ + data: [ + { name: 'GT', description: 'greater-than' }, + { name: 'GE', description: 'greater-than or equals to' }, + { name: 'LT', description: 'less-than' }, + { name: 'LE', description: 'less-than or equals to' }, + { name: 'EQ', description: 'equals-to' } + ] + }); + } + }, + 'threshold': { edit: true, label: 'Threshold' }, + 'add-scaleUpcondition': { + label: 'label.add', + addButton: true + } + }, + add: { + label: 'label.add', + action: function(args) { + scaleUpData.push($.extend(args.data, { + index: totalScaleUpCondition + })); + + totalScaleUpCondition++; + args.response.success(); + } + }, + actions: { + destroy: { + label: '', + action: function(args) { + scaleUpData = $.grep(scaleUpData, function(item) { + return item.index != args.context.multiRule[0].index; + }); + totalScaleUpCondition--; + args.response.success(); + } + } + }, + dataProvider: function(args) { + var data = scaleUpData; + var $autoscaler = $('.ui-dialog .autoscaler'); + var initialData = $autoscaler.data('autoscaler-scale-up-data'); + + if ($.isArray(initialData)) { + $(initialData).each(function() { + this.index = totalScaleUpCondition; + totalScaleUpCondition++; + scaleUpData.push(this); + }); + + $autoscaler.data('autoscaler-scale-up-data', null); + } + + args.response.success({ + data: scaleUpData + }); + } + /*actions: { + destroy: { + label: '', + action: function(args) { + $.ajax({ + url: createURL("deleteCondition&id=" + args.context.multiRule[0].counterid), + dataType: 'json', + async: true, + success: function(data) { + var jobId = data.deleteconditionresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobId + } + }); + } + }); + } + } + }, + ignoreEmptyFields: true, + dataProvider: function(args) { + $.ajax({ + url: createURL('listConditions'), + dataType: 'json', + async: true, + success: function(data) { + args.response.success({ + data: $.map( + data.listconditionsresponse.condition ? + data.listconditionsresponse.condition : [], + function(elem) { + return { + counterid: elem.id, + relationaloperator: elem.relationaloperator, + threshold: elem.threshold + }; + } + ) + }); + } + }); + }*/ + }, + + scaleDownPolicy: { + title: 'ScaleDown Policy', + noSelect: true, + noHeaderActionsColumn: true, + ignoreEmptyFields: true, + fields: { + 'counterid': { + label: 'Counter', + select: function(args) { + $.ajax({ + url: createURL("listCounters"), + dataType: "json", + async: true, + success: function(json) { + var counters = json.counterresponse.counter; + + args.response.success({ + data: $.map(counters, function(counter) { + return { + name: counter.id, + description: counter.name + }; + }) + }); + } + }); + } + }, + 'relationaloperator': { + label: 'Operator', + select: function(args) { + args.response.success({ + data: [ + { name: 'GT', description: 'greater-than' }, + { name: 'GE', description: 'greater-than or equals to' }, + { name: 'LT', description: 'less-than' }, + { name: 'LE', description: 'less-than or equals to' }, + { name: 'EQ', description: 'equals-to' } + ] + }); + } + }, + 'threshold': { edit: true, label: 'Threshold'}, + 'add-scaleDowncondition': { + label: 'label.add', + addButton: true + } + }, + add: { + label: 'label.add', + action: function(args) { + scaleDownData.push($.extend(args.data, { + index: totalScaleDownCondition + })); + totalScaleDownCondition++; + args.response.success(); + } + }, + actions: { + destroy: { + label: '', + action: function(args) { + scaleDownData = $.grep(scaleDownData, function(item) { + return item.index != args.context.multiRule[0].index; + }); + totalScaleDownCondition--; + args.response.success(); + } + } + }, + dataProvider: function(args) { + var data = scaleDownData; + var $autoscaler = $('.ui-dialog .autoscaler'); + var initialData = $autoscaler.data('autoscaler-scale-down-data'); + + if ($.isArray(initialData)) { + $(initialData).each(function() { + this.index = totalScaleDownCondition; + totalScaleDownCondition++; + scaleDownData.push(this); + }); + + $autoscaler.data('autoscaler-scale-down-data', null); + } + + args.response.success({ + data: scaleDownData + }); + } + /* + actions: { + destroy: { + label: '', + action: function(args) { + $.ajax({ + url: createURL("deleteCondition&id=" + args.context.multiRule[0].counterid), + dataType: 'json', + async: true, + success: function(data) { + var jobId = data.deleteconditionresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobId + } + }); + } + }); + } + } + }, + ignoreEmptyFields: true, + dataProvider: function(args) { + $.ajax({ + url: createURL('listConditions'), + dataType: 'json', + async: true, + success: function(data) { + args.response.success({ + data: $.map( + data.listconditionsresponse.condition ? + data.listconditionsresponse.condition : [], + function(elem) { + return { + counterid: elem.id, + relationaloperator: elem.relationaloperator, + threshold: elem.threshold + }; + } + ) + }); + } + }); + }*/ + } + }, + + actions: { + add: function(args) { + //validation (begin) ***** + if(isAdmin() || isDomainAdmin()) { //only admin and domain-admin has access to listUers API + var havingApiKeyAndSecretKey = false; + $.ajax({ + url: createURL('listUsers'), + data: { + id: args.data.username + }, + async: false, + success: function(json) { + if(json.listusersresponse.user[0].apikey != null && json.listusersresponse.user[0].secretkey != null) { + havingApiKeyAndSecretKey = true; + } + } + }); + if(havingApiKeyAndSecretKey == false) { + args.response.error('The selected user in advanced settings does not have API key or secret key'); + return; + } + } + + if(isAdmin()) { //only admin has access to listConfigurations API + var hasValidEndpointeUrl = false; + $.ajax({ + url: createURL('listConfigurations'), + data: { + name: 'endpointe.url' + }, + async: false, + success: function(json) { + if(json.listconfigurationsresponse.configuration != null) { + if(json.listconfigurationsresponse.configuration[0].value.indexOf('localhost') == -1) { + hasValidEndpointeUrl = true; + } + } + } + }); + if(hasValidEndpointeUrl == false) { + args.response.error("Global setting endpointe.url has to be set to the Management Server's API end point"); + return; + } + } + //validation (end) ***** + + + var scaleVmProfileResponse = []; + var loadBalancerResponse = []; + var scaleVmGroupResponse = []; + var scaleUpConditionIds = []; + var scaleDownConditionIds = []; + + var scaleUp = function(args){ + var scaleUpConditionIds = []; + $(scaleUpData).each(function(){ + var data = { + counterid: this.counterid, + relationaloperator: this.relationaloperator, + threshold: this.threshold + }; + $.ajax({ + url: createURL('createCondition'), + data: data, + success: function(json) { + var createConditionIntervalID = setInterval(function() { + $.ajax({ + url: createURL("queryAsyncJobResult&jobid=" + json.conditionresponse.jobid), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if(result.jobstatus == 0) { + return; + } + else { + clearInterval(createConditionIntervalID); + if(result.jobstatus == 1) { + var item = json.queryasyncjobresultresponse.jobresult.condition; + scaleUpConditionIds.push(item.id); + if (scaleUpConditionIds.length == scaleUpData.length) { + var data = { + action: 'scaleup', + conditionids: scaleUpConditionIds.join(","), + duration: args.data.scaleUpDuration, + quiettime: args.data.quietTime + }; + $.ajax({ + url: createURL('createAutoScalePolicy'), + data: data, + success: function(json) { + var jobId = json.autoscalepolicyresponse.jobid; + var createAutoScalePolicyInterval = setInterval(function(){ + $.ajax({ + url: createURL("queryAsyncJobResult&jobId="+jobId), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } + else { + clearInterval(createAutoScalePolicyInterval); + if (result.jobstatus == 1) { //AutoScalePolicy successfully created + var item = result.jobresult.autoscalepolicy; + scaleDown($.extend(args, { + scaleUpPolicyResponse: item + })); + } + else if (result.jobstatus == 2) { + args.response.error({ message: _s(result.jobresult.errortext) }); + } + } + } + }); + }, 3000); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + } + } + else if(result.jobstatus == 2) { + args.response.error({ message: _s(result.jobresult.errortext) }); + } + } + } + }); + }, 3000); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + }); + }; + + var scaleDown = function(args){ + var scaleDownConditionIds = []; + $(scaleDownData).each(function(){ + var data = { + counterid: this.counterid, + relationaloperator: this.relationaloperator, + threshold: this.threshold + }; + $.ajax({ + url: createURL('createCondition'), + data: data, + success: function(json) { + var createConditionIntervalID = setInterval(function() { + $.ajax({ + url: createURL("queryAsyncJobResult&jobid=" + json.conditionresponse.jobid), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if(result.jobstatus == 0) { + return; + } + else { + clearInterval(createConditionIntervalID); + if(result.jobstatus == 1) { + var item = json.queryasyncjobresultresponse.jobresult.condition; + scaleDownConditionIds.push(item.id); + if (scaleDownConditionIds.length == scaleDownData.length) { + var data = { + action: 'scaledown', + conditionids: scaleDownConditionIds.join(","), + duration: args.data.scaleDownDuration, + quiettime: args.data.quietTime + }; + $.ajax({ + url: createURL('createAutoScalePolicy'), + data: data, + success: function(json) { + var jobId = json.autoscalepolicyresponse.jobid; + var createAutoScalePolicyInterval = setInterval(function(){ + $.ajax({ + url: createURL("queryAsyncJobResult&jobId="+jobId), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } + else { + clearInterval(createAutoScalePolicyInterval); + if (result.jobstatus == 1) { //AutoScalePolicy successfully created + var item = result.jobresult.autoscalepolicy; + createVmProfile($.extend(args, { + scaleDownPolicyResponse: item + })); + } + else if (result.jobstatus == 2) { + args.response.error({ message: _s(result.jobresult.errortext) }); + } + } + } + }); + }, 3000); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + } + } + else if(result.jobstatus == 2) { + args.response.error({ message: _s(result.jobresult.errortext) }); + } + } + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + }, 3000); + } + }); + }); + }; + + var createVmProfile = function(args){ + var array1 = []; + array1.push("&zoneid=" + args.context.networks[0].zoneid); + array1.push("&templateid=" + args.data.templateNames); + array1.push("&serviceofferingid=" + args.data.serviceOfferingId); + array1.push("&snmpcommunity=" + args.data.snmpCommunity); + array1.push("&snmpport=" + args.data.snmpPort); + array1.push("&destroyvmgraceperiod=" + args.data.destroyVMgracePeriod); + + if(args.data.username != "") + array1.push("&autoscaleuserid=" + args.data.username); + + var array2 = []; + if(args.data.diskOfferingId != "") + array2.push("diskofferingid=" + args.data.diskOfferingId); + if(args.data.securityGroups != ""){ + if(array2.join("") != "") + array2.push("&securitygroupids=" + args.data.securityGroups); + else + array2.push("securitygroupids=" + args.data.securityGroups); + } + array2 = array2.join(""); + if(array2 != "") + array1.push("&otherdeployparams=" + encodeURIComponent(array2)); + + $.ajax({ + url: createURL('createAutoScaleVmProfile' + array1.join("")), + dataType: 'json', + async: true, + success: function(data) { + var jobId = data.autoscalevmprofileresponse.jobid; + var autoscaleVmProfileTimer = setInterval(function(){ + $.ajax({ + url: createURL("queryAsyncJobResult&jobId="+jobId), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } + else { + clearInterval(autoscaleVmProfileTimer); + if (result.jobstatus == 1) { //VM Profile successfully created + scaleVmProfileResponse = result.jobresult.autoscalevmprofile; + loadBalancer(args); + } + else if (result.jobstatus == 2) { + args.response.error({message: _s(result.jobresult.errortext)}); + } + } + } + }); + }, 3000); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + }; + + var loadBalancer = function(args){ + var networkid; + if('vpc' in args.context) { //from VPC section + if(args.data.tier == null) { + cloudStack.dialog.notice({ message: 'Tier is required' }); + return; + } + networkid = args.data.tier; + } + else if('networks' in args.context) { //from Guest Network section + networkid = args.context.networks[0].id; + } + var data = { + algorithm: args.formData.algorithm, + name: args.formData.name, + privateport: args.formData.privateport, + publicport: args.formData.publicport, + openfirewall: false, + networkid: networkid, + publicipid: args.context.ipAddresses[0].id + }; + + $.ajax({ + url: createURL('createLoadBalancerRule'), + dataType: 'json', + data: data, + async: true, + success: function(json) { + var jobId = json.createloadbalancerruleresponse.jobid; + var loadBalancerTimer = setInterval(function(){ + $.ajax({ + url: createURL("queryAsyncJobResult&jobId="+jobId), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } + else { + clearInterval(loadBalancerTimer); + if (result.jobstatus == 1) { //LoadBalancerRule successfully created + loadBalancerResponse = result.jobresult.loadbalancer; + autoScaleVmGroup(args); + } + else if (result.jobstatus == 2) { + args.response.error({message: _s(result.jobresult.errortext)}); + } + } + } + }); + }, 3000); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + }; + + var autoScaleVmGroup = function(args){ + var array1 = []; + array1.push("&lbruleid=" + loadBalancerResponse.id); + array1.push("&minMembers=" + args.data.minInstance); + array1.push("&maxMembers=" + args.data.maxInstance ); + array1.push("&vmprofileid=" + scaleVmProfileResponse.id); + array1.push("&interval=" + args.data.interval); + array1.push("&scaleuppolicyids=" + args.scaleUpPolicyResponse.id); + array1.push("&scaledownpolicyids=" + args.scaleDownPolicyResponse.id ); + $.ajax({ + url: createURL('createAutoScaleVmGroup' + array1.join("")), + dataType: 'json', + async: true, + success: function(json) { + var jobId = json.autoscalevmgroupresponse.jobid; + var scaleVmGroupTimer = setInterval(function(){ + $.ajax({ + url: createURL("queryAsyncJobResult&jobId="+jobId), + dataType: "json", + success: function(json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } + else { + clearInterval(scaleVmGroupTimer); + if (result.jobstatus == 1) { //autoscale Vm group successfully created + scaleVmGroupResponse = result.jobresult.autoscalevmgroup; + args.response.success(); + } + else if (result.jobstatus == 2) { + args.response.error({message: _s(result.jobresult.errortext)}); + } + } + } + }); + }, 3000); + }, + error: function(XMLHttpResponse) { + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + }; + + scaleUp(args); + + //setTimeout(function() { args.response.success(); }, 1000); + //setTimeout(function() { args.response.error('Error!'); }, 1000); + }, + destroy: function(args) { + $.ajax({ + url: createURL('') + }); + } + }, + + dialog: function(args) { + return function(args) { + var context = args.context; + + var $dialog= $('
'); + $dialog.dialog ({ + title: 'AutoScale Configuration Wizard', + closeonEscape: false, + + draggable:true, + width: 825 , + height :600, + buttons: { + 'Cancel': function() { + $(this).dialog("close"); + $('.overlay').remove(); + }, + + + 'Apply': function() { + $(':ui-dialog').remove(); + $('.overlay').remove(); + } + } + }).closest('.ui-dialog').overlay(); + + $("buttons").each(function() { + $(this).attr('style','float: right'); + }); + var $field = $('
').addClass('field username'); + var $input = $('').attr({ name: 'username' }); + var $inputLabel = $('