From 392d55dead6cf7825e96a4c9c25acc63bb40a651 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 7 May 2013 12:46:37 -0700 Subject: [PATCH 001/412] Add base VPC chart --- ui/modules/modules.js | 1 + ui/modules/vpc/vpc.css | 100 +++++++++++++++++++++++++++++++++++++++++ ui/modules/vpc/vpc.js | 70 +++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 ui/modules/vpc/vpc.css create mode 100644 ui/modules/vpc/vpc.js diff --git a/ui/modules/modules.js b/ui/modules/modules.js index 490749ff085..ee553fd1af2 100644 --- a/ui/modules/modules.js +++ b/ui/modules/modules.js @@ -16,5 +16,6 @@ // under the License. (function($, cloudStack) { cloudStack.modules = [ + 'vpc' ]; }(jQuery, cloudStack)); diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css new file mode 100644 index 00000000000..2022c22ff8b --- /dev/null +++ b/ui/modules/vpc/vpc.css @@ -0,0 +1,100 @@ +/*[fmt]1C20-1C0D-E*/ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with 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. +*/ +.vpc-network-chart { +} + +.vpc-network-chart .tiers { +} + +.vpc-network-chart .tier-item { + border: 1px solid #477FB4; + overflow: hidden; + width: 326px; + height: 182px; + margin: 18px; + /*+border-radius:4px;*/ + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + background: #8DB1D3; +} + +.vpc-network-chart .tier-item .header { + padding: 7px 0 6px; + /*+box-shadow:inset 0px 1px 1px #FFFFFF;*/ + -moz-box-shadow: inset 0px 1px 1px #FFFFFF; + -webkit-box-shadow: inset 0px 1px 1px #FFFFFF; + -o-box-shadow: inset 0px 1px 1px #FFFFFF; + box-shadow: inset 0px 1px 1px #FFFFFF; + background: #69839D; + border-bottom: 1px solid #40639E; +} + +.vpc-network-chart .tier-item .header .title { + margin-left: 9px; + max-width: 268px; + overflow: hidden; +} + +.vpc-network-chart .tier-item .header .title span { + font-size: 20px; + color: #FFFFFF; + /*+text-shadow:0px 1px 1px #000000;*/ + -moz-text-shadow: 0px 1px 1px #000000; + -webkit-text-shadow: 0px 1px 1px #000000; + -o-text-shadow: 0px 1px 1px #000000; + text-shadow: 0px 1px 1px #000000; +} + +.vpc-network-chart .tier-item .content { + width: 100%; + height: 83%; + /*+box-shadow:inset 0px 1px 1px #FFFFFF;*/ + -moz-box-shadow: inset 0px 1px 1px #FFFFFF; + -webkit-box-shadow: inset 0px 1px 1px #FFFFFF; + -o-box-shadow: inset 0px 1px 1px #FFFFFF; + box-shadow: inset 0px 1px 1px #FFFFFF; + border-bottom: 1px solid #8DB1D3; +} + +.vpc-network-chart .tier-item .content .dashboard { + height: 117px; +} + +.vpc-network-chart .tier-item .content .info { + padding: 7px 0 10px 9px; +} + +.vpc-network-chart .tier-item .content .info .cidr-label { + font-size: 10px; + color: #1860A7; +} + +.vpc-network-chart .tier-item .content .info .cidr { + color: #364553; + font-size: 10px; + /*+text-shadow:0px 1px #C7D8E9;*/ + -moz-text-shadow: 0px 1px #C7D8E9; + -webkit-text-shadow: 0px 1px #C7D8E9; + -o-text-shadow: 0px 1px #C7D8E9; + text-shadow: 0px 1px #C7D8E9; +} + diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js new file mode 100644 index 00000000000..065a91de18e --- /dev/null +++ b/ui/modules/vpc/vpc.js @@ -0,0 +1,70 @@ +(function($, cloudStack) { + var elems = { + chart: { + tier: function(args) { + var tier = args.tier; + var $tier = $('
').addClass('tier-item'); + var $header = $('
').addClass('header'); + var $title = $('
').addClass('title').append($('')); + var $content = $('
').addClass('content'); + var $dashboard = $('
').addClass('dashboard'); + var $info = $('
').addClass('info'); + var $cidrLabel = $('').addClass('cidr-label'); + var $cidr = $('').addClass('cidr'); + + $cidrLabel.html('CIDR: '); + $cidr.html(tier.cidr); + $title.find('span').html(tier.displayname ? tier.displayname : tier.name); + $header.append($title); + $info.append($cidrLabel, $cidr); + $content.append($dashboard, $info); + $tier.append($header, $content); + + return $tier; + } + } + }; + + cloudStack.modules.vpc = function(module) { + var vpc = cloudStack.vpc; + var vpcSection = cloudStack.sections.network.sections.vpc; + var listConfigureAction = vpcSection.listView.actions.configureVpc.action; + var detailsConfigureAction = vpcSection.listView.detailView.actions.configureVpc.action; + + var vpcChart = function(args) { + var context = args.context; + var vpcItem = context.vpc[0]; + var $chart = $('
').addClass('vpc-network-chart'); + var $tiers = $('
').addClass('tiers'); + + $tiers.appendTo($chart); + + // Get tiers + vpc.tiers.dataProvider({ + context: context, + response: { + success: function(data) { + var tiers = data.tiers; + + $(tiers).map(function(index, tier) { + var $tier = elems.chart.tier({ tier: tier }); + + $tier.appendTo($tiers); + }); + } + } + }); + + $('#browser .container').cloudBrowser('addPanel', { + title: vpcItem.displaytext ? vpcItem.displaytext : vpcItem.name, + maximizeIfSelected: true, + complete: function($panel) { + $chart.appendTo($panel); + } + }); + }; + + listConfigureAction.custom = vpcChart; + detailsConfigureAction.custom = vpcChart; + }; +}(jQuery, cloudStack)); From fbefeea46109b138a6a5c4ae800b483acec89c98 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 7 May 2013 15:27:06 -0700 Subject: [PATCH 002/412] Add tier mini-dashboard --- ui/modules/vpc/vpc.css | 57 +++++++++++++++++++++++++++++- ui/modules/vpc/vpc.js | 80 +++++++++++++++++++++++++++++++----------- 2 files changed, 115 insertions(+), 22 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 2022c22ff8b..f84cbf0aa8e 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -21,6 +21,8 @@ } .vpc-network-chart .tiers { + width: 362px; + float: right; } .vpc-network-chart .tier-item { @@ -79,8 +81,61 @@ height: 117px; } +.vpc-network-chart .tier-item .content .dashboard-item { + width: 145px; + height: 54px; + margin: 7px 9px 0; + background: #C1E0FE; + /*+border-radius:4px;*/ + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + float: left; + cursor: pointer; +} + +.vpc-network-chart .tier-item .content .dashboard-item:hover { + background-color: #DBEDFE; + /*+box-shadow:inset 0px 1px 2px #000000;*/ + -moz-box-shadow: inset 0px 1px 2px #000000; + -webkit-box-shadow: inset 0px 1px 2px #000000; + -o-box-shadow: inset 0px 1px 2px #000000; + box-shadow: inset 0px 1px 2px #000000; +} + +.vpc-network-chart .tier-item .content .dashboard-item .total { + font-size: 28px; + /*+placement:shift 7px 5px;*/ + position: relative; + left: 7px; + top: 5px; + color: #145CA1; + font-weight: 100; + /*+text-shadow:0px 1px 1px #FFFFFF;*/ + -moz-text-shadow: 0px 1px 1px #FFFFFF; + -webkit-text-shadow: 0px 1px 1px #FFFFFF; + -o-text-shadow: 0px 1px 1px #FFFFFF; + text-shadow: 0px 1px 1px #FFFFFF; +} + +.vpc-network-chart .tier-item .content .dashboard-item .name { + font-size: 11px; + text-transform: uppercase; + color: #0861B7; + /*+placement:shift 8px 7px;*/ + position: relative; + left: 8px; + top: 7px; + /*+text-shadow:0px 1px 1px #FFFFFF;*/ + -moz-text-shadow: 0px 1px 1px #FFFFFF; + -webkit-text-shadow: 0px 1px 1px #FFFFFF; + -o-text-shadow: 0px 1px 1px #FFFFFF; + text-shadow: 0px 1px 1px #FFFFFF; +} + .vpc-network-chart .tier-item .content .info { - padding: 7px 0 10px 9px; + padding: 9px 0 10px 10px; } .vpc-network-chart .tier-item .content .info .cidr-label { diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 065a91de18e..68f14191e78 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -1,27 +1,45 @@ (function($, cloudStack) { var elems = { - chart: { - tier: function(args) { - var tier = args.tier; - var $tier = $('
').addClass('tier-item'); - var $header = $('
').addClass('header'); - var $title = $('
').addClass('title').append($('')); - var $content = $('
').addClass('content'); - var $dashboard = $('
').addClass('dashboard'); - var $info = $('
').addClass('info'); - var $cidrLabel = $('').addClass('cidr-label'); - var $cidr = $('').addClass('cidr'); + tier: function(args) { + var tier = args.tier; + var dashboardItems = args.dashboardItems; + var $tier = $('
').addClass('tier-item'); + var $header = $('
').addClass('header'); + var $title = $('
').addClass('title').append($('')); + var $content = $('
').addClass('content'); + var $dashboard = elems.dashboard({ + dashboardItems: dashboardItems + }); + var $info = $('
').addClass('info'); + var $cidrLabel = $('').addClass('cidr-label'); + var $cidr = $('').addClass('cidr'); - $cidrLabel.html('CIDR: '); - $cidr.html(tier.cidr); - $title.find('span').html(tier.displayname ? tier.displayname : tier.name); - $header.append($title); - $info.append($cidrLabel, $cidr); - $content.append($dashboard, $info); - $tier.append($header, $content); + $cidrLabel.html('CIDR: '); + $cidr.html(tier.cidr); + $title.find('span').html(tier.displayname ? tier.displayname : tier.name); + $header.append($title); + $info.append($cidrLabel, $cidr); + $content.append($dashboard, $info); + $tier.append($header, $content); - return $tier; - } + return $tier; + }, + + dashboard: function(args) { + var $dashboard = $('
').addClass('dashboard'); + + $(args.dashboardItems).map(function(index, dashboardItem) { + var $dashboardItem = $('
').addClass('dashboard-item'); + var $name = $('
').addClass('name').append($('')); + var $total = $('
').addClass('total').append($('')); + + $name.find('span').html(dashboardItem.name); + $total.find('span').html(dashboardItem.total); + $dashboardItem.append($total, $name); + $dashboardItem.appendTo($dashboard); + }); + + return $dashboard; } }; @@ -47,7 +65,27 @@ var tiers = data.tiers; $(tiers).map(function(index, tier) { - var $tier = elems.chart.tier({ tier: tier }); + var $tier = elems.tier({ + tier: tier, + dashboardItems: [ + { + name: 'Load balancers', + total: 5 + }, + { + name: 'Port forwarders', + total: 4 + }, + { + name: 'Static NATs', + total: 3 + }, + { + name: 'Virtual Machines', + total: 300 + } + ] + }); $tier.appendTo($tiers); }); From 3c9632814ad21b741343e1252438ab96d860df65 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 7 May 2013 15:44:28 -0700 Subject: [PATCH 003/412] UI plugin API: Support IE-compatible CSS loading If browser is IE < 9, use document.createStyleSheet to properly load plugin's CSS dynamically. --- ui/scripts/plugins.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ui/scripts/plugins.js b/ui/scripts/plugins.js index 122f4a03491..6a886ba0ae6 100644 --- a/ui/scripts/plugins.js +++ b/ui/scripts/plugins.js @@ -20,15 +20,20 @@ } var loadCSS = function(path) { - var $link = $(''); + if (document.createStyleSheet) { + // IE-compatible CSS loading + document.createStyleSheet(path); + } else { + var $link = $(''); - $link.attr({ - rel: 'stylesheet', - type: 'text/css', - href: path - }); + $link.attr({ + rel: 'stylesheet', + type: 'text/css', + href: path + }); - $('head').append($link); + $('html head').append($link); + } }; $.extend(cloudStack.pluginAPI, { From 9c68b09376df5c98c4e5001840acd25f61a1c3a0 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 7 May 2013 15:44:41 -0700 Subject: [PATCH 004/412] Add tier placeholder box --- ui/modules/vpc/vpc.css | 45 ++++++++++++++++++++++++++++++++++++++++++ ui/modules/vpc/vpc.js | 11 +++++++++++ 2 files changed, 56 insertions(+) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index f84cbf0aa8e..d5ad6b21c25 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -21,6 +21,7 @@ } .vpc-network-chart .tiers { + margin: 40px 46px 0 0; width: 362px; float: right; } @@ -153,3 +154,47 @@ text-shadow: 0px 1px #C7D8E9; } +.vpc-network-chart .tier-placeholder { + cursor: pointer; + background: #EFEFEF; + border: 4px dotted #B1B1B1; + /*+border-radius:8px;*/ + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; + width: 325px; + text-align: center; + padding: 57px 0 55px; + margin: 0 0 0 18px; +} + +.vpc-network-chart .tier-placeholder:hover { + background: #DCDCDC; + /*+border-radius:8px;*/ + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; + /*+box-shadow:inset 0px 1px 4px #000000;*/ + -moz-box-shadow: inset 0px 1px 4px #000000; + -webkit-box-shadow: inset 0px 1px 4px #000000; + -o-box-shadow: inset 0px 1px 4px #000000; + box-shadow: inset 0px 1px 4px #000000; + /*+text-shadow:0px 1px #FFFFFF;*/ + -moz-text-shadow: 0px 1px #FFFFFF; + -webkit-text-shadow: 0px 1px #FFFFFF; + -o-text-shadow: 0px 1px #FFFFFF; + text-shadow: 0px 1px #FFFFFF; +} + +.vpc-network-chart .tier-placeholder:hover span { + color: #535353; +} + +.vpc-network-chart .tier-placeholder span { + color: #AFAFAF; + font-size: 24px; + font-weight: 200; +} + diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 68f14191e78..ee9e1485f1a 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -25,6 +25,14 @@ return $tier; }, + tierPlaceholder: function() { + var $placeholder = $('
').addClass('tier-placeholder'); + + $placeholder.append($('').append('Create network')); + + return $placeholder; + }, + dashboard: function(args) { var $dashboard = $('
').addClass('dashboard'); @@ -89,6 +97,9 @@ $tier.appendTo($tiers); }); + + // Add placeholder tier + $tiers.append(elems.tierPlaceholder()); } } }); From f9b843d7449a1466ebb0f6f86e0c8b567206a713 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 7 May 2013 16:11:45 -0700 Subject: [PATCH 005/412] Add tier 'view all' icon --- ui/images/sprites.png | Bin 192407 -> 192511 bytes ui/modules/vpc/vpc.css | 40 +++++++++++++++++++++++++++++++++++++--- ui/modules/vpc/vpc.js | 3 ++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ui/images/sprites.png b/ui/images/sprites.png index 132588d10049a3d4058d35ba137015ac988fa27d..2c30b581cc4c7c338a39b128cd0b796e98da2968 100644 GIT binary patch delta 157782 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�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 IM6N<$g3Y@r=Kufz delta 157239 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').addClass('detail-link'); var $info = $('

').addClass('info'); var $cidrLabel = $('').addClass('cidr-label'); var $cidr = $('').addClass('cidr'); @@ -17,7 +18,7 @@ $cidrLabel.html('CIDR: '); $cidr.html(tier.cidr); $title.find('span').html(tier.displayname ? tier.displayname : tier.name); - $header.append($title); + $header.append($title, $detailLink); $info.append($cidrLabel, $cidr); $content.append($dashboard, $info); $tier.append($header, $content); From 22a546628910a46b6bdbaee7f63250d30e20e966 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 7 May 2013 16:27:03 -0700 Subject: [PATCH 006/412] WIP: VPC router chart item --- ui/modules/vpc/vpc.css | 46 ++++++++++++++++++++++++++++++++++++++++++ ui/modules/vpc/vpc.js | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 95f01111357..f6ede66bd9b 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -18,6 +18,9 @@ * under the License. */ .vpc-network-chart { + width: 100%; + height: 100%; + overflow: auto; } .vpc-network-chart .tiers { @@ -232,3 +235,46 @@ font-weight: 200; } +.vpc-network-chart .tier-item.router { + width: 258px; + height: 218px; + background: #BDBDBD; + border: 1px solid #808080; + float: left; + /*+placement:shift 10px 176px;*/ + position: relative; + left: 10px; + top: 176px; +} + +.vpc-network-chart .tier-item.router .header { + background: #908F8F; + padding: 15px 0 14px; + border-bottom: 1px solid #808080; +} + +.vpc-network-chart .tier-item.router .header .title { + width: 212px; +} + +.vpc-network-chart .tier-item.router .header .title span { + padding: 0 0 0 32px; +} + +.vpc-network-chart .tier-item.router .dashboard-item { + width: 100px; + /*[empty]margin:;*/ + padding: 0px 2px 0px 6px; + height: 73px; + background: #A7A7A7; +} + +.vpc-network-chart .tier-item.router .dashboard-item span { + color: #FFFFFF; + /*+text-shadow:0px 1px #000000;*/ + -moz-text-shadow: 0px 1px #000000; + -webkit-text-shadow: 0px 1px #000000; + -o-text-shadow: 0px 1px #000000; + text-shadow: 0px 1px #000000; +} + diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 69cf795252b..4a3d6493aa5 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -26,6 +26,19 @@ return $tier; }, + router: function(args) { + var $router = elems.tier({ + tier: { + name: 'Router', + }, + dashboardItems: args.dashboardItems + }).addClass('router'); + + $router.find('.info').remove(); + + return $router; + }, + tierPlaceholder: function() { var $placeholder = $('
').addClass('tier-placeholder'); @@ -104,6 +117,28 @@ } } }); + + // Router + $router = elems.router({ + dashboardItems: [ + { + name: 'Private gateways', + total: 1 + }, + { + name: 'Public IP addresses', + total: 2 + }, + { + name: 'Site-to-site VPNs', + total: 3 + }, + { + name: 'Network ACL lists', + total: 2 + } + ] + }).appendTo($chart); $('#browser .container').cloudBrowser('addPanel', { title: vpcItem.displaytext ? vpcItem.displaytext : vpcItem.name, From 34774c0c30d52ecc7828ffe6252f57eced75400c Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 8 May 2013 14:03:51 -0700 Subject: [PATCH 007/412] WIP --- ui/images/sprites.png | Bin 192511 -> 194177 bytes ui/modules/vpc/vpc.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/images/sprites.png b/ui/images/sprites.png index 2c30b581cc4c7c338a39b128cd0b796e98da2968..156df1ae30d6f813272804c48642f5af8da00af5 100644 GIT binary patch delta 94752 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%0D)Rr6|U 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 Ce!a^8 delta 92646 zcmagGby!qi)IWNLmQD$2m6lK%gdzNb$N&-ol7a{b2#P~UoS{JkR8qQz5Ew!M=@g_p zlm_YU9_|6(-+Q0?$G!J?e0b!<-g~XR*7|(bTKnL$PI56tQdCEbGtcMOKwREH46Awv z29VW%R(M0L>>IK>xU0^cljFwvE{Lw+-yghA`^W6EYa*tJmV=l2qt*1FgJ zcALz&dxN_B>OJ;}6Td??WA!DP>@{8Wk%*Bhc}?l?pMqRXC9lPg!PoDm%u~dL{y%c93_g9Lq~aK-YB^icRA2KdN_yo$yf}-(5_%& zORK`~Q&TtLx@Dj6={RO7$!|ES%F4=-(Ugkdpz-Pcu0P%R`VApNTAjG$ivMVE_FuMiwSrhN0s9C-gbHUpSM zejod{gU`!fTD!u)(LwxHvaAP>S=mkCZ4G*gS;T3_Bvf_;>}RhcICNC(kPIkkaMxhN z7k0TJT=!VuT_j`gdcO!N^5VPQuK$*nIY-#Q%LpKey71nT^eXlG!yU7T05L!m_46C$AdA1IIl%=qe%$T~3u z>^3LSTKxo>EO_uS3KaN~m;l~I}tn1K~x&R0YfP8TrA%F{U|J(6teKX zy++`2e?+^At~N6ZPt0EF=SZ%`hpmHuL)}s8)g9G;op{=H^`Op&#GQ_3+T{pNd7FziRmvj%z4@T=)05(5RF)E4AvbqOQUGi}<=$&J$N%SA9YD_M zqfA=@|5uGEjGA!uKJ>J5QFN2wzq^HNLFT^Cp8@gLc&?=Zx4-jNTrcWXhuZ1PmPa}#+|K>Q@V|_wl z60yReF!orJP*@w-)-uh(4o%2Zq4^&t;3SSRf*IqcE=z8-5Nz?FF3vsEw*JkU9-1l;CqrVgQ+@>z5r4!>iBF*f-5j9(UWo0|>WHAL zzjr1nYC(nQIXL|D{zcw^}Rc zxk~8fzEyNhf9El?e~@1JmH6~v%>+PQ`KV3$=YX@EiP)WL5s%5MT~(&x_#XDGT=t)+ zb6xiT%&TvY7?mvZ?(pZ;K8jw$VgEP5zz3OkbZ`PV2#KEl&|H@v^%>^WF16`PLRecj zD<)wC2;vo`?ugGi)d_GlvIcKg5Q_92JXH@=A_Hy!vb(>8KtX_u;D9 zcn4S~%b6ZY3#b(1mkOg0d6;6!)fFw>C=^uY<_2|*PTl}CXL8fk; zm@}7l?x5bIn8HK~_~K&@PA0vn^ZkRQ(TUiD1mFAa#zu~{C>7U@|Mbl+T;#24aEG!tP?Yq|jfp(;Kti5Oc-KvND{^TDwq3}7WHdl&$7BIOl}!L1JQ-3<>y z1qU+#t;gD(SP(LoWw zCfWJ&_GK`93pNVYoxK4hZ`2n67+CLHW>-sGH`@9OjA_kDD=A9nqpt-%gd;X9KQb_w z4KtzYEkyx?B4FFiv)OgpJ-O6jtZdcP`*?m88fGU{ztGg{2JZCWBb0sEO~{F*UwO^l zm?9bw{nYlncWMeWcGWgA3SGrik=NAJlwZCF9_B+cNfFfZ#=^b;ww>~>lvI6f#c;I{ zV?kF%S8X0zEgssBYUtkK;W7Zgoz|_Qyn5QlPhB0-1}SPT4CeGfs7c_GIUrb)7sE_7%<-L8KrXljuF~9g zgI{p42DvzFK*Z$z6%&B;1xXUuBL4)iP1KNugxP|9q=TD=UJ|Z&{Q6=@zb(rVba1BA zf$&9Us=nt$sC+y`5oM8 z+G@&sAV9s#M%5R!J+YYVv0*bt31z&ytmmYP#k0eV1qzhhtlgeR`|Q z%||NA$~5eNDekl>>-)&Oi4flmRLC{REV$HwOc6+mDVX}}T#lqx4i>_kXjEV{mv15bfgfU{OSfJMlZdI! za6P%=w&S?d@!-3g5seH-e<#fF>*Y|QtZv=VFh#&GCE=LZX9uv%*OHrV?N`SCgA0mj z(1aF1;;U;PuR~I)3XQ+Wa-etef5mwh`C z@*aZvWhBSK&r9M7$?%K_B1D&S3C>U?eh&M0(Gcf3jc{0p?bXPVwe*&zVYZz;(wF;a z0WeQJ5)o?o7YV`|*%IcUcOci+|I+;()B3 zDK_HYV{;8U+HYuyu2!ybvnmu#Z6_kSpoXTlAA3bOy=`-)q0x6g%CiZPh^}5Fs|(YO zaLb!X8kV~!u_J(!n{se45g^LyTfALmHZ`?bxj{cR_9vD02l!g%I6+sk$dPU5czoi# z=Dvx!HMD`mMYLjN99uN{)e8$uJxIp_Oo5UpYfzSm-@wx6!koY6YOJ-Uh7 z#CUJfQ-C{#;laV=m4i-CsUgABmjl)#AjLqYaQX9Jko^DsMbza7^`&n5IAjlg-E7_W zh&A;RWA*q;g<4ANZ~GW? z8NvS>+^*d$W?i$}tfwoi z*uGPDx-YabZ^nA*l;LJaRlHwzOOzqsf7y&aWHdJ?p;-z*iSHGWBK2>c4w^S@!BdHN&WH|+nvSg^AH~-zv<9oh+ z^2kziM3g8{o-0KKiHS+vF|Le) zedH7Txpo0Lgy@hc?8ruvxz@7b^X(i*`@}8HI0#^GUS*;znKA(6EfO*v(V>zB@sEai_OA{>PTZ>YETM?>{yfxp3d` z#gCJ+?#>*C5!8#+QakTcFrDsRfdP*cMSX!766t8-+0IL(!WrqJ0(lwaf8iV70-t%l zoasl~ytMtYCj&MCl7Y0eSEMYm!xnf|U}xjj^I1nTK^ zC`pWfLdOS2otLdp*pkJ)3+s;qHz0VK+x=4+NFXXEp;^IV359sZhS*t{J3_rZxB7R9mPb5Voe)$z^VVbBiHv~7 z&Clp5XpK^^H%tsn5tq?M^yLO@%de)&#Vc*K-X}s(Zk~2<5F8Q#`acQ%gQdu1vT3&W z0gY@IJ7>G);-%k>znQmD$7QtvszLv-G|3I(EO%vrX`chH)7svN-|i!c_$0k|En6}f zZl!OR-Uy=n6*k833VN#FtMPTBPPzT~QNd#JJK`i378>leZ#e%?&}INffoa?TPpl78 zsOGI*$=8)Ey~X4XK(zrKcZnW0d>(uKd4=A4#VV{{dg(nOGx+eopnmCwtt1fMU&0#X zC!$s4_b#X|k?ax){|^FRqRRTy&MUeFVfd63TNQ=A-tkNXgcviHAR%PJm!9dT$Jk@9 z2i$^2!$Oh7jj-NX&Sfek=)$Q>zGk~~Mt*KxW61^d=Fktqm!f#|KQX&S2EZ{uekh+` z*O+#>jcz&^#Z$i|2%;i5>|rwjamNw=3lx{On*s47XrVt$f+3D`d5;TJ1U#_}jvZqx z7wawo{`yugIe$K`Z@z@XIYbdBa_4v#*f<~Uzx`;#w8)8rf+#F^e|6ZR=>)lXF}Dex zru4|}x8g()QS}wOuYFPpUp*A8IeB>*23H`x$z=(Mv!1qaO^XyQq)Y~DPM+%lYq+X& zK9_`Po1lW`2s9D-bO=#1b)lxq8%~qkQCHCS8u7n~I6(~|;y>icJ8CUO*&55aY_b2t zWG*;F&y3~Vq2C}KP9gtSU_G4YY>+Eo0%joN{{x)2t1lVk+)l1vD@39}$VPXiJ0I6y zMSID0MQEV#+Rr7{RcQ^p?<7Y5%DU>|x}| zv6;(nD^e7V&{^?Sb)kg~iktt)Nc!^peL#+QnjgR>!JH~zKn!J8N<~>Tsd36027bpk z6L}2H3X#=dcM&d#XNu2k3IdD3D4F?n6gRgT5#(z@l$T_Efb*<|j|e2Ht$ms94~I;V z6r7H-zq}!Q_AEUBx*~!=+2drrmRg4__pE8kpV$YAwB8~MLvbx<`IqD*o?*tZ@$y}f zI@(e*mN-Phz6L7c7E1oa_II1p!NmUDwRd+<1oz2!3r)NboLLPnrf{2Z#$(XdnX;ko47 zEo-+ob*DE%KYV?^{EGRJwy93lP#QLC+}C}w;p@L&4MuMLab=BK1#723IYV`XD7 zH(92uWTuRDV|`h{MCabyq@bjhz`0S??19m0?7SGj%u2<{i35ZMncJUjj2s?TVNQP@ zl!^1;zZ%!Ta?z2mjX{LL)fIHqq#tF3oEk^qL6`&4Su+HW6%H-V6GnM`M$$0x8M4i9SxvHc?3J9QyV=bz(< zdW=8x+z$>Z8?m^{yy6Xjcv}-g6xdk>>=2|ug;*wyN0-8h*2Fp`!rp>5Z$#XPf^F`e zVl`R!YFk8WcnULxKT_m=l`c zM(M-iWFF#9!+;)KH{yB?pz64vnhNrfKCSupQ)awqIlRAxTOxo5?1yJ= z2xPIrzsecvT|vN@(!U}1FB!0a>&AwX$NlJ8adkIKj6*q6dxZ^_Xy$E3qX-|s!)wkQ z=f=n9rSrvtOsD~v=VM+ZE=tx63HjBnbXx>3@;2%QEDkVVl9uJ|8%TJNrq%eo6BYO@ zwUUouKzRD$wIjW*M*x(hi~;XG=l{(D=Dh<(aD!ne%<3;XVjH{7{~XP(V^%fVfgD@u z**?k_q9ZpMBVtsF7yvn<=@CGD7tPHH(WZEeMu7Y*y&@1kmtIgqBIDv(A4y#)E|o2# zr?s@+llu3EL8bY1#TXy(kN+XgOJFuORQi*&;%DC&R9I-+&0vjr$P?fcdD`q7;kq5Ccv>u0?SV zC49$Z1gtt&YWozgCe0y|x;U z4G~tEiHg(>syhq-Zc)?O4Q$0P@XYxAT9qlld8b+Y1Q~&R^{;j+MavC8)87oVNaD4> zpIgzjpP!SRiCBLux2MbMOe%w!W$39<@*-mFF&8t$nXXR+L2X{u+c0!iXV5jewT@{5 z)annKK0IR7{IoSt6zK4RQjzP;?IO=oo*+3w6j>^*C(;j0u%{_nX|SUtpbU`LzDvrk zyB+@_;LR3)LrPPAb+oUgJe~Bn&}V5zH_BL+$MZ;kmCVXoK75O(P|EySYci{aoRtsQ zsb){VMg?@tfIXy*5Afj3aM@3)%^TIb-E{P(ZZo!@RBs8Q6N~8Iarjd!@-zuoYw`RI zP)xIc(q{1D{IC$)NMBndMC2Q%%u&Rwtk|&nm?U*!ghK8JLn8EJnt&J05zX`skH=rD zL4wFIhL1Kh!f~o>{hoD-*FlP~6n}o}4#zK|vZjD~u>HOBv66}vGK$`Vpk4_d&Uv{m z{w6aV@1V7KT|T|j!i$qaeY4j;>0)n3w$TD2eF;|W99jdSxwcGCpFYJ@te1^FDgFN4 zbGK~l)!2K#Qv!`WxCNe1H52?EDyDXF^7@#IB3k>QDleg-Vcq@RT_7L)i5#)K-E<2jkbfj;r z?#&T|8Npmnp>TgU{^G##;y_Q|)P4Rzg>g53w3-@G8b#F-;ulE>C5~F|8~ol~*`80@ z2dx&x&V|)G?R7POKR=SY2$DZ*NCKayWiof?0fY3RKP4%RQ)q=_gaUGMx>R|QJ-eiU z#rw1S*N&pMh|c)jlyY@n=6?P9HLP`RhRRkDe|LFjO*F-rsC-RvKCy!_Q^_DhjAIe98bJuyxW5=xx#Irhk&G_bIaZCRbk zYuTGt?KaqqVX8T|q!Fe<3<5d&c=b#`Sge`%F=OB}|NiCg1-9{~-Z$wKc?LwqOh&i| zADHpp_NUyErBEo<22hLc?XQ`a-IHlTe|CGHubNG5K>~6FwcWExljR)ql?QI|qUi|& z$;DX4=#`=(Di$g?o-?q3a{ZPpm7cVTQjtiH+{p5}e4t#`N9~ttyc1;(cbLf& z9>ofV8OESJk_kEdiTU)@c&!4OW*E5iBj>kl-4&C$$?7Pxq5cSM%L<(qrIyxwt*)Y) z)R%=|*0eJ`jfDj=ZtIV7HaGM;kffVXXjlpMWWYG z)Ty&=@WnqQSj5QC;Y_#X0BX0X?%wH4lp-k=D-C{-A-vaAXYw+h07jPkoNl&Qa<-VE zY;82~EthlSW%-0NJ_lA*(nw1{`REq4UcCr~C;6;rw-Kt1rhU0*owd-Nbb~BFu~J)&Pd?M8A))Z2w#EV1qdJ+YJ%W?# zVe=b2hPA?Zxst29TaW86UsTJbqIk0{yBX*Ax_2*653h&s$VB9&VBN|2CRn#$(jn-<6ciVn0CSp-ORwY$_7TdWKD5ve)4=5FsO&e6rik8R_{1)W!T(f*6>fevHE7R3hZk{U|Z*4ADQ4h|*8u$*d#Ic=i~d^jH=~Co)%+_zHq>|nR3$&e zuf&@R2T6yIum}r~IKSvrRMF@5-MGcQ#i*ett6=&AN(XRLC7ZLz>X!7t&OC0Ui9R&4 z$L>OxQMu!kDXrp%5p!Y$FP*lU9xr+&fHHZt@`+$9Ke@VrXAbTQhREgpJNg%i>}UJ1 zrSS^q#YuWIPYzYQz^9+ZTf{tM?}eFn8N;)mX;70Q z-|0DeTH;*Xd*NP+)GF9=!!Z#Vr}aKQKHhnx!00d>8WAhQBA}Hr@>T zuCMm2EGQKX&mR=EsMl-9r zIwoL0EG(=b<+#&Y!|v59a+dvwDPar#C;4vR;z;d5sRJ8!E6A0wuqksjQtB&AOKmAW zTvbxH_)UfMMx-f`kDIJ`QR3KZLO3}NM|#_JAV?ycU>AUFREnWNxtyB=V4~}3z-14h z)VcE^Dgr4&DDX z&G6rxMBQ}t0%*v@lqL>9@Xet!AuP_2E=^qMm7 zHO+ThvhOTB+#O!Z8UM`Dh1Wb$dpFdn?t&|Oce{5H{R?o4lpXtbE%}G{InGrWc30=~h#t5%9%WRu? z@!41%-T*1*h3c8@RyJ^cHdM%)Ix=70e4d@sP^9nZbxkP`e$T#^Kny#Y1O~f?G*0>K z4|lh1qoF{V7Eu!s`cQ(1qCf2l@jx|UeKZi$w3$-FSpNusYI9qe6sUQmbM{W2WI&^t zJL8X+05aD2OwOT3k19Qp<5DL4;3@7l+ z2wkgNE>EkdsF5_(@)}60AZoUH{Mp{ne?HbH+4&z>{o*+cv zW)PHrnvDKT3A{&h+PC^-*>1^h;+aao)hIr43(vfBH>}E?)KCyz{@kRH@=2a}S|>KK z;+9+;8WuM6&>ul#vb;8XFj@OksJ7%3VdR38#hrEl$~67}s*zkk3_qv`Fae!wtE+b3 z9b{s9z{MGNYRfNk0hGcBk{BxmwP-$CVyX{a@TfLcEMJ26oWfCFxj^3bC3ALv6eAFl z^2?G3O-UYfW9n={0$k#jKW^iOoJU^u2ysAz#nqmFTCaV4zD|3#?gq7dpU0ZZi2L_y zI2GIDx4s6D)kx(dI>gbCJO*D~G;Pw854>2d>o%3!T+bFV3BIF2TGs)vn85pRR}^21 zKA4p}tY(axs-CY7N-?b>sGr};^Mz`*{iGCR2on>NK4U>4hpNn^QM}StdH`0xD;uTe zB9f$`-_6nXXe>?1^Vm`B!?SbjM&&9-Pv6W7IhW%Zr$?NtyRzp2LF2mfLgz6EG9)T{ zD%J^ROb}{446!Hf2ydz6?QF@~& zDQz>}83niU%Muh3`jsu~V@T+Zy@`tjUroZMfu6CJXd(|66==K;3Dk(s|x=*h83Esh5m?oI#ZczH>HGgKc zootGnQ1!q~ycIea=R)PHUuGq`Wceo2y*)MpWesYfPA2*Z{;l8MEGyk4QhV`J|K8E@ z>%to*JhbhHcKC%xc;0jp*Vs$#f0ry2dhh3D4-F58pXkcPXBp-_DLAPzJ73W!;cD_| zuIFQH$nu>Z3wNbaWM?J@IhaC7zgR4|Y0@KTxX%)A`6{qC9YhDW><(lT@@%d|Vn6 zGR8{G_}wyk^Y}99{qx@0#-f%m1zserJFi89Go+`NlbahT+rXti(c3#d>hx=6;#cxW z8mOT>cd|_LaVM*0m=rV&MN7|xH0m0Vw~A0iavZ~z?!YzcFz#sZx_bbv+GMf z&v&FE_M#_!b`}(4EHsbKb20!F@7%f5=OoG8wYb}7Q5@%FQr^8=&BtriDT-KiYt%6_ zGt)yL5E;)H@HzZHYsX_&{}V%mdhbN07<(?GW4qrltB_ZFH=K-BVnRP*^zWc`=liDT zc|H6o`Iy3zUFO0hOi#GOg}t4+%0%LwEs186Vjxq%t2`c`3-}4rL1De8z50V zLpB0VI5m;QG6bfz>nss*d{<1H_TCOP^Q=ar=j9#7H2BckYPp%(eCXA8JN#zHY7SbG7+hqh=+faRr*uhFCCkjWKd>JV{ zyR}p)SW+PX^=RLAF&HpeHM5oDrC#%?#e&|dx6@c${W_+tC3vNX<7{whjsZBGaYtQc z_$?qFdrFu2odH<>8?8jpWJV4LAy@lpA;RI$kT_@m;d3fLNgQBqc-^1!#VzVD>M%;^ z_!t9x=U~jyu@3U$gYnm`EY?uF@A2}JPip9|)0z|anuiOFz>7;(i=Hl~VE_|J{|q5b zc+fBZJ2B|95!L6Rb;%l77Ca(jv;~$;s$AxUcgjnP0b5#s`2&V?;A^mJeOiz?cblrzj_o!8Fq6}NZhMZwU<65~(KQ~2WtxqE z!7Cc5-gbN@OPtn@f!YwnvV!8L8#dxiZzdz4crg1P` zyLr91cVNJ|k>5wP&DK1aYce4M4<*kzhJ7JPf@&mEBYF*-69TtWEFn{*(`_HB6GdqN ze^CO4D3{54!>mmwvB&LMVWs8lj(se?Qc}`=1%P&I>J{x-ZLl8jK0Yh#M<*?L z&^pA3$x``$IF@sswY~RBiDPS|=)?20@ViB8KNe`h#~J-0y<7-)1gSz>O=_Yd%k-b9 z{WRS5P(cCt7p70HD-I?7T%8FNz!~lN*c2(~<#Vy&{ykzE$9plY@fG{TdG*Q3l*6T? z(HY1d4JE2?y{jrV5P)dyY?f`{8C3)Kg}Y7s!pryAH(tuRA}HM};_NYiq1tl1wnfvSS$_8a3B8RGXt=Ohn=z~hb-uib>Hae}%PeUMmA z>0N`-Lux;|Dk`=|9VO$se9^f-9K{62HA;w6nMcEeDB)>Z&1Fed!j9d>%8k$dlH7K~ z#Z?YJIK_#_DDYAwl&!(#QV5-V?O@KFHtE+GfcwCaRAsqb&`QfUpJ=R}0xQQEBVa-N z3xl6|xV-Io0`)bfX$y$>-GO&pVC1J`Pr1C+NYm+9#ZwoQ-Ndm8WL4i3 z3ZL;jOc~%eDw{6mPVt|U*Ht9+5dD1Lnu}v)4P^>qG{0R>Kg>J{d zu@SLT3f^6rT6FC(mXI+PRSKg8^Z#>ur$DslPiZQifFub}Jl0f3G+Hhb_~gpAxwvc8 z|2jXMV#8*GC)uNy?1>2>Q;f3;MTHS432?;b&PgsmgCUaso+m`XdinK%2xlgS3#CeV zheu}8Ve2xk=JW6Ait>4wPe)H+EAa#-L~G;UZk&#dQIWotE(UViL4F_TOs~ys6b`|p zWf_i}*xd*$-b}4wy&=4ffz)Syi~i5GU$H*eq$!-Ov~6!_H+0M@mhAChx3|$kUdwvc ziLYDUjO>z*yyko5va?jV!5BDW7_+RL(PyEjr}vPk*kP_EjAGJrx2uZlS?Uu727YrV z?IqzeuHU^9wcBA^V0$!wlf5ZwQmWac(-vjt{C$2d?(PwXUik}8W z@foC^AKiQ7uAy!+z>bERQlT*mPiZ0JQn?nbNMsP`t*Q~{)I0hb$?hA(Wv1^|{CNECee>gBEf|(3ZhA2k3`Y$?rga&o#o?%czX)D0imr}1W0!|$r2 zQ-F$n^&UNk8*zg@peU}nRcitVLU(?P8%>K@O|Ptcx2+<#^NB6Q(wxDE3;%PJjI=oDTif+&(py$_A%k`(6-y-BiLu(L3?{!Pgec2v2cMqCqP*xhg#SuvEa9>p)nk5!(hEV|gT7}M3&tbJO(?Rb zK-+NNXhwoJR`}YJ*|=aBB{y>vUesGEVwEDB2I|iEdl#cw=2SR+Hq6-qhJgh}5~q>6 zp)}x>{B1g@|BLtq7;JPb;uJkXYZn(3-0|p^S{#fOGSyZqV6OBv58fsPo0QDm&EIdq zyyq5R=- z_-fUU?aX`)Y6~Pl{krc;%=E|2G@797Sp$Xl7VnW>GbbN`*H*y_(Q;LKJ6DuhsZuYk zC+C;RUD?d9+6HB7Z<}Yg8<5{3IGI8%f41xKL=(GNb!3^}Dw0gAV_XJ<120c)#IwVS zjJvm|C48bq5&TP)$sQ}Y*#{k~ufJB7?N=vBd#I%1;IaLaUf(EYzU=#Vb87th_LY3RX<`o9o?U__h87H-gaDv{ ze$myAMd9nJpBj?;M9A(09}8XV2+e4MCj(?ni-hBq$kj>;uO+3ruSDS5q3fEV!_r}i zY;>sf#_qtbv47LE?`7SJ;9aN%I@apR7N5oOCBL!6Dw`vrBR#{?Y|Hsva%2KCN`GzG zFT_R>53Y*`zX6{V{_x$(a2YD=px0yT&a75WRtr;N)w#Of6fv3Z-&o}{*YeQ~T5&Zv zzK0l~)Wv?^*w{$28^N3(W6w_y|I2u6eR{CIaqC@JN1>*Bs-hq(7~wJFWbgwclQmQ5 zY21-uW!R_1lZi-s^i;|wc9C$ckjUp5&@xhoMJ76TOSP1am>TI|(UX&tm>%zgZ}JA^ zuS^I>H#a4BrZ}>E7@x7h(Pv@-P)B-o{TUup3LL_s)J{%YphL$rJU~oBZAtRtezY2zyl3~b-Eh7L)B$|g zm;L}*Y|ts&LQ<1MYRRpATOg?8)04-(4?~ljB#qyilv{32)(A<5e|w?q%Bs7XAB0fwCNR9kMev$$uupSuLyh397-GfgkR6^ z9;}y7CEwp4)agmS@8QypG&zM1S63VoLUrFV_Os%tsbBJgEVQ-Zr3nhh;!sml6ye`Q zH8?z+fwm%mO5*;{ONhJcpD*7$Yl>ujYGe)7C!aM1)}>}xvYnisLQn~J-YKrGW2wF_ ztVzZhFA2hW^PrC#jy)%rmY4ey5)x_-4i1d1truB2`~?GR#-j6l8{kw3wOoEQDeyE+ zODjNe=Evl+FDlKSG8wb_>J~LspN%f?vBAj{=}#ODHBD)V1Bf99gSx<{G$G0!#RlVb z$kpL+?qs+0;I;wEdw)xP=PwcQGbl6}c*JlXiPC55Q+KVlp5aI^!g%uctRO z(%;{ow|bHVy}J%%jdzwoCM{6doDR^IIZjRP_gtLye7UR!jL1T5ue#G;Dsm+Re!J&k z->0TNa$)BRa#EH)BWVNyR!hhxbbFsCGl++@pOtkTYVxY`M#!9z1;W9Q=XV2~ zyDNxHk_d}D4qeu!rUDNR%KVZ~sc=&z-VKaFA8NP&0uiXSJJD)3j*hN(I@EA;3JVGl zfr@Dy_tX4*=;IgNE2H|IOh)!(djz7@{`2stidDUJSs>VEXJ_4&F%>Q`cp1N78@~{r zwfw!bbpNtL$aLcdnaPM%1qe>fe3einQW@FzrQ}?6H@(_+$FVYp&5Q`SKTzp>Sv`G4 z-HLLV&NUxTpr=9x zBv_3%Kt5FTuadcb=jrz=YWGMIdyFMGLE)wzlozWPUObD7o7?^IwW81R?ebd{`YL7RtbOYPvtvZ^j8trNzU)(r@&@onv z-7ma2n7TNha_vr%p+_Q-P~@cWyXi&PXsHEztCXh2%NntWjyfkKXI=sqzF435eU$m~ zRd3$OG&zMFARZbTvKQ3bV?(jQg3m8)?Nr zkQpTfwL=}=%MWvzk056%$*Dv51WY~oAXm)DDkBeZqt3EY7$H;T)og!xLydO{tR!y| zhRPBjRd4n7-^F!a5NyVtdlWmYzIcAumLU*k!X|CtkEmA`eT-{9FDi_F|5c~qb>dqi zqlDkX!%r|Mz|XlqV7jpO6RM6W$ZaUkbZ9ReD#Le%3Rc>@hT1BPJ>ECLZ~Q^h9RB@C zlcAB35y%{-9BSudj9AYgrvZ1iQm}j3Z=L$~0`V)`yih|>YmA5S}`o=wf~ z+Gn0)$ye-hqTV||q`QYkQ#Q@&+i#@;hk~~zu`yP^q3%JH>a86lh(v0LCzy;)P072T zvV2XO(hq+{2=pL1&_TLineJJ4DcP+Gh>Dl7f64KEu>=?X`F=5Jyx3%Z@wPvlhgRVh z^#eXVzpRBP-@kW4iu2YhDlaG0R$~Z!yzRjtJ;z7Yca55R$F^ivXXT^&f^S#7`9Q4r z+EfAinI3LViil+knBN0pcMEHwib78g2BYx<6RMqi3mtWnHS;MWwp|ZCAd0p!48*LO zIy!Eqqd#>jPezTw*7I(VV=b5oC1+z-J)L5|p{SfeVy6~G<&I;x$(FevO zFU6`E358hh#cs_DLX<0jdbKoK3gsQ^ze$>eXC6{~a>9eu^K(sV7ZSrh zQ(J5+(|=KVu2xf#v{M}QwvxiZ!}Zw(RvbEq!*cFbL#YUd1#{2F)wyw~4+zLF#=?vb zH_fo%nyG0^@RHkyCp1OUodKq#B#by%sD?&KRvS@hT=Aw`Pu~QoA#gqP`CUd$$Yrxz zhvW;!oAqA3Apm%-cxj1qv$C>63!#qn-*Tu&g4l#w4`qZLe<_jvKI{g^$h+PLPv{hy z3W&VxM+@uI!ix}k(ICzI2%LF#bHOcK0h|Ze@TLy&XL=;nfjvG&bMaBG{aCSxhlUQM5oI%L zdk|&)k&!_@-JddkPO@xHQi?-4v#e~m+595+W`H3w7$wPwOT^ch9|!NTmS$kx`pd>alSAp`nY;fNDL`0omWGM2wrRj^^sE<8e6gPw9@2kTUi4rS4 z={P@8vbz7(=OKu0?=zeY54PC@ai?c+2Dc~;YI~zbe8k@WOecUh&%M5x(i72AWh4dT z>PS^c6#kWs03^%R5S;HTnAg%&SqH(Lvio;Ej_2$It& z-wu3oi+kVrLl7|vvzUgdyiP(dsGz4uMjX(X`6op?7v;WtU2)*MA0Z07JT!nuhEk~- zC95F%lEq8GEcwyo+l#K`Z{5;%S&QyZ-3z8^G<+0viZH8~XABVFzqTKnoSK}heYe=1 zbPTogf|F!iHL_m~RliZM8PV3mgZo&nj!0E6dui)eP|yMQdz3_(lnLcL4sF|; z{g3k#$LZzgDUo*oR&b@pA(ibjW|h%oenAIG$^B2DLS4{iGBmNpXOUK+%@*b+R z8kv|(I6`f0b*^0t2ZsV-QoqeiQ9Z@!z57ITCJzV{KS%ZY6L5Tg82=L1)aw@~H@as; z1>kFfobexEy$^(u0jrfsP;TSbmT?a%%>B38btlfFyHp?#qzzFnIrgj-idI9`r{qgR zmAkCHfJm95FzhG5CZ=9e2(!#tJ&k<(aFiX8r9q#$McG`nPpT z7sjm4`zu(W^hlItokUKTNe!0uOmP5dgHxoAoAs>zZrDS;yE;UCsI3v&GQ}rb8&bl7 zcHHEw_5Uf0K7+tu1^+g{$xNOHF>J-6zfVU?@|6%~ug4?wi_08EKB@kN5-GWat``!H zlg1WDH{;Kuv=gR#tVfri$YDS_qspKN3o)QYfvu)!OAiHdR zt;WK_5)==_2Xk2i<2b2g@Qk-6%5x&RdipnM$ulaYeHL11)zJN!V}))41STVtnp{*M z8xe}Q!DHkRgR#ZO;1-{waa`_$$^G0BjVizZN=fzD+nu*<0cA6@6v=B5w_(5R#f>V` z331u`28mENW+XzA)24fHpH2<>$Fy)MH*)wZafyl?V+RpBDhLyn#*m}>x57T+9L>_| z7-Um9rFEh*$#-;_f`kkssp-lgrV-VljH}iJ%PN(k^+dBcqoZd zA@Wg15-ZBsC}f)Pp-+xVXu?igduHSf$fvmg{YJCTyWgi5h443ZB5;nRP^AN60J;22 z%Tno|Z;gF?Z7QotS3U#!nl%xQ=2#$qu>&E>K|L)1 z$Ul`XJg)v0+xein40hulSiiZ0nuE>~pBae-XbU6Fu^*mRdoNS}eJ4YIVLK3>65NBG zWdzD|G^c)e^j{q~eJ+H85USJ|-<%xvCDJqH9c&G@C5pO$ZIPP2NZ;m`+uaR?jf3-2 z*~5cfCjNvJfQgaQFYRHu){DwKwj(bYEqYAreBMf*_HMg77=baoNI&ZAaeR_evAiuZ zMX2%Wk#gj3Bgcy%pG|(g7uJBJ%qW)~(OS@y>(XtYj^He7z1>#(;_${rCk@Ib-)|s$ zvyP$O(ad@I_psRq#?i=zUqR1p>pSs^`X@roU z2D8E8`wd;NYUU!25(bjRGE*Ko_o>yZFLj}hScuWJA2A6L#!T29x(yx&@sE#imn)A^ z+GggLe`$a-9{9f~JIjEmqORW$-3%Zl9fCAScOwYWA|NeDNJ{r1hEA1kP(VOw=|%+U zZXCM1bLKrh&;4-kxA%U9GY97Ev-e(mt^e=8ewoO$G_;jCeTaCE9xaNnhIczEdP2}4bC~CKj<3!9Mvnf^ z(^bSx%9Ralg}>rdes=%jn)cLn@1d8HSw3#e52UvbMjD zQU&n^ZJPzP73XNf8xHQP<|+QmKgNgY05N}MJi8WGpMHC^12s6jvT;K3Mb6?B{uM5= zNC7pI;2kaTHI9&*J&?edJ|XgIya(-0=4@&$EOV4dXB1rM0;F6Ae%rD3->?5j2)zwB zqNYG=pwBD7b)&*I(Ok)wse%CgBe=#9IzGjyC)zBgC{|rpXYZ7k^!dbpDnH<>ugDB4 zYmNeOmy2D*U!%}gVC1xspb}Gxa$jGiF6Tp_38>zXji^fp|Ia-I`|;^Q{y2^me*%H0_S435oCgG)(gGWRtWr9aakRY-Cu;1FY`1tp*QqZo4TaTFMx z?{BGFOx{wE5@YU43&x$dLz zF2rm@?i*=H0+dT0TR*9S*4rKe2=+ww+h2>+4is3pnF_w2is-UVez(i0{wPtW^X!#b zn-+&y{GuU;ScM|f^oXXJ*x3l^Ji<>VwwC69G61DLuhB%B`Fjef#t!UOpbIj@HONE36zXf0mH_>sY#HQd_N))u(&x7CkguSwcv!L^SIOJ>I7&T%2I=R=lq7||7| zVs1|gHQUf1ISWVcs&bvlo(azh^jui%rGwvJW@O9`U3f&@P_)1QXl;1=F_4?g-x1v< ze$5+jUV{I1zPi{j8{AZt5w)MnLSkq^_$<_(|Kv3D_)yaPAf-`vp?&UT zU94P3`xUy3qX9NoOxkZc-5I(a_W3Im?^ggyBI+ngt=Sm|GF17d`{hmOAo1|b;{>T* zFFJ7bl|%vAiS4F%P)ELqu61$X4BV-Msn$MfBO{}c=$^M9Z3V@jJV~i{Jx0?a^wf)A z%i0hfHq6<2_~N*5=i0b9f^rW=N4!bhrefD|dXSdAVf%~RMdUpQFTXuYr5JPuL@l4& zUwnY4+U?ppl~!kDmJXx1Xe_j~0Xw_H2+4G8!2K|*DFCt<<%0TTlc$v`=k`u#B>1cu zd1_UA3g=>mg_mMNP^r*iqj_@OhdCz4`)_sTdy*lqQLmHeqUv$fn@5HU+tJMT45R=l z>;Ppu%(g$17guvRUX7^BjdXyb%PABZ_;yww!X|b&7!vAb*U`)ro+IpW@VD*LnH`S% zDK`v+Ex!cYZ;mJY4Va{MzVGiPfC%MY|9a}wcs{O%Y%45$XDvvj#6?bx9hgxB>aVdG zG26gr0^t&wQe|Ih;&E7b&XXGIxBy~rSJpLink{Er2}d-4;vMe$fnLpTIHKfuLnc+< zFP;{2Ihsys<-&@hw53K7foM{#VvX&sNFXJ5-dEzjXhlJMnss^Y0L|d%_IbUi6joc( z(fdp>Z~RqI>Lcf-i}ooBohb!_5d%M;`H6hw_OoAcPjp_>CY61usleksLf{G4S15ax z+w&zUtwR06_LU(Hfav_0ZQEf08a&}EssXZWN_nYQV({1=%)3&|e8%IvySEFEY6`>S zyZbKi?5gE#w9=OGle&6jk&c#Xexk+~u##5WUX9T_Q{MCYDIzk!sIJ3G+@iJdqqx0M-4n*PnIdu~9%@l9#O7Y@}dD*GwY* ztjXEfV-u;*MJ{sz)%?CEF^6R_$Igwc+n=1i`dL?Wp7_rE$pce@m>nJpW6*p?TFOhs zzil?oM?=7z!wIcgsvy##mcFd==(wk+XkgfM_2Oy?j_i+7Lt!9^W12GvQ?W$1mJfAz zboad*v#XcmL}?-&O$x$6v*3nRMi%tHWVF*`CM)Y@DK-fbQT!WpnWNECxu)sud-u7I zjDJQpvP?+eJiw0P$oqyKY6ZRWCvR&96&*)Q^Z?Dz2PBq&r2b6p(c=!i<0pd**FBk3 zRm;@_m8-w0M_b;Ckq{76zI}%6xNUxIWr;?$Ino_+HXN2c@}>9(f!~aCM|f{;M+{UN zb{KHSSUB#7S11xDcu?fk^VusvCKEx?mJK4FNjO2&?WZR$Odr$ksz5zz%s*Xd9GE=> zz_-N1R9mSeSnK`HnM z8e|yJ`7U?J!3;>Xp>Q_=Rcdd-wimH+-=JixC?p#Trp2XE8-?^l>AI+%y4NaNGg|-3 z#~}bEM|d_;($P5SLLplH zAdLhg#+4Ho3T9w4p>QfeZ`M*|Pep?W4ra%^(tWg<>%%H6i9ny0zNGZMtndCjzRNvt z=A$p^qozOBwoB2gMeqbPamuJf`8OGOAWu?wOneb{AeDTS<-1$rMomSfrK^iJP|Z*X zE3%tybbFc;cx$(RwEui4D=boIsCSi&LSEn{5=D?8sxtT&6I5viZCND3TH?~eZA)6x z53Wf(6$Dm|u)ui*p8vt^`^S$TY%k^#hpg$FEwz{60T;+Cw$&0XD; zx>r2~(`oeH>W{va$K!HZwW!&yPAcJ1@%pTmP-x8e&FdlIb0b!l4&oFLlAqeYtc@zW zmE4;RYkzMpd&JGGecwHM_OuD2XDx(31bb?tvLT1LW`ZFo_!g7A@U{FP30ot^IVsd! z8Ts5{2Z1*YcvQ$)T49z;tce@9oxfkaDn@o^WCPzri8Q%y-KS_P{Ogc=PtVc<`|*)A zg#NG4O;Emj)41_AQV!W<#qWx_S){SP=|A&HY{6SMyuI&&!6MF%WmvGrey2r7^Z_O z0AysY;1KgTEZxG7vWZO)t*ti+@xL`}_&49xSigSQoh;-Y)7qZ>-8owqdYBVv<>s^Q zilI`NI6wx0R2iUW5JmPdMfMha`%x0|F1^>iz3N+-Sk3P6Lfe-!UJ4VJ--gG<^pAyp z%$SnPtTh)`3?0ISu^>7E%zV@=(M&OoRe-L`*?xNu#)UqE-K$P*LK-{fgG&VtXb>(! zQ!c4Fh69L%LGo}2J_tk!Wb$;D{*D>TS%~|ImDBnAaBJ*SS`5_m0(#vkpniQZpNk7I zGrCXTZ3J#+5zzR{7x#t&4m=e{*K`@Nc{pz#N|Hq>x}9YckdLR=w}V`|nHD zXj5kzas#SBMuQH~p`b!&(H%ey3mO;1R)`1+!VQ0|%FZZ-v2z_T6Wd-hkRv{)HzUCg4mLO)Y%?0G}k!Kr|b*UMPAtw8KTq8rg65Hj3+>zdVba zXzDn7a_QxsUsgu3^YXB=e!%*Hb^6q?&H>z_DR*~E#}*B+Ok_?&IDwQx%i;Yd770&J zDIqV7C5$g*s9T7OOs2UwUWY$*N4Dbx!Mly*L>|QBtKI`4SIZfXonZtZ8IV1I^@z9ACd^zwKJQCn%I30HT9_Luvp};x5xaCw=*zB1M_t2 z2n;zoYhKzm5sF;PGMhM8rUopa&;u)mHWp_YLfZ$TkcH1FA$JQ`PfB?nu3jSPZ^#uu zo>dxxxl+*t7W_^ZoZBC$!Gd5^ikO>L>Pz_<57HQ=MUV~dd&4f6b44YKO&Tp18S{p( zi&YXsv>dB}jub+R%E*%VlXl$%`o>2?b|8q)!#f_9eZpy%e><0^Ekk_(@XEx|-!Ee| zY-ZReg26zhN{2}!Nh63W3{W#k9mudN#+Q*aI0ZBzVv}p=q~BcYs#?Gi8vFATgfkac zBlvpx)|+KoORnUrKh+pu@!m@$G7wM45FNVerOeUhBhBr(vnS)}fnNp0_Ww@A&k1CX zjvE8NI$1JU`M`mS<_3(Q_x>v(;na|I&GVii=Y?yXx&YWwwLUy>)hH^KafhNk6es^RZcHCc+5w(W_MyWEl#2k4xuf zESG|XW+%^>$c2CW5`mw?uR9Wun5w=!co$q_Xl$L=>iWl?_)f1=M09MVV(j8=D=n~3m#)k3OHSaMdgCHajWAvw^Xc+3g zJh6uKk#iVR%iFOE+!R&02^XC44}6wmOq3~M!%}`L-s63R8{m*J6i(6ix}5^`^adm)I!NvhDP%qi`PctFh346sN=wn8J8VmKFh?9b_ z?cX1{ei&~wZ#X9fxl#B5`I6Ca;~CyWO)pF2*&TxvG~a-`;E(tdogcPue4f?D$i(Md zs@pCcE4UwC)7Bl##>As7iF)k#!{n6@okeXt56Fz_X|HEZyiD&3c>{0q*XW*&xz63R z?<}1!G64{kp>f8SPyrIW6U0WY0*7%#z>Sm^~*v?Z!+WkL_i$__3`H+WS;(!_VHdZcDx96;6P-7VrW!|RNdbiW$SEA-tD zj20#Nhz{Z{a(Y)DpV+^?Rxj%4tz}4h^XExv29TL2MrfYOkVT|ef^I-ggf2TGxMEmD z|2rA=YlzK4AOv6DLJZQxB7-j8NVDjVVw%5<)d7w)CMH>@s~(>o9MWu{Xl6sMV_tn?OI!HthQ(L3A4~8I+%IyVsX$jJyh0L(8z}hz!dsEU5Hz zO{|O2+SB>^9Xs%D>!N{`=D8jl?ADfaPAO*eGBvgR>V1XVkn{PiV{g+JyiJk&iT-y2 zDay8F!!M4R2NN?`&0k!WHdd51#pS;4UH zfl$3?>+!4*L~E*40g5yEJoBW!_+E-jDz;n)>j|kOi-jHI`a36E2rNWv=!nxtrSbaM^Yzx*y_z* zJ)o%wMkRGeWYxqQ$*)D7 z6CT)wkR9mpC^z1yM>~{3ix{E?xTvv+HcDoNv^VxJwdmkfzY3|P{l;+b6OU)Npz>P( zOX4tCEX;3TpB&LpJj6Pt`j3(5BR}ZZY~pLQJUW%K^^MQ z{g%>=yzAiPuF+6Pd;N@MwzZL&C;I(ks_2y9w_{3e%QTy>2}jL>$bp~cdcDgJ+9d8fyWsA5 z@pgh!0>#$A8`1mB?T~+V$ENZ^Gr@AC$fGhQP-&Ep`zuujahYmov|_#;mGQ!J#!R`o z3hEnonM;Q^5mK4c`9R%;_Ml^BoYl(*xhe+_J%GPDSP9}Fc)bYtRAgQF8u+5qhCKwD$qg z+OawE<*D>VO9O8!ZzJ-DIud1Z$OT7a4WP_xUl~`f%fbOR0MXI%S{2qpinKA4Cs;FXj&=#!B~^1H@yu(U+UjSEvG zHS{wtK2U1FkU6Nrh)8ap@;NJ{}MN#CCN08x$i)kY` zs7U%*I{!w|oi<7lwf(w>ca;6TMVYuQt(}27J2uK!2?3WoJY~?LZ8R|f_OT|L8NEZc zGdP94)3!WBn^w856ncMPP)5y->Hi#hkX|wNN>fW(hb4iow z{rt&hC2mf^&t`Q$#Ty$LpYhuoR-l%b{$d~AhT&)=T+r+YZCXG;1;|^2dbXBAwgDmd z?|MI@cl5ftqLlr)g|k3NF7+S0`KwjkEAqR`h}cTm?=NI=&9&b@-h7gp zS3ikC&12jE5j56vdz|s*hf8uyaT6j)YB;`ood-kRHt{{**W^1S4tt`fK;+JcUxSpU zQ~ntE7J1C@aGM2v1-XLtxo>eomiE91=C2acfidmOL0Y(4`QXutZ^jQ>37xcEucI& z8aht zg}9lyJ@Ry(k2Kjc>-wuN%&>IEofWvT;Ic_@wS0s1H;Nz=s9TFoRwG<^%16V$B7R!; zZCS z=xHR?v!oVgRL^r~d5%{R)UW@BAg|W-83iBgKaxwb6-vGr<-n>*ED9h?E!#w)!LFbF zE~hLVk&!yGi9!kFO4nl!Yz`^ZuInR{9MQ`0(5GY=S(5e$x#G4Pczk0#hpKn4m6MW? z_>9-(U>v=sfu@DAT>Eby*2gtb{@z3tu9K*KC{T+@~uR|yMaAyk)E0U6J8?tBFM z9Gi!}f;6g3#wXA)q4DI84FQdKY1YS;&TXRm&M6WHfH^i%y8TF;dd6TKF2VIHJaB-8 z!(ZA^weDROkk)DOdyqyGwrmPiwwA#zFFm(=n5cH&6Ia-$ftlTfc&&#=@{yWD*}7hR zdeTWc2JLnd#`-IhYBC9F2vpCC0C$G_A;Un-;fDM7?gze9ycG!IONRs48l_7sDQ*~_ zJ999Q5$@w+0#XF%I)z$&XbYx+7`#%a^pQO@S#C>vMDaZz55SOD77a_cut(qLlgrj zJ{*0~{$)u9SN;O}kbssek-cvtdqa@%J&CcsA61_m-+9Stk)#`| zbifqJqUSW1m6r!&O&N7#yr6?_#6p(5HajUT)G2_Z5$*B%)fHnd4?}Y^wG)3x!B)P3 z>+&n!ju_syNKifiOZt{Q7NW&=p5J}2Xqn(FZ8<%dfk$|Cba&~+lsRPC3HZ<}IhImO zIZ{`$V*_WJ5_m&yHs%MrNZEzDfWD1_$lnEN&$eEzR|gtY$y>ZCq{2Mp&D2h-x5aoV z5bG`;%grm95I{}evy*BTs=^fIUejea6~u76-gAIYTcbMWJtLJ z(b~?^Y`NqEF=u%=p+W^%OBDzr({UQcm8OT8zP~ll_WCUwHgI8l?=pm3NodaLz`H}j z;wD*Y_k~HCCue|`NOt~HDYK#pQ9DLK}AKatJY3pQH&G4wDwxV0@|?3 zx9}H{rJHzK6z}DVCb2NWE*DMr|2QZsJMj)@dVMrAnmkV!u-paEunjUimBsGUF`HF4@F05dn*vhsBW zAbbT`zkxKJ#bqRpVbrhAM>$pv#Hd`n#~Z;D21r|8n^I8A>gT_D$X&Q>*NAhe!~+BR zKe$0Lf3JUwm;&GIsN_FlyE86}5(+!a1`+B(7d%(qU^?hCw%23T9dy;1kor}#9qxr1 zQAA`J;u+ys)zivWwbJ*v)72dmlyJmy>JJ8$*k7;mB0^F!*m#V`BX1Mq-|A!D&|+!r z56RU{Lu%}`_xtm_^MGy3bBg8P%%Ck=p^5sltJ= zjsB+d=1_@*)y3f16VC%X*2`m;e)RErvMfUi*lN4 zniL@I=?r(f_AemyJeUn_j3Mvfb62#)aX(my;Iv7$Qa zWtH}tfJ6&3jDCQBlU!|hL)nkQ-I#_P6Y1nR%1aKq!S5~pKbWSeEE%h zHXf_@!-`(7_WPsIwFZSc9Zk#}R4b;~t{ghiMkbB)k6lW|f)*M9G|2Nhx093@3MO*? z%J)tLpNR45TYVo(KNJa!eTrWG#1aRj*}p%Ah!(}`I3x0U+4c`%6a<~x$MgcvD2(cz zl)yro$&--VxfS+P} zBo2X_C{{Jvq%j|A2Q$JZhD~v1Jj@qD2QF^aeHD1O3*X}8SB8<<@F;U>$M^%%(K#B> z<{9bOcNC_WklEW+x4x^2Zt%V!^j+)aPJ4+_s`~ugz3KY$OKW0dfP@n&M2d+4N3Cy#>E;Yk%3`-w)?{Nku%B2!v`gTPR0+FBPn(|?guSn1`$>xTNKK3Sy{x4=8e;NBFb96I z*s&(~)Y{y_RqPhGwNL!9JNbN-Of%L}51PTWtFH^^hk>!nF^H)>wr%(^XHZJHLMlm9 z5}zbAe9yUQPtMEOmWBFF75Yl!Xozg_SJ>%S`pFP=!aLI@p7C`2zdk@u^7S!+i1|tE z<4U!l>>g*Xf(YcxB175}NH1qCiwkOr$i zp9D6CGHr0x(IlbyTG*Mwj`9(i$K7U6(4X3Tnaq`7%kmMzNxF^cYp=yCmBvw?aoO4% z?l_pKPz>NlA2`-u_!bYm@jk=ABO+im7j`G8*I(hcWimzXZsz zw_I419T(~%mOev(9UAiR1#}PE5=+%eXu%zVdQW69dD{0SC`TSDxkIohO;++8op^un zASj*hICBV^w@!+Mc-Y^J8Oz)Y&5u~|<`Iv%8%sXw@Hu-(_lFh1-D^J!S#66v4kUsx z!jO8ZX*zQHK|IND=%`1&SbY1OFl_Hex6eC}?Q@O<(K+ySYXpOhQ+UP`!akNZRN%WO z+~RR4*>V)ksl`FQfn}|SYXLiPh*#-~76KjjEp-)w91yzq9<4dwAs4b>-VG-1nFH<9krTwAV zbKGP015nW82vJ#-FWK%Ik{G3-#uA?}c-B4!pi*h=?q9Tv;P-jsS2ufY*GP`yS!&=d z<~i%}Ovc8?6LJ|=qq7DX>rPbDBd>eeApvCV9)+Q$y&E z&%c2SJ2yS|+G2cXyL|b&FJ#Miwj~e`zgrfNBvyDrB_~P~VzG^_EYC*fcz$utoH``O z9;U0$0~*+%J)&tY4_?|c>!?E^~ALWiv+^m8|0C=OB@^Tw&S+MksEwtprfG~0jMdyrt6u!y27@&Sl$ zMAfe|6*+c}w_k(jAUK4#M3}zI8<^IRC{;u{jT}|5=Fq$ER$i8syDbQqL zuJGpxm~9MmXBn9Y3br;TZ$I^jx&_j{DPYa$VzpLOAN(F)&{%?*nj=Ink}UxZf>Su>HF{}E zNOHQnkX2fJnM@6}dE-JuVaOc@9#IsTh+jP=`Szm>)VGVe&9-HGHbmv#-ST7iW@_s_ z3i(8m?dJ(TD=u*iKM_enV1Z6tNDX`mN(^vAvSQl)2JSoC_qMg(eH^l?vaz8itOeuL zKZLQT?z9wRv!k;5Tp8PQcO<2|nag5?zLy7bdg|CK-U7aN{1E{kNs};v^fm~4(IDU21fxXZ_Tt3n%>BJsmSL#+j1+ul(jbHi(}bU{5k zhB{sfedq~X)Vji4)VgCGtli98-q5O!27k-fhWOQvf3vh_(@vPrM6hz#B|Gf~~n!CJW) zY7c*E(a;%bz5s+DOR^b`{gN2n-;-}r09d7lp zaPwU61A+bv24iF!Ba7lO*#>dD!n7tPjVAZn0wE~J^TTjCS?wWaXEB}$BSHmTD7JYY zyUSn|v@RR6M`mMyV6yGKW zD3k@g(Uc*Ik`?E1jm)dBr}(IX4muInmE!IU50gOx^-nls)~ZGs>G`w&sc(HVSXbMKau-jnUmU#fjug?-w1-??KrlTTX|)u_b=qN)s;e+cdeJ%ngx8&T9_ z%4Olfz{B?Yg}BX|7;TBQmsU*(?`ws1`f&`1srY1jbK^b2fJ^=z+M&9uQDIWE1GyH8&H6v%08&UElY~y z-Sxa^GNLy7dDCP}Q7QR%I8w)lK3B}@0%~AN2#BK#LYi~}r+ByRixuS9a8%k=aYn+3 zs9;-izKC7A4b;5cenHV3FrZx5^rU^|aCLFMJkO%GvcDLH2*>pI8xpYn_Twxwz7Pf^ z(JixgT!gr=hnD8j=sZj$h2ZCW!KV{mYY@1pQ&1FSp&gJkdt}XTU~(?J z$;(S7lkrel?2fwpa3N%L8cI5+93fOG9L0mCcYv0J$uD1TN?yEsdV!lfKWt47A_%&O zg2r25?)Fm^CD%ZP#C|+QME9meH(;pH@2od;e*JJ%x=S?*Cilwyj*ko&@zUL$6f5dv zDA$)EZ@AcC%z^Me_j>r(3{^dgnVi)z!pYTc4B3h#y!nY_GZcJ{XBZ4yn~+{v-S6*~ zFO4M3qY(%sQ`PGy(QlN%B=(+gs>(q2TVLxQ*r< zzlQ9Y;OXFg`j8?x1JNzBcQQSCD8du_7S66Nr{Y}cO2Kti1UcW}w=FII9ao$iTzGL2 zxht-!`c^>`x~YnxZ4$=|w4bA{jQ zLP=p8k-S%WfMV!OB=s9JVD_n-!rtBqT%tVz?RTKVzEOe$dyIFVb72l~dYrrsiVoZU z_Cko;NtEdk9==Vx4Psi<=Kr>9PYi~`1O3#DL!y1|qfah`|z4G%buz}{R`XuK`5 zLwrc=g4(Fwgo;2^3P)21Rw{a14)yUQqG2fu9#%E_Ut9kk6BMdACX zg5CRpLp}}*OFW4v@U4=TP|4zBlL*&+a4U6Vn#mperf1(n|;(ZhEcR2A*=FohGwkvZClG zD%U%2J(@W6i*J$|5TYm;aa{Nq|JriVp~{n3Z~nm1_GHzE${4$}*Vvw8^hZR|X~!*J z%3+?@!3?TYfeq)JnxzHeB{2H^?#<FMn>v{qm>}@J`7ZS{{0Tn2?Uy;Mc-l=d{^PzLcaJLa?c=)8|V#t$l4@_u& zNgC)(CgFiVam$HZP@crF`0s7btduL$h3_@;nP2N7Re;)~#8C>^pzB4|eZaf33dk}? z$;m-tr0s3ryXx)3L%KJU8Q3U95J7c?*=h!uEPpyI<>fU+MFzIkI3uT9PLZ6NQcw$_ zVPi{1kwRG%U6qI5#k|yY!c)^u;I6_%ENxGa;JIFp2b?djP(JYuSt2jtgAYHhT`$Uv zl1VnqE(07E&|h%I!^354``CR5>c)?4DnGF-rUYZY2vx6_+ju@BXux=ux3T_m;2b$r zP55SYsy1Ycy!u{&-vtdX$0&qoURJ66-Z=hlBJ5DV`IXoOXw89^*_8;e%qKzk`3ZDk z=FNq{TltV*wUj*Hnn*d~zVH}Xi!+j%U@;1y>(*mB<<`5YEZAW~!k-yvC#x0+3FApj zEIfRyLUmc`&{ZaAXS+gD(_q8~9~Wc1^Werd*&vYnT!GEurG*>N$W?Aojjn%r!Q5{5 zC~56B;-z`t5pLn~ z=b(8?#%P3~*M=IQ^hr$W$(Vj_wEv$T6XXN>aQZ?}y>UxJfj7XSy{!%<@a+#8yhQ1V zZi`64QKw8?vXgDw-2OC9lK1#yLgfDV`05y5*Jc1-YVN!wQ~3g6tPY-5;5l zh>3b4WGGIRqwHTIZ3;b+TlqTo+oe(lgX%E|KauK3vzV#wl)Z};**V@=R zTjNJz-BeT6YR|bjMe7?JuTD$-k^TJ3v+i*uo#eYqJ4=YOL|KLJAC7gk*du6L z7Z^x*Nb=DoH@cE}TrWG+W`NlM^xKH@8@#Sux|cOfLBRmzTXJ_ZQ6S{|H;8XeM7y1x z+)kCut5-Ydq(K2W5#Nd!neO5Gko|ZLslJM{6j3Eojp`f-c{u}-X zm_2rnQwt^{R`<}!2N&5nV=8%$m%m03Z)|LePnCsLL93mHa=i5#7gwK#8`PE-f~TqU zi~NQSVE|1_h{X9;qvWEolM01wNszx6{oi}u_TP6%_>tCAmVz$Absq^Bd?J#6YEvQPvMa zm1VXLBoFTol#h!MG8T5;V&!&iPJObUQk%_S7H>ww!jy#6umamE+6jbdY{LmLQ2zC# z1++xNA#6b!#4wov10^k8TwgwDo@q{y*mi_I!nu$Xw}(W-(Lr%HnVYB_kND;MemZMg z($Mlzjop1f6O>nVXM}UxC+IwJq2)2fNLH|W+cGe#6)0oyqhQ3$XSw%Sj=uX6#locK zX82zVKlZ{r$tdvfYE?6b^I-gQso4wQEClH{`6>fNE^>v%Q_PggaeUg0B7!*>s^Em^ z_7;bk;yWW-NrzRxdXsn&(@Jy`+JNhMv+Phst8E%TdGqZDN^w}x4aG9!m9wmEpfWNI zI8d2t6Ovj zd!W_lnl%%-F`YpOu65yVwtH}iV`t#!`HydSLb6}cb3AgoE|4ehC9hM>I+J<6e)4v@ zt7*7s^J26-e=B!@nw2dH6#=;XmpYD)j^GiO{t_uqU$1g=JNJgnhoK?ssw`>EQF$cU z@)=+Np~u&S0g*Xh81C})X&-!LsO*gr5e+s>4)Q%KQOt~Ebuxo0@5hi#dq`lYJvH?) zR&Tf+P`$}*{b$b21_r{E)fq?J$B%^?T3>aG#pp1S7 z{VbHL41Osd9sh8gtL{nGVIq&}_&}2i#XzM26n^I$RhWjG1soC>@SlyM8FC(V$13N& z@EEn+Wqg!z?rcPh0{Ub>undG)w}KGrQ1_vyvNJs2Nc~Y1rK?onghO=^A(((RHiLHk zd5!&1+2>tSaJ0U3f~}x$Uq47an?E6ubD9+t8#5p+pNMeCMV90h{h=pF+WQ-)$m9uJ zKk7$T-&FFx6>TGgz$6SFbmdsIFlZNZ=A=YN@(PW-J?YzT;i=U?(|=b!U|?A$^Wb-WzSnc(1!4YV%* zXlPtf7Zvbu+JHbMo3HHBQZUqD=5_$FEOy!(Y}yC3InOiV;i(^QMm065$#45t%4i6G zMkBm8-pzroR+7*cCRouu!P5Tns^<|3l@PFX!Us+mS|iu_tM}&aykB9aT(`_YoFQiK z8qhg113-^DU7zq@1ddQRQ&eP;^+iQ%D{bMpxz{sC)T_H`u$xGjB;?8G7b*bzKY{-{ z(mZN$cb5F#f>CB-Wcz?o)iw|Zz4H-upT&A#8c$o-CUkt4f+MAk@o|8l)l4DcF;qdH z&Rsz{V6rC*ES^Wl>VRHPh!rScAg~{Aw!16tUR5ZbENiA;ZzfeE|IN(oS8cHL8Z13$Td2oD~eZ-N<{Q}YqHA0EilOcq35hGNVPmv)A+YH9z z!M3MkpZ6YLXV-K2N-*)`E0VRf?=;VjwOTI2c^n$CkpZ_*;-3Y;XePR8z@ZQt((}x& zBI0kUTo{?O$L}o{)rsXUazDST)u@AnF9PteDWlhGgO9iH?~TmbD1bw6@+QV*HVT%J za7O&?(W){(35W$fJ0wS+!^%Se&K*8wG7#*nvQ7P(7X97{HG^n&VnJlo!H6)3;&ma> zvzO=VuG`$?E(3;u4H%qLR~I?Uf!!g@FfhUi55M69rPa4yk>VeI=F1G#zW|$jKZbB( zX%Zrja3~=30q0|fr;h}?EWqJ=^#v=uT+WNe)&kG>iMxwZ`74-qa_P6xFpFBjg!{*l zow#}Fs&b+2-)D|{CrV2hqJ`Z}lmlivzZ=$ImI98nw2r_(58j1{r85CqEJ9j{F)US9JFbF)b+W2q4|kXOj?UEl>5`uN zv+*Ff1^V3#W;YY5X&SC1wERe$XZffiFM>O&!0mGIFRzpMtpu$F)z#-JaSwtfD{Lh1 z-bt(U!l@I&l4vAscwh`9XV+1x<#xk;VTsIAJaLp}k{f z8iA)#ViulK+IbTn`13N=*6T8#A;v|LqR5F6a8Mi@dvvZodA^lUOIQh%N5Z2mRx0>y z3Ftpaw8&FD!6)ayIltG+5ne~My#AB0R?5L64$9e z`OTVJk`tfLVg)~0KM@QilK+kUvI!H|SL5fOsV3U)H{a)MFn7S85oYN44Tk!dF?n1= zwyxtZN8B~Hyj`V}y^Qw|RbEpiZny4~EKjtL1`&E5dfD@S6>s$R=8o#yoS%tF+~HRS zb}iL#auPV(`K*yi`krEP_9AQ+qBt+1E7SJZrYWjESyFeGz3)Cmvs~^s)4>5~$;|sK zv4D{Dbaswp{_;JNu;}Nl!{g(qYZN1HtJlP}B%&o;3tszjs!?`t_U8bINOYQH0A>(v zgp(Y0XMYv)h4ad(puFNdf)IOl-MFgyKK^L@sGHZw9M@TuNsk` zCJ`|g6A=g&5i4hT>y`9P-FskL8AuDKsGo6o7hdCL^CC*{>Y~kQ2+c2(+NYKe>f$lNY>R`G6J^{g)gosl2Zdg-{*tGKYbf;NrM}uU7SRIBiK|v9P_Jw$^ z{~?VL=2;o16?X870q~7MzJk+}t8jV!C7ItI`PX-OtXq5-=0CDSZ3v;n!`uwqpR3%{ z6(R_KsT)}XTlg)QkAJ`MfiCB+ z8QF6k^<`hwU0~|;u~XxNhv2S|My|N(vLB74H)r^Vk8HS~b?pH%R>S1Auf*fa8>u<( zTx>x{C1=KD)tZKD+$rDa4!6g%EpeScBpBZFH>O$43KJ!)-Gp|nN+7$ zufuCJoy}eNJwmsA;02QMb%G*_j)B!6jaXUV77xPt(>NS(C3v_HknhiU&EYkrZ;II$ z9p8-=-_ixp(=Z8%P_(~d57~L#m2Q|ct@uRgXH-lQ@(18pv)`1&pFULvw%(qGXVlS+ zz4038aGwGVaROpg?c?~~K6Ux<;iL7>@V4+b^?(_~d!L#9rcX-dOhBgQPn~1+9G6^G zU!>coQXXBA+r$fTPk{b?APA#Jr3)uMj&K>1H)L%;N#*?>5cTd8Jn;7Ms50apiU ziBx81;x+IYw~-KGB@Ja8WMg<|f0>%G=)3ph;CQ#^&!0bL$Pwg5RGh}}MF^Bu=Gk3# z{bFtM#X#5N@F$tvRi~%Ux=lM~6A~>^2N6yyivzqaC)bak*F&|+jPtY>mBh1qu8cRg zY-Z~m>1~E-c8;^BQJ3ou?$UCa(gxLIw-2bToi2fQ85Y4C>3Fj{_;*Kb^g+q`fiVu3 zkW{1UavcwFO~@6y@BgK@758T~1^VVDC--X;*N>uKnmq%PFDwe&i6Z7}LXgMn6~`Tj z`bs~x;o+s%i^CB|w{C5Be~zr2%u|??Q^jQb;=K-TP5=J9x?@|%P2IQm3gjhgj@5U$ z>Dvc#wFX{2QzPkqcJspsNxgtu))C-)NZO7V#QIn$8meF$Y+2N|z`|Pm$tOZy`f=Q% zfX`32{niiquf$vr6SVFXZSwaBKOcQMF4HhC?{TQJsN8S5hqsMrs{JW?{rlx<$nX^b zY`LoH8Dgg7bJC|zQrT|e(}NcVHSGlDxP?}e07?6|ux*6A=YI#h)NKfkKm_&|S^4 zhr5!)JuTMrCE)bn@%H7OlPL{NAU9YF_4D76@e~R}YidS`iUNbyVx~0hg5KSfhH&|- znA|UPI`o5#@+j@%%Z;8+_Q}A6?J2$Tpq#W@)ogm`tPtrZACA+zUR8R=`RnvwABtw^ zRy~UzK;BV%swk7&=-)Kp_x|U{lU-i~sc~{O_stvuLn21G@fFPwqqmM|0_UXQGg9VI zqj6QHgwdVPMJgEc@@UJZ7y(ht0y-!rFp@i&I7|~B-DoMiFF~pLUE`6LmCBPQy#m5W z$kofh6YToN>h(^(M7JiCQgO$l%&(&H34^Ufw6b~F|t8@Kj;+7 zGgl+-25xuD;!eSe&ve2HkkAJ&k4-xstNe0QJ4t*?67nxMcX^Pw=RMJ_FUuFV-l!mS#LkE@7%go0+YUT3=* zZtA-iJ;r77kjDdtRgL_3fE1~n9qrbxqL~5FbJ!Y?lI?S0W@S`1`u6H{@2i5|MJhEC z)b_0EdyoH;!BA!E{?A_y$~O)j03K@mkQp%%OdpX;usFu%jkyAGk{mup9ZA~Xs9@j0`epQ=~aAXYT`YmjBK9A{;I#>|*Z{n@# zl~8MtQIC>(iviXQ@EWKZz8z8Mrca@pRX}Np-)!K=UaKD zAFe7_fE3JamgGvDu2(jYkrZAZvXx5om9R!Fh~wI7*59B+i1LFSHH!0XRWvQekP^p` z&dFa0jKK0$ugB6NExn@R)1l&X&WqKHw}?kWQG<2Y1CZl!KXQVBwYgBgry~7JbFda? zQU0mOfW~{bBUL?H@&+8zIl-DK{3tZDMfY=9X>42bCuJWF`}f+(qXvT_qxCQmef+hS z$Up1G4;|8OhekK7?g?XrROKqboyJ*RRRP%Xt_Q+hXBL~4N@1W`{ zB*&ZF4`k_%7H2A^m(;n*lC0IoDnUb;|33{U>nh21S^)z8}gjN#^fpey&Ll#s77B!c&r7G2Xt`s!5GjsR5D z1fnvl&8r0Zh6Ic!rDk|jtCVur6^42<%ouDYA9)^kKG+9Rb#Y)M|Jgc~1Ydl3d9B&p zfAP5F+;WEi>2zFMe&EIu7EY<$pIDibEP6zKBz!>-M)5w(l=-`y|^xI-_+L@Yn&flA8&J9h)q~Q;V&=N@AeA4VeF1ynR1CT57 z2KJ2%j6}bzy`Fl;mq2YF94I1&+6&gK)Kc*UVeq8FQycGKBk>V@`W38c32-$Dyz0>u zlNN1VUTPx%AdOCEeN&I6y?@5aagUlElp3aw}JxRXt-$VbgOJ1?EPQdDs zSsyAbhC2_+SVn4ixIddY#hN$B?s{8RB!mVe4t;07QXwJBv#><`kR@imnGbnHcKPys z@ia(aY^(RDIL_Ioszqw5?lZ!w19%bJbuVH-l*e(0d_}+Qwg-ytn$b1xp1w)m!Knj{u8Xm_-j8d4_nrV`O#n-O;CEul)?vl&cz zG|$<-CtzB>t_n6q4^X0SlATD+H21T|s?rA|-p+oABx%kA=r5*6i8oR{@?SLlpLbLO zsdeF>A{kz^tvnrP$36EM*#7PGZ96?_jnWvPC(_aVE#euNw)J_^B#c8f!>BWKDzRID zLsrcdy4pwqa%mJd%_yC2eQl{i24dK(@D*1aX{7CUa=nz#<#yzA&D@ zA6E1M*3Vvc5KdmGl>B0Lyx?yqO0q>Ra0mEWF0PMAlMS56NWBkZMnorc!-HTP42+Y0;%B$*l(!AN;7E~sm)#acNtf42=zk6z zi`#E&w%>XkPwf_nN*|q8ae?1Dg@UsJ5`^avft-h)vmuUXisz6UkwC$0{+ zb?PL&*+G|I$s=7!Lt^>K9kgw}eP8-X6BEnhw_5OKcXLIS!H0M}i4BfAA$HtSjTywG z4ku5SHzva@wmOL4F^n}@CZJsvau$#R_-)^?hqV35QMD*bcH@elWe-ykDlH7pXBO~0 z0_d-Zdl{DVOI<}5S8F^}0p*W{YNP}@r=@ef2F`6A@!4QTTKn%Rh`Do>1e5arB@D2s z8HBSfLl(j=EGa2DqPz{D^w>CgK0d*+U8+IYIBt7?_lJ4Dfy8~$2?dsEciXXiUvWeI zWr6qyGv3fbWzhI%)~XRYgE@J9iUi{4hGeC@K%9nYu@Ge+93cNQk9&HyWg5!L6#b(#Qx>>*VKt>R!S77^E+|RG(JGMOJnNO;>UsF9IV4Q;lEob$3tf$9 zW&1uq*3RBn0N~T*uyO8boou&d{i%KCFU4+1*Y&%cP&v@wr+8q54w2HNd3BAlzD)l!X|6QErW06OqIs_DJ=v)kLh%4qF9 zxBm})alMj<^M~1>xbNZIaudAJtMG`3h~02w-xEJ@knnPT@N)9tU-0kkH_XC&569%s5d#!CX9l;EAvs!F zR;T@QCr}Q~LRIyj;XhifMolW#gA|-IykZ7wnl(2j1{9pP7a!G#q*mNkI(n;o8bnw7 z312FQIvhHNNE)A6GJUVw>L`e0m0TqN61Qfzb3NO?c@gA70b&d=I(ppR>{(7KG}ga{ z^YBCJF~X8Wm$Orw8glgq0re!>zfkl&SEf}BjxKE8JN-e&CRMRbOujST4AYqv2s_EQe3k8RN*+Kb<#W8&#rX<_FuKEvgXm zEi*Io4CQ?uW$Oz)a8B?3JOX))Uhv+X*7o#ihTKsZyU)~IzO+!gbmzH zej!XVC?&`0-TmOV)`6|}sD1S7?3SMAG>)5p`Bc?w@6e$zu=ocWMtZXNd-C5d2>kHk z6&pQUNA>E0+h~>MiI%u+0TQU^ycz4YF<+-F;p49vlU}-*MMZCTf%jm`AEL^#>}aBk z_QmCKA@--&SD^X3d!pq_vqX`6X4{{5LILG=QV$o`;DwWLfN~D=e_-&xJc0{!=-)-R z`pS?lGC%F*G3Vui^j~@C!0#A=lA+_dvhBALAUe#i_cNj+{n^W$B`xhY_kK7LZYwCD)AkCW~8}+0_!r$k}S`}8d>gM@n|GA}5!Z z)t2kT8tj-`n!X*JV|9LmUh*B$0JE*Q;gQZg|M~z_VY^b**n{}2y_JsiF@e;lB(dfs zCh2)&9J8^~9YlMX?r2X6szL_5j=*-RwN>$cUQwer*P7=(O|u5DP!NW-t-yrq#Bz~g z#BhE`z6S6F0aIbvlzvM$oflb6Lbt0;M|WN9PZ@_-DA!RntU8+d`sRLq+2(Z-R_QQ^ zy~gvxTyE-jEz9KIH_}T`564HwzQ;!)WqhN*e3P|GvS&lm6nb|-FV`Oc&%=UJus0POC~4#~nMr%NBC7bV`dAfy_*^hgp$EhXeW?Mc=*;3>xXW&DDb~=uvWz zBrV-?Vf4oX1CJBQIKK|j(|&YCdH9C?WLMv&HVu>#? z4nK(=zmIoBBPJ7|BE~wzJ7G@7#CmZ|q9)0`Xb{i(jb)*Mw+%^iIYB>0By4MJfcaBO zl*XcqGRlkg)0bCI)dqB{bHwoKZOMUkD zGYCK*#KwiMZB9O5j^J6mTDeby_?#a@e6|zkzy}YV4g;e111|zM+x(r%v9Yl#Y$+QR z+I_Bix^tgoKjR>JWY4i}1p-N^Q_=jx#?Dsnz19;32q^C^=_x&*_vl~XJ&035B(K8# zD!w8wV?SP>J8!J8vh(}h_Jn-en2)eRMn=QH+}t}FRbeO`GC%y)sa3aEr($2*tZ8E3 z`R7nK9vjqatx2>BsWMspR$W&Ie-U!(Hul_jlW9j{*VgX|M>6S(G9xQF^W6#epd-C{ZZ1xr$Htm$5k@_XK-FHgEK^AA%AGN`5F+_ zWwaU^=X2W&%mJIdl*@aC+&Y~{AC~zpV^8iYd*o5D(@iq?qr5s4Dki8$kty!MAcsRl?|GNNk`6_L7ld4RP+SIC!vfMaxOqOouxR zP>C8YpyP{4CL$V~no6Wjtf^sj8Ex&6wR)r~T)+SrQZiuoGz--Hp1wqDNWXzUZR-MF ztdl%i(kBb5jJo&E({^~;g%R_!gPOFJk5CHF_1V7(ybG2=irGq1&$H~#;V75U+)JNr zEpHGgtG{I?PI>`>XOj&2pF29+I`dsHH8Edg*(%`XMMhg+=188k;e25`T1LSAgiO>y zJD_Cu>7f1qNx|DbCrQvGqmt+2o9>T)lj3a0V1<~Wq5U=ghP;jc_Kenh^J$w%J>k2} zL>>}M3XfGd^j$b)e_TV9YCIwYuYhPHqOm;5u*guO!HEgwi^Ud~8Ly7xfBH7r^YTI{ znF{_6=^k25e^(wyS3b%z3N|rBDgm-*9V-tV8bY0865Zg-CxbT@?=XchgizaY=Urof zIYbINEcO?=(|wH%!{<>-18pr+2ui0t*7Wrz_ruTqP4Ls7pGA)$SrXw5r%lS#mb_q;AjNOBXMkj%{F?|M?v~->D%VR(1+vVsXa4i2`XL$h_|TPV4a^ zOxw!)wFf607(O13OBsL5z~`(_w-!6jqoqX~UfR_VNfUnm-+>(3G*d-4cmsNn)Wk~E z$|T}5&4RX=2dFvQ14++c*IECq|1><*fj8?xN~jdrbcZYdFRQB5|JA8pH#dqHZ)@yQ z+>oCPNtg9E(Axu!nb80HVgJ0M21*wj&%cZRzIy6aX>!#1zYnf6Gol=NTqvh$c-%|s zcTBsn9lc9ZyZbi9gj0L=v_43!WebOV694W->ZoU?Rd`|mL`qK+W^gJblH zIhJiIv8sogd-0F;$zFC267@NAD}e}ObarlO4;q16W6+qtr60uH8#=+SY8nAo7I3>c zv=pM}G_{hxGW9>>VFTH7DlvV`;l1Bkst&v;*f)HB%*Poe7yU6?}V9M{q|v97QKFdJw8s*})9{#BQSCiJME`Kkt$)$?hu zts$-^;nTbhR6@fgQb7#G=}huaK{Y@-LZ=p@J`Eud#|RU|4^*FPwFNp?8>fy%ROILw4HS@>=j2X$)>7n@YjP=aHr-fa3s0O(Rh(^P2K!tRGzr%hu0fcAN>D+w`9ZOZEQ{vPoor6Hc>^^f~IInywF9wu-32i+^^JG zztCB{6n1?|D_@FlR2Y#I3p{;LXHAfhVXVPqv{n66ZiQ+qX`G<~EiQjwT&@*b`Zu&( ztGGNPv@`=y;R()VM5-L`NeKkyAs9<@$cmjF9Q`eKe-z9jY%_@;1!>{5+$Q@tXUM}C z0yN;l1<&qBc*loWuUwUOUAs(@$m;HqN#p05icC#zruQrjbgEocMzILLeCx8Gh8yqH z48f0UIJTP&}5kWD4>mA3TngP^8HZE3I# z+*&$%eC+rs|9PMEf1W@9w8$_Gg%_oV1KSeql~b0rQ~nGj{AtV`9;u&OJ4)4qj;~*O z+Jh=OJ>(lx#UtCdK+X!4fG@_MN1iY4;-qV&c9LTx`?9T?oS>-X{ML;4M-t<3EZs2B z$oQ*~`d2aazRK^QXtCSer?)(0wQjMibI$R5-<~!F2OwJLzMJCs{>HUi?7kUT`RH92 z)My%%+;W43;`*)EQAn+bN#Adn5r&F&f?UvXK8RQ2Id(3O`|zG}^MUpnGm13GD`u08 zc?%Z;#TMZFvy9}hProe^u!`A7epS*3u-V{JunPd%X20#$>%CnU{O^9s@aLrDPDx4` z%!r--I03Jn2vXi=k7P(B&aztD=Cr^i;Us-;)I(PHm#uZ}F|_cO8VZ|qMfjh4?Sum< zfw)3N?cPR^B`Zj@%O1c+E0Yj5RHffZKNd;hT=6~*+TP=0istW0s|kOa0W^4M14`%u zI9`PWd`H$MK{ffI|E(~d>0>CaId=~Y4IK?vaq+55ZKL>KJ{f3je8Z^3Up{26#+uC9 z&F^-9YVH0+GJcXr+gCxA^k)wyfXB6txhH80AZ_u>GFnbPPys0;dL@o(Vf(sVaDYL> z%t~dlLW--Nfikb^;VY0NEP-Ie;c3m}cUkKA=;_(7F)k$yPSW@6SqE(cHQ|%RQa&JW z^$X(Qmt%T9`}*hXT1uNB><;gKZQ^ONud6Z=?KjikX~<;G@~_pjD;RUrrj&YlNOvA@ zb=yC5$%-|Y+_AiB5usZ0492adLNt?9X|nYhkb+_rk_B5OG?vfISpy-2h?XnT9u&m~ z-_H@wjbw6EHr2s;?>PJoL7LGIWXwY$G6(R5c;%mL{BoZkz_+{q!~IcS`DeL23HEj< zU;u)6Oj$RQdZ#_5C415xuA(HREqTf}H2Z!sGclUhOg|F}8Cl?&LlV9i3GhvF)tLP% z%~3cfQV-)2H=g+fps}M`HB+F=6Sf^ER3hP5_-EIPkG&dtZ_czsL`ytyIP|w4HsS>2 zs%+5W`i4StFu03tv!B9HQ*>! z#|#`d&4rlet`x@e%aqV1Gjd?WtK=rVi|o4KjS0gqnLM0!UZD#x@ZvW$f_coE_WDE2)BP7MF6`7ozHhs?I|4KZ2pjXiB&?l6(3nJnn*l2JHS;-78*m?4#DXfE76rcz$R%383;MIO$9h%WrdcSOL1zP<4-LSFr(Ulr!};~;nWo4R8P06ZtWw>~ z$1`ounYcu~WbT4UnW^}|1Mo%l3V3fAFw;_zb-~@SS%>|J5dOb)a13e|*|f1*a(kLg zm~5d^L1I@D#uVMJdYBL!MOU3MmzlxTvJPhP29&yc>bNyKw4#$@sKxF?l~MW`$mcqipT^fr_(96tYcba6Z0?kzJy(x%AJ=n zY!#1;whWG#+0iD>O8wz13c)W2Mq2e(7T#?GMLd5x_ue8N=13BzX4f`ZtR?VovCf$RFZNwNcGg7#CF%67r}k4^Awo zGWu}7ho01EI&jp~|CTKfU%EN%!71WZL6x(LuVq0i!}nca=~!S1lagwNCDGQ|0Hp7S znZoEOXI-_JVnzQpM`Z7Bk^sZ-8P7^aHq23P)&k}7F|0ia!~bmqutS3%UJH-B{WYf? zXTLE%%Z zQHfk{y&MLj3M%zmROwp%0>ljk{Nyo=1{8bF62JF-3PX69T=Ywn zWTMm3e)ZnzubEGeh12^&ZPjVg{YX1=B2Lt87vv}3chG*@M%&tjj$v5+St(QC=zKm= zULo`ReYHl7i8m?Y!+N)7p;(nW?9{GlXFtw-+Dx0@vo_Y?0gzn%e<+VBpyU8ej zHM2-^6)uA3JP)Xk$CSJpgTr(HLkvkD)fHQvghN$4D}&DQtzY*$qOrBOAL9xS2h)LL zpA40IzlX=wycx6Ywqc(70aQ^VR)&GE2+3GaNs((SXj2I}5@a{7u4>&c%-ycAS5uKM zW=J48;I7;A*TXUITp?}Ol%$d>awWurTMN7#U1Qn$SQuEGs=u7Sd8KO<7w=W5&EtL| zI&(6`vDHEGKRD36e;d6V()GV6d=Amc24K&5&v~J}2C+{0rYr~$GU%Pc&p=NL0)CRl zs-=s!E@EieBaTedB-hI)u^Yc}77K1_mN79WareuvR+p6C|DI*_=Fby?GdB~Dw5-C3 z9G$MERIQW!X9zkY`(1ZRazxs2A~7+wuu$|@hJ4zXCe3#TUx}+}LtO}pY}4wXcm;bK6Ha#Pa&tG&a4hn!EY7W z$7^zc&w=ihFypD6m_xPYCxKO18CcPQp(;#s)dgW_Q!A^G?TIcZgFW5eP^=VqV@p5WNqn*i?Q&n;DPdL}vx zqC&yrj&4Rk0Ih1`h#C{3QN20+Cd#ZF_LE zvAWBEa7Q*Z!vryCm*L^U&8Ig}9@HETNqYaIG1_zB z`>+ZSE(;$}i9<6cf`8<=WCPcA^%*T|{ChA%ABf|7vp-1ag@#Pjh?6IEt5}k_ewIvR za&xPI^A=8q@grx7v$Ga`hD*7yaL8q>@z~zbj_QJ3zZ)5pN5=)^E9!iJ$zIMM^h36x zrCHOeQ`_oRb*6Rat4(Ez+9Ix0D64%7=WsXB!I)mTen&{!tREyf7jg)#Lx$hkfYWM1 z-n1U@Zx?C5n*nu=@$t;H_#t)d-!@2u)oip(Q@I)kT|5gZ|MMXW=*aY64GceNTI>SU z^9OALyKG=D;bNaHvUh=Aksf_}Vl%BPY_eU8t$+@Gz$)3^_OSBF+;eVu>bEa+9?k}; zaYNgJWMcMAWB0d`6|DdQm62kM4YgS=$mRR%wAl?}sJS;F+GGcn<;+^!d=!!%%Pg+1 z9_-{e4DMp$NUPs)c0QYU6_B9M3bakZ(1L%EVigytakc8rwLJZ)hV{KNuoNZrXVmap z3q`fsOQ1=C%s@dmfmI^M%|e0mIw)r&)tq`GsS$&~ z{%>Q^P1p8B_VROQW;JkR!;V6Pi0gtr<7nL~#Cyztoj!PMsFwdg&Up6AQxhH&M>Mb)I6Hm1UU@U(&Uei*PG%J_O^j%(5p9+Z*G zj`D@q!-=eEp|GVQ+}p79V#aJ*a}>t|6U2abKDz$eSwYs^ z+k3vvh7cu8Iu%y+UmvkzV_|DpaqPaj)_7VaU{~=L3{ceVMQQfa!e{(QXF?K&GF3)z zCpim;wujGew*d6y$!C<^iNs|I2Q>?fQOxPFQqz_q;ME|EPMT4j{ydud&pgomt1v+! zvqVGb9gUNMc#O^gDn-SVnA07W-->Xf*=BqR4nzotBavvMgy&Hh_BRO2vOj!Ls{9=! zjz0+Hi+IA;6}{|o1ZvoGAlw21GO*)lPGoXZA7z0E9C+&A=?|=g)eUew8S>T*Bo`MK zunVl5bfIS$QHh94%}8Lp3cBQX5~9LpcXkB_QNNZ@J$>WphfrL2YG_Z9?IV@%8tZ*B@)|K+I;YfWQ!|la z5+Djq^1YkBM)-iT;8lioQ3usly1>Dgt4dFkhsA|z}^Q@Z&3l-!3xaMX=Z@i>& zSVBWUO4XGT^$hg_OK$Y9ja64$!$pVJ=?=d_U5uw(qey_F`sHTxSPr@&46UAP{iT$0^YE5aFY9^|25AZoJWcQKyf zZcnkOyMJVsTNNkwE-1-))SL)eMRt2r>m^uN*aEs_e266X5Ls-1cCK&db^U7FZelMDGJ*tF>SXzC9~iq zisbm#Pr5zXN?tE|S`ObV`t&AGbO*qzL{?c#hl`UWCm-x@nW%>42>P6z{A}ZJshj|Qu6%&Rlo#5$Tqv{&-k}U>y4TlC+!GS*PJT}z&T!=9SfF}?^ zChnKFLM^Ia=1mYBExD(4r@1ZGE`3NJ-&$+tW6Ms9|8-$AF*{_@@pjjD#wpp3#rVYo!r+p57nWLxk7h^Y3B~g;at3 zE5;puu_(|#V!l1%os}rqC?5J#ti0UpAgVRIKA~D7Ajtd1Kq*8SVq0+MIKFoKD%Zjz z{9yT3%HDeby5RM6g&&6ORIBwH`D=fJN^ob~oMko7`=Q?x8lG1Z?B_iz&*WcJr?P^# z3CJI&g3_ihK)x*iOBJ*sqr%r$h+kxEvn=uOr`n!Y1KBV+PIL2d4{f_PL-YS*Nky|q z&g_gu-`LbzH<5pWk3$NEV+^6jqbV1>adpg*KfXmwi(&hDrfn5SJ6i0llZRG5J-J_r zdYt6hH4&mCO{Dl63@>nF1Zh|FC(ue0QBv*zJt63@a_i6Wpe+I0<<^*x1*@MkB%@nM z!8p+|F*Rnx1lNZPymx<=e;q`Tzi>Tqzm50p)u!og)7Ve~)`8#=|8wzJ= zBVG$!e~(Z`W;`oh#&f2;!)*3&$!J8q-H3LmE?;ozXK_i?HS`*izwKzCN|Y3du!|-j z#B^-s_gkU6=%I1L>fNGgWE(+?AIj80*d<(JE%b_8__CXxXX zXwKrHasTc0?$=aes}6yub^N=FH_=d_u~(D*{ubjt&E-g`>Z2W}(hd6M@$8B%Q;d`4 z6l=TDiq1S|b(lnJ_$02Z^qG(M~ZJW9*j$7y8vr>r> z1de#=#TJAbUy#DsYcleZ#x@II84sa=t~*Ss)5x`pUm(MsPttq=U;-8g8#*5#lLs@I z&u%^738lJlb!s5i$?J0e^+icDo#jvT1U5Cl(z}@FV*zzM40<Ko<9ztu`>Tx9)V~qr!`0c=#ChU@|LHR@@-Fb?nUTpE#W@{l-=zaRI z&&c=MG*>eHCU1W4u&~?7--v`j9Hj-&K%i59reP>z)jNhBr=HVh{QnpoCP*iOvUv7y zge16iHw0#LpJ~0_jAs_+vB1vm^ga<=#fPf6> z*pdq#KHDsg!(WsZ{QHQhUE0K4Uid8Th-I2_eh?6y+~{}{`_STjLpm?qnj@uIgga{) z>e^oV8OM|1P2j8yH7AEtaF9vi=M1e$_3ZMro!!l0pXcB7o|n@lWu7z53pkLEsX0@t z8>!(L{l@%`t#X%_#7u2pn&&X9oN0A6Q39@9?HY|3F1SGR#P{y`gN zr|j3!mSU!pnq2PkUh#awK3b|VgE5Vz_=v1vU5r5>7iV!mCVQ5^7cZ!YltX6Z_}{pUG_r+f+yW8Afa+(&~ z8&V-foyCtaKDm z_v1&Fhl>&ZlGG~2+VwHjKUag#r~(FP_l{j2lL03Bx)Im;5$Gv)W#R+!Wq3)TCcdAUGla@i1U_r)-Z^ zVd|qd@cj`L^@X7#rc8UIt(~K_C=VmUoiKO2CMTscfL%4^+*J$6F(IQ>*?# z56$J$V^ftYX|ey7^tmXNJ%utS>uW^qQVeuWRJ{V9Zjm zt{rH{!=Z)fumuI+jpkxXk1)3qkjDC&QNEBz8;#>_4^J0Qd@-!z@hUN7C*k!E76cq* z+=R(Nwd9^+@{j6~z3f7eyTpNodMi95BIDF|3WD;$ED7=GZ7(E8>wPSTno@;fp-LeN zvLzzUOh%!6H3p-+WO_2Sa4$|Leh=X*P@s}TE+44tip~I-Z3A@HcaY+Il*etiJ(iX{ z^@W!=>*D%P`6qLrXRT1T$8pR#JGV)zgLDy@73|au19Nk?fU^kVVsiokU$@Mt4Q>{; zA8A=Sh1xc&4{)*M7ODnOw!^tq^crM)!xTavY93V@NIt4^!xhjHpj;PwZimEEVJrwS zY46wvX@Y^jpFG~9wqtRoE-|>OqrXC5)NBs8*ag^xIAnrHyE^V=Z3$WdwMk zRv)iim_N@*f(OTHmNMNT92@cpE@+2rhdW9TRx*~Qp0P)LuS_@q=aeZ+2T?0(8i^{r#4`v$JsYx=tmS|MCWJ!l6brkjft&y z>s<`BqG4C(_z7^z2BEPomwnp)Eygs1QXmGlBlijkNX~D=nYlBHES{DBg!C*%K=^Vq#w2VbVp$TlfL6+4pCz(Rp*`RsTq7;+;@KD3TJceV-cQ+_4(o=6S4os19oOcB0 z(O|rpf5&B4P$W)d)uV(P9Lb@SDxudZ|4ZcSb&>jC8+7_`RjiMdfkEK_;5EJ?g-~!B2TQ1kos+dxKCk zQta)pd@aeZ5ALfyVrHLy{VB36&2ua}V|AqERdVw1v^A!XXxB{;ujB3=T!VH{(H0k; zstZe%8Y=B4zQj&z%LR_NPk?WWtpRn%&f)L#HD|i;Z32nwV_D9|zO}wyxI5jCig8o{ z*n+ymFTqH#FwkL~7pElNXxUAxWuYwBR}N`Mm75VB!OuB;<8 zc0TRG5+f>Aq+P61;o{^ITA>q4OitWvoT??;?fM`crpN!dtSIER-aEzZce)4B@ECx~ z9xkVCoF;JT@JVYXgD$DUnRZE1W|8o9Tcn`(JquW8e>*>|e+<cE-JEWgX9n zT5|w=UWsW3uh5P^pq0^x-W-9Wvv}Xvo|EZBc{f+xxRKF*qP4Al&61b+V#<}rGHEmr z5dIc7*i9lFG!8E*g9s0maT+K_N&BUE-g8Mi!vD^5cRTET^BxZPy39Z#|Ag<=D9(k0 z0B_XT-|)GkJ}vurVJ_(+(I`H;-&+qcwf0vv>3I*=(#dvJcxeyIBc5bWDj%Jvqo+Z#@!d5AL z@H(NGm_FdG3y5}w{_>INdf&B2DzTcl>ZC%K@aW-;01*cfC-f2n<8=$$)*VvP>_Q0O zb)lDm*0PeumQqkMmc13@eN$rvRUhNuUtINaIzYYGWG$Q0+z~UVfc)2&M&5OWW!Sz6 z-CqP#IlaIT;wfiZj-8)ys@V~o^NRDOwa%NUMjZiYx)(W%yqBR(%kEQo#x>z~(2vR= zi&WNn<3z-jh$Jshs8HVoz$=dcx$;5x&O!aMcvWi~-qhkaeO0gCB+LpF7jB16FXcWp zpGbSy`$JD1N#PQ##Kx?^VOm683|B6jgScK%#HKh@m1XJX_BmHB>} z<;l8fa|(;=UCLU=bI7TNzLbZ>&=!VN)AC>=6o_5l75-=x^mo!KS%Zm%^~c^3qj9VRP-mt}A0IxpwI=u_W0zULzk=_OC4DIo zG`T(d>C1HpW34%w{X(SQGg-S(g6>2x_Lob{rxx+$ha>REsZv-l;P`O*bjU=JOO|96 z?E2FP$)M%-?|=q{{pM@S95IyUKXgiG8~U|$QOO~z zlkM4;Xng4w;6~@R=S-2-FwtL$nw?7{s63|G9&bCsQ?24Nhqh%_+b0Zv3Jon9`woQW z%437%nHdqjybZ^@GPOuM?`tk~rn7~Ofz)4WXFa7BWfPBn#Do>yjdu3~jfQFJT-dXk zZE5dRDrdK%*VsD&nJML9H25US->W-b!4boRd>OY5UhByHY=)^LC>SL25_5V_q>$cv z)^=@j@=v)#o(ay*jjmob`X!<%7B^i~(!*vW+N{Mo0Y5HlkZqt2z0QJqDF3pjeL;T> z_c!Y}jzTZ&Z$1j{Ape}cq%GN&V&Qq?*BERkYfZ?uT9pJ$$VM2IaGsB$EcA_XhPY#`|k<0m~d-GF3m1LF)mE_YVAR18#wtbmBm8DT4#Ya{RD zQsVj-!aqpKVlTT*&F0Dd?L53oVdjm^5(qm%!AHifc{sjN{zh<0TIG0FhRx~y%(E4u zmCwiX%woh>kqNJX=ziTPr)&sl3rc zFwjEU0tw$jLO3|tm+7AB7R>o*h<|~NGf*HP5+wn_x5R&-9(b6q3$;SUB?cBt_PcOC zo+_lr^~rH!Cr7bNt^{E*=>~|nO+mwl^~KKIg?i^yb^2IOnSMWW$u~ti7?dOGLNW*q z6*5LwIHs&+!dm{i54etQBg7kBTYA33qPy1V1Z*Cafpu$|yw9l;&--=1IkEjM(4)!j2Y#EI^<#92Y3Fc1m%>HgH&44`;t>q> zO4_-zkOkiPi8Y1U+WmNo(8Q&_rS0#6D=1L52m|;A9JAm80J+R~(9Ju@@J*gai4>l7 zHF44F>Vo^~20O%gX?`vDbx2-VJL>M*KO~SnKok%xDB&v z_?;UcYYEXO$Fxey#w1RxXyW;XG?I{CXsdYo@5Sq~(^P19RI`D?QNVAXh<97 z|CvqQ>NoUx0b$3Kktx8O1HwSCwh!Jbi zs!@i>UeRqLX(mT+q}}}uKFs$P2;U;8xS`?mqbH{0lU#k4d4h$^-F}^W*8Fh8f&@KI zb!9Fu@6-wmd9xTY43Y&lNnaTCbMc0li=%Mjx!y8RHW4xQJD2JQtkEDZP3s+=hI?`+ zFZ=Cgvo9g=^F4$y^j{wGm#A90$-^_{zS~AhqDD$ABZ_RKG7++D-Le|n? zELRr9TOZy3Z!#mUM|u=Js{HIqA#Tm+C-=y{qq-tVAMyk;3Z%miFM>6O--=9VknhXl zT|Ght(GqpfC+b<`Y`zxFTJLd#2tTkaV;KCK@j9Z}ZPqcnn~wzlvcbz>8%jyCWfUn? zfsaVd`)Ca{eXcKzg>&&S1N*ag45A+%nKm;Gf08|`HhsKuCFZ{=ZT)B2PRAFZWyOX) zXHc}DSN2$rVM5?HEGx6m0;g1(OU?XQxjaji*lb(-xZ&FWIqS?7Wsz=P<#9hN&e?>l zKzktvWepOI4>L@D2y?|8S1L-x?1aW z62J}KxMrbR2fRk+=$c-b|j+;}kn0VUI@clQHATJFL^KGh(9o1Bmx>4r=@Ddswfx z<)gJ&9rg6sP(N_e{YJY2op4DpuFq;727u*0tyzP4OZTgPhC%MV6LOh&L94jbgVWbU&+F+I6Cd7{lU6o;!zbTKaW?< z)Xf)8DA9Ls=|^uPLkky|3RJyX#RRDHDL|3{zE|Kmvv#);wCHlwrr1`@Q&tO5kx~TP zPz%%FLI@j`l2_Zt^9|18=yI<-p4pFE$Ew=>Jd2E7Q3RP7xwZtm)n1S2{4ct zC4UcmCV6S?J4x1!aFQKVcrc+t*iToz=)bxpoIkK)5l^Y7_+PrbE@eyRSDK=h(j8kT zfC=(^qlPzfBgF|o#2*x^m zmtV99a~=u6@`naEAu>ptMY; zchcCLozr{(|45bH*Hy>$4dKq>o>}Wlun))75b;T(5hK^{7Moo5w~cqj&d)w+gnysE zzx(&!D1o_ctU)CRf%nPHZSAXQS3Z1asX#1WDjcc+wc>Lx^I~LvU1RBx&tx<|UTrr) zG&>rfc+kunaI~k1+7aaV#UjUU0O#Mt#F#Q*Tt?8~Vi*Xl)#Z`0(EU|4t6rFL1t|o7 zRmQVA8duRgMe+Di%9#F0;NiXFWI?fAFjl*I@IKwhfrxKM+L%mxQKDJGe8evGLAh<= zmNC0G?JwZnl(mA16~+Upb4;N;w6S1!&- zh3W8moyAIoiW8a);T911oqY@Z(7=^;!F4jhg;TLi7=P}>@h09q@?s7w zf{!}jdWeu-n3`0|P|xSoIuy6v?CbWp4mxRp%jTJVpFepJb4#lJ0(^-g5%ZXB6}PidKtC_s@4}P4?C(q(d(zt82h29l>#iS@8}G%1M5a(o*Da=SqBYtl74sL$)NTG79P#hT(FaAmNMC1E42c){CR{9X2A$qv_DC_MEQnc&c`nR^fqaL!m9EcEgp% zB7u$R-2UNkJJSlPTi5MD4Hc zgmtJ(V96z`x~^xzoIlv<3_-J@0lz0N%mQCd~5{8Y~+vEl*1%DZS0QDe6) zB+bg2eTj3L7CIKDv}hjG4>hh;H6x>|`EvyAhF!&v3ck(~D>Znjg)WFaK&;OX#eCsu zKHpbsqZlo`<{JOT7M*3C9AwmZN&eMDYdt>zthH8b5?*tVSVerXPHsCg0<1~n^$@AM z019AmC<|3nh4iIxYV=AL%;%As0C+iR#j%bfAlys4C)lq&K5?oO@u5ZEqyMv{0^HNU zG^}Wf8bB57MF`bFP`GApSSCxZ{n62^6k4mw+fKXdR;pCdnsi6fxJfht?^q=Pu;)ow zXng>QH7e|vH~+ws@J12tub?K z%!_%W-7XYtxJRdSj)Og)oCF(a^*rHgR4NL(FePDWN%5IbJ7 zWVO*r%fU?2W|1mUuqwE@CDfS)sBNL(K)M0)uc*)|auu*di)9RaXTnFzUAt7VBIyD{ zP7!UO#6*ZDh&Vb!crX=ry~4(B*WQztomQ$~U2$3VI;N(rV|k$(MtffO*t3bW-kPA! zdGjHgmQkoN8!=|K{^uI&N$Xl=O)gr?vf|sVsZwt=O-w^AS`@##kfN&(JY6wsIg#BG zA*svC$C{|fJ#18=|dL;`he%T8X#U8BAE2KDVOgX8?Yt5qX7oR*B{OGR6)nHX0s z5pTmtP2Au|)F67MUVtx42~#599P}ZDg7`Qy)K_L{1+Jp=bkN_JWR~cDBU$&8axmSE(}wMd;Y`` z=j}}9O#GidNe!q2ILC`+#m1eGupp0gUW3EX%{3PRCru!8)OmM2*M4pcy8m$7_}kxf z(V||N$n*7n7kQ@ePDBDDl%I|FZvs{jxq7vu$g;!Ji%-mO??hu1BrlpQHgx35zDjkg{NZMOwq>s$PBIvDgCo`7{MW<@vD?>Qjo^(&lX3`g zLtsmSUL4qUUD7g%JU&GfsDCzAKt*}?Mu&3J6M0hjD@tm;@w4gr2C@f!8_JTbaM`bHm318#YRq$B7^!| zDg-#p!y*?Xgub8MXkvnIA9gscA--mb>3V}~iY;;D`P{=nV-{ksAqn)^T^Xr{Gt3w( z%)rq4eG=6(twZj$3#sjmRog6LeZW|7Ve>)&@z1^S8(7M0taD}oTtDL7^DRv4Q3|0z z6cTvGK4csym}!V*QIVa~cToI}g7)iyV+O1+)gl%DV^6^Q`TEvW*JQ>GFJYB(hiMj+ zHWm*(-6T2`BOsVqtdJqW2vusZCoQuu8kirzB?oKJrftBHGb~+llKQ=a50a++e)+%m zmkEcTxs9mix^Ll}VBs}sPzZ<^92mtFcb1=oy6!o-V`G#tU);*oc%7uCYrr-!e4XM7 zbIPaN>ia!Yw9})bPI|8h?${sPpV;Gy+wM@W;{s&bM|4%psU|nN^LaWa*&6`9F(8bt z2t?<2J|pu#O=VbQXOwtY()q?n=)k7en>bTSBL}jagB6#Qpv^7r*J~i2Z?c^(W6lG7 zJ%UDD80RQ)<9gtBosdYgq7$1~)jervoG0|qsv6ZT+J4hXgIpG=>EG3L!=PWXg*^4j z694_(F*C)2>5s)oH?f8J|W9WJM zV5Li0vf7z+;>}E)1Vt5)XIvG`j{IdIbht^9ciil>gWSH4n4Y5`G#GxcBZe0?HN~P^ z7nB-o*x*s__^xm9t8iA?8n;$MU7LBoxEjp(7*e1Ub_TpByxP)d9)Td`2_#P;B~iLTh>!i~5eCDYeH>#g2ykZm7CfDlD74}$kL#s*#qLkYrX#aVLu6))XD zr84oU=1D>easU{*8btUG1n97KRd1}Z>81(dl_dYzN##`e=&xwSz^X2TaOE1;NY;5Ux ztUOF+Y>q+|bGTU(vrJrF)qsM6Vt^0$@pLhTF(IEQR5c!Vbn;hWnC^btU|W1N-dTz} zZi!7ZgD4WzOt(Oum=v66rQDBl-Q@q?>i?;I;#dK+lbNPt`1=7S#rs=N*I;*FAu#d0 z`_3iP`;@*^m8JnxByRV(i8noptjTDPgAHUT=AaC5CX2GbWwAtP zP?XWlloC*2;_i}hxCR$qcl}@g^$4-Mr+4{6U;hn4W}0uRc(xZIhB?Iv8^(zgaNsyF z=Lj6x_8YxN2~$Lo{1hr%tN_P&VR_&(X3# zwsFHw!Ogo}Eb7KZiKQs!Kmjtp#XtPa9vd*d0!E?FoG4^dy69x%-C_H{T4LQ7L7)U< zmXN&MTxR-ER_1PyBFggJ?)RZM@b{IEh(1W6Jq(e^pB5ryq7scusX_xqUM!Hlf9IPf zR$nwR!g3R*l@d_%OYicHd=5SSw>eeRY6ih%d;_XLm$@=5{~rlZdX@n%L1e$_>T0`- zyE>T(aUgOgmrHmM5q`U!&~7U+Q1@bP#yWO%dy)o?mE#Ao1hIY->m%ad5i;34;o&4u!>%)xGQV}La~Dp<&A8bVupM@f zDdq6F`{(+Yh$M&=k-^U9uPtZrg%DQ zpdyC3=?*z6Sh};;AxjKM44DSfWh@UKgbMxZaY0B{)6{{178#H7s3O)EDP=eVi^QD0 zQz>Oh;Gf|JD0)N;4uMb3^s5jwmiE2eTFjA*>-5*yg_v&k9LVc_2ya;9%eb#!yE*t= zMO_?`VmLTons`R}Dl*9?K*`SbBq89kXjsyrl$CzGDz=5NSWE$zrwtBd`5bol-2K;m zKOf_k>lNt}YGDhb@cdyxpT^RgY7Te9`6WRmW-w?DRJv!`+-k;~ttq`~`qV%jD~DIw z)GODJZ^e}o5pSNBRF^KzNaDr_pZBBW5|X5dXw*R{!Pp9+@T8FpeZLjXMZ{)L9e(i(T8ZKsIoc$N{%_Cqf@!;e_5Rk(h?`R_>`}wDNj0y6L1Tc~Ot1GD zH=kutWflN&<0!qz43J4^p;gKX(rvwL^Z=RYOuvDpn)rXBF=$lBs&G*`iuY-ugKK=* zaQmX4ekXAi)|VzV(&cJX33Elk3Y`{eXljxqNf~HhM(_RgX>dC}8O4y+ltX7thanCv z^e21~kwT-qpeUML3+b1y75%Du_0(ZnZ1uv45*I9Ime&a)tE}ve zep-#E6@oy8%>V>0&!Q=260g^EX0IVr(NK3pp=Dm{=wEj&ZNeR!UtJ$M+dNUMW&KZuD=nzscVlGrL`} z)urHBid9EoCNSM4gMOn>DV8#XOYi>7$w&V~6mIUWtIa1a1-cMuX>Tsxp3t(o1x)l~ zx4aK2gI{06P-7RUgsAZQ^}qwG3Xy z_7L!n{xfG)SAPSzWI~cOEL9!D$IS)88jppd{d~vfQJ!fK3xQU==w3{JdGVmi{t4gT zqL_VEup9&wT@qR3Rqm|49GRdmu&mI9@T;UDjqoYe-<&hAj*vM(1+bJY1J7(g>4FJy zpNhn{WHi+4d%;mqxe9YXL!IFOvQ;i{pPDWJZs8tUax&NZ(?7kw5Yr$tZMi7dM{8yM zj)ro%%PZR!oMo7&N6^|Bsw-E30ThcRm{gSxHAjZ*y!w`}9}E1xiO|GkvDbG%aGvqy zdj1C6T6OO*1=obeNGCBlm9cvhY6imq zu~J4V8-R0*Cz+Mmmem74z#FXChBPQQ+gpB?%FY>(tG#R?%Tlc90F&x&TM1d197++) zR)wJ%!4^!ncm@xAy1#zRur>Ya0dV&BEQcFOOP|l-&d&GKci;cIO_Q-gB;?ZIR~N&@ z2~Pcforn5tMEicSkG+1jW1$1M?uTKAkxavkt1W(y6=Z+^KhCZRw;$-?-h!FCgj>Wa z6(CW4>w^3faGVw-^v}9CM=@NgNT`1rrHp}W)Y$PEm_92_9k|}H?~pd$jNfB5#6P*_ zV(&wU;d#+(>QVw;yzC>}!WXJlia^z-z5eHbo3crNMA-aM4$<|7=Y@=c~ z-hf>xkOOC55#W-MuFORsK^;;1rb30$%5bS$dx0PV49&J^x%t!DT3L9aJGcDPYTcTY zT)GoeI`6rB=-1)`7)z+=`{06)ZRLuu(hGy zRqvzIbS5yqJL`jbLNLr=<^BHg>Gm)#WDWWONayP}J@JnJ^>SW=ith92`UrH|YJF>U z(#jn1`sj(eSBH>coT@nJpHMNy89~Z#G_|t8!nuVsO(24q6SWzAScK&VX!4bk> zDDAGUjSSwpDjBHn%AdgBCKaYfx(iFlO5C+0P4N775Ln8X7<%SLW}v1t1h>}+ ztaE5wy20%ped^Gzxr@1KNeF?0ie+XNQBpdl_!+U?rHyE3;9t}$c&Z?>#tpM8 z(temD_ZXsBb*26TTm{^Z7K@?vs=?RlonfF>rXkT8I{_-im;e88PnJ9p_4Z{-k2 zE@>Vc+dDj5B4l9~83LT~!j7t%QvA*jRKM&y{j3>v1qyY;`j=a?`N|{EfGI6NfTD!< za{K-bg(5{GBY2xEk}&QMfKhl4Ehz#-4a)S@1SVXIu6<*4JQz%X`=;7@GXCAoy3d6r zgd@+9^I~lH*GvqNjc_5c1``7qc_Pa4sgil(jCmfY;O!G&%*40pzBzeGs1%Wg=PJM( z+lr)AtMTFU+$Dt+CEU6NK7Y#cH$i=BdfGn|`|8j0_c!yr=Z_CZf2>li!Y1&Z1hlLb zStfCI+Lt4wkPc2(vW&yPzt)qo{DGP*UFGAR)jaq~`9)HPI0r|6l<6qDwJaj#M=Tm| zm#0b306I9C{HYR*`2wQx1?pL^s*~_5ck?H>(jZC;LI3OX`~K z9By%Uhs^C=(e&YHl*#E?e!i)YuCpwTWKrzkn_K&^c{34(j_xJC__vD>^5MpRRf)CC zxKDS+iKr*lomegDA~7gG)ZO_6Q;AuL@~G2~y($*_`u-|pF1HAqyutSWFp;B~Pv z;t&5=+@V0|WT3i~F-Qsyv8YidjXUyh0t48_|1`$Y{WX~Iz97tANl=)WIvD+6C5xuv z_`41U!R=x%2!3o@5PNP53si^%8z76K$Sz}OiZW(VV&oJfpIwwkv#r|C#4!dAB2)Z& z?oD^Ir*pGaK;J2(CCb-`ihx)zV_8gt4KnmsZfiJv0Ja)N=k0*~(Wfmenx|w+Lodjn z&*D#$D;dFXOyN25F!65wW5WGx%hNqCsU@P|+{m{puueP}YbCpEg%`;U)+Relq8wYl zTRHppqnOgq&rcwzfkTbFD3AT21w^+|G(d7!Xy5HOwP&Lq=JPGTu$;!yNr1)iz#*mAt&1dbT`!YfX*~saEv_k z#7bScC0$YJAX&IuXC9lrm|mFm$m!<2^<0Z(wa+^L;v^e7ZgWQjNLUmIwRnt11@e8z@g@h^OzqOaUDi%R4EQYx!)nEU2+GBuivs!qVh=MU?$)N=k zKX_*SCAlH>PsF6M&tq1m{H3T)(T$ii#HBW_js|Y)4tDibk>W9}g*109Dq|Y_-2y)u2xvjA_oyla)7tZC|D;{KxU_8o*p zvt8>sZ_j_T9QoOh?Uxc8Phl@v1g$FEEcu45NT$(gC9XH#w0mTo@t_1o%MGQwUc1NG z{sEpragQw2v{U9nvusRBe&rOj2Ao|UptkxgbkYL0UL+h3@eLdwwY-#ftl3Y{N_I?B zR#W2^^Lr#R|NSzH&wNb+lM>`wNe;21>FI5t6&HLdG2N*XOB59Kx@xB0t7`V$9@0Z(5 z2BB^RpO*MX^6;Y(mEZWbgYsK3suf-jMY#L&2*o5l&m7c{Dq(sk39O=`1RW|X^5j(A zTc4=yKdzyQgoK+Qp^)~{1WzSRt3Kmy$vW{LV%&MZVbiOoH<*6LMfaoQXE&YxCjwqv zR73Q;pxgrYmw;MX+<-;~&dtm#%`1#K7+1hYF5%*`{+a2KFq;vym#hnklgsX#ft>_vHs109d6(RqZdWn5wq#C*8t*)Zdi;C!b z+_<9(p^Fd*9qjW=kqv==NIFz=Qh`;B3Kr>k? zwKsJ{`yf5K@lfEuw2kF+NZH|$% zgg=#|>+-VZ^r?($Z=RJavOuycbWJ&)f9`REJA_ZW`u|BP)YxpxEPn^)4-;i>br5z( z*MwjLts(TC9SoUDm(OfssW2n<&tWJ{@Ybzl9j{gbp)Ar;c&dNUWi_eLncQso?#jOJ zc|F!L0NeY3@Bg?*N|uv_QT{Yu8R5cE?r9oY{o4{5aj)}Q3eGkJBZizbMcaBx%hoc7 z?T-W$TKI<^apUGU4q5R0`%b)uoQhiF44v8lZ8#3zKjWS~MM_@$^|>0Dw;0i;tj7N{~R!(DHKT&Xq@SW4*-3&)N$q^@`YMe{z~CFiWQkF19)q4&}sHj7cPNycGp1g0$X;*Ju`S+yn z3M~C_->h)5eUZ_YS&J1J7{(MaI5XaC5~Yd1(>~ijt!P+%^~Gu;sGl|8B3BL<;##ar z##){L3uI+|T>8|inHrSFYr+QTJU-l`%E>jI-KS|f7@Nn6gA+t>Y ztBMmkagS!G5=n>_S}0ZzQdre^lea2729BJ^8Zx@7)6Nx(F1j!-gX_mOh%Ws9MP5eq zBl-=VC?JyGc-jF#QbFk88N2BH+CPm&E9#&uu}66K=@voJ(9X21wX zDnpS{4s84b2llrt$8;ZXeZFq_vcEOm+q$=8T^StANfgnqmh7lYV01T;0}&t~KqA`p ze^c^P$wd~!0CSDn3>>lV0N6leDOeH*%9#f3)Y5~{LFCdKo**MQ>vkVi>c-chw8j%> z*qSA&&(P`mItKA#1_>&k6Y)bYVmT_8RU~`TRXJE-zyRWKFooRTpIS1vXqMxJ^JUb| zC^N@Y0&JwSxarj-olVyIzy?I{nnUA8aVMKG4wdy-N!WfWfS6Pxnv>?}KdLZ@Y1QI& zyN-4|(Q{?B=qcWLQUmUpPDkNdAdr-P%FoVIMc zA0F_U?~oWe?OGfW;8=6*$J=Fm7YIz(7+K&|=_DW|N3-E810S zdHNn>jaI5>7<$AKIXmP>@V$wXq*l7L4fJ1#Qo6J!a#b`D%~2wz)-{;?&_4!5I#^27 zGWZa>zYg>vqD|tO@eeIo&lSI;Mdj`7Ba-W~)#2Z=A=~s=U)W9B9PK$9t4X#3VjFF& z=L^rhL6_$Om}uYrs1X4*(^6or}^ zAHDgWu_6ZNKm%NOW1*p;A#)bomTKv$)rFeuj4Z};YXDH+l*vz^9C;iWTTx#El*t?4 zD{4-M&>J)gKySCClx@Y0o0ORQ-b1Obp}Xqt66MV&?L&z;iRt-Pn~DCA^h+^F7G^fr zsmjDptU!<~VRZ(l0}oI54)FxPMFy z%o7U_)dspwO8-HG*@q=67~t|#r9`u|h4+&@!HZG{%=m{h`*E{Z%_`oVZp1zQxH?~- zY`RB`z1|qBmq{jHoA$Z-B+T8y5lMuUhnBx?+5%Doy`F2ntAx&ln@#gg#(;tx%hJcs zPuB-8g4*5RF5Q~GeA{>iWsHK)?%1zC3iv0gyMUV)N70i6U~obtB+A~RL1N#^UHTkM z&LZ)@DozTRtSaYY8+Rt63YU6w;2boYc081|Q@X6#vY{yg=?hbJQWU(x&YC`#1c>5q z<${rAsw*qG0woVG&%%GrB@ZfgPQ|b9=WMugAC5_JPgvzz5lJLaY#rI)R7QH#nmVW$BAn8PhwpJ= zMyiT0P@yaoPea*g3#?NYY2gFq%;f-YLD^%4suepy#Co0&?!RLyG5W*6W=0D4zMoR{ z>3$QCmqLM^#nm3*!Xzm)lKo>SSl@tWp!G+4$UJIg9u961LB60Q(aePS5PaVC7=IO= zyp#pKOU*#vTz~S+Y7sX&K@3KaFUQQ;8O`hI!tAJeGf}D&ujA4alo>1uYB~U2QK{Hu zLk`;xHpRq(KY@>z+ye3U)mL9{9#qb1Zt_zGZfr-Jzy=n!DM;=uxoO%*l0mRV$;a%CEfX~MFtLfH_9oQ#qI@zD#G z1CuTMtTecDrW1qH;?G$&vokPzyq@QXMgDpYQ6!4NesWCm8dusB_NUHo5Q$Z&z{WRv zEZ?Qe#=DT_&+n}=h!yfTmF*chKF=lgG|Z$yR%O=Tf5F5!}=DL ziag)4g$Yh1F!TgcG>AtW(Xv4N80XsHAfLXCEz(%~Svj9uJlEx|v3^*)V^A3#9W+zw zzZ3xdGThvqmE*bh<0Syt+!^I&QvaKV?t6+nW`rcNtQm5t+ViQ?7mF%sO2~qziJ~B0 z>lCA({}P*}frL8ET*pcgvD}k?_jwE=XqbUT2nb6eQ4H^yD>t=@vhv3W!G>^MYcTer zZLH15;q|cOpi!kt95eYtl-u2%qgwLEWL>>0M#)^V%XUS%HSz-Jxw4t1zDgta1yHRD zKGN2-d$t~uERU4s%#60ZKi|-pHfdxr`T;$T4U2<)Ix@4gGz22;FA{Bs4dBa~&PcVF30F?=ez&=bo*E;_mj z4{*mBg%%*av3YMl%DChx~U?auuOOn*sm zS`NL_{V|HEsp(AzkJYlR@mZ73C{b82pOUQXq`K=j>UsI&W%HBMqOwl3eApsK_bHR@ z@Il;?;lo4!l_m_{O+@B;lx(c|xjOrLs?-RJ_=uEm$HuupajRsBAEiO1mxuSkR2EwZ zbKe6rGC8mz)>L_TZdHn+{%!kdBB;y@o2M9*W#rwLUC3*{Kp){KOcwCQ1c4D0DX3bUn$|$6S!AZ*+ ztfg&(9lf%&UJp6D8>6YIJQ+f6s^n7pVjflqH>zjGJ3i5v6Q?I2Ecw{6yKcJqe(CuM zm}Q>U584sJn00o$oAr4!pEAaL1(XLLY0Yg^Btvxre>rWN`^=f^)Y480SWzbwF{zd% z%9W;~SN)*!n}hxhLrljAAY1G%tkE*JIMC8uw3d!@VwJ-)%!g_JEzpj*HW_5Abo8!9 z=xgoiYa8u|&BV<=z1C=TqKvi@9i>vf+r(~kQYyqc7mj|rOl8Q6;ZmA&1@fk zW;9ugQi?iBQ)e`tgJfw^gCrqHywhpSq!W{qidCv%eW{LR0}Bg~m3BNaPbc;RbQNsYzvYnw=rN%M7-H1ZYm=VThC4W>^>`OBJa8Cf@2utKmuS zX{N&ygIwuZ<3=X+1HSs1#ZxbKJRkQvZ&z8zV^0rx;i7Hr?HW4QX;o8-bzCz=sJ3R% zH_y*?6y>$Na9QKXa$Ez^lW$+ZldvLo2kUfCEs(>!bTUPa0?c&O78g=_4{6xk>|}jj zR#qSq|C_OPe{nHM2Ahqct84Ozi^pr4S-?)&wY!&___rP#i@`C|GTDjvdUzaikE^C4 zs$|u!KLWH>6ARSpe5q8WsHw@(uA)jxBWbgKy}r=X^U72X40`4GJkIVw>$9xp8s4+2 z`v+Ht<7@aT1ss!u73U*fp2sra{{Eepdh?m;p&63XdR9%IHkIS?HUW6|(Bnn3kp?*k z$x|#vtAUG2WI?rrInDDx0Xi_PH+_GsRu1p)*H5I>K{it91XvvtXhJIl?Tq66SaQp{ z1K{LPIBq`=6aYFm5u<@s*nIdP`i5x{0NegDu7_IX_qf2nmGs>0c>FX$o2%i51N}Cc zNUlOgH%$y#b(n;)oFx%5GF8TwU?kqh5byPiEq^QWywmQ>R|P))n;ISr{!!9Bq`S** zFLFZ9;hgyKeVi=r{1e`0fV?|Coi+{$_-Wuv$+F0;CpREa9QoT`y4nNuFqZTz^St z`tkK#de!V95&qZKAv=MYxzg6Ifc1z`sd^yKD#ECA@-=y45c1jkxlT(%Aru7&oGy01qS`);X4A&c(p#>2 zM!^2j9NL8VTU-DUVBl`}C-0R--jW!gEAyZXLtF^%UX2!Em{}p+Ti^dZX8`i8EDoqNz3^=Mt?iCc!(GsZel)M^-u5 z=(y16%xVH(B76YT<$%|dQLns?~*g=4ID=g=cZTAaY;co(NT6*&ZX`6$^GIe_(AtVtKP zffgS`M8)4A3nGnwKi%?R^;tXhZi(;-4ndF!1xNW#9CdyK{TItx9Sw_J4vm`h$X$IG ze&A~(676S{zSsM#4p2(-R(I<`tZr$J?8@%a z2M$;wP1>XD=Ja|rZ>5~#8aSwW7OLUpCZu~t$Y^=(P~0JS3KnY6SThdyVS-J{1(AB~kg7&Ck% zgd2Wtmlch#(CDq(Gj9E=0Ap>ZYabSS$pcEIA&C^dUxaE;#uGf4PFLU5beYF8!_vM*ui=Q@L*;wyCY4xZ)vGBwaHv2|V*Rmz_ z9Vi6jfCCm=h@_v}c#AKsrmdyM+*G_xQK|WD#<;!hx2Azf#=G_K_I|hTLv6ug86~h! zCOEB?Gd(>Wn?i);tzQx*Gn1HkAq$d+KBBn|t3rE_O3fa=bRJEeY-oKZrnxOeMvGZa zDO!dvVy~5>Rp!=5+Fu5U^>0X!X_9l{G5m^8;vimr)CofTSZ)6_U;c3+{Bx<_X7PN7N{^R z8)4gRJ%6!p-cyN9^9(vO8s57zvHzP|?jWd{fh;PBoTxI)?S9LI90#e7)2skl_XBPUr{2Trci^Mgac!n}VbDYJY32Eg=BNgDa`#I#7+? zGdoz%j1}uD*4>6b@!ofHasWN;86lf1L9Co-p!1PuZ5zw9N%qc__*eAQl$`J3QqIw4 z1@O8OtEtw4+3AvIeiH3{@>s8){fXw0Dk`b*rRM@TkbXEwb(vQi@V-Ra9ZXmHipLoN z=aX3J`EXS#IJu)57IJ0rr#E?s$VtIxbz&8x2bLklS6&Yf4Ja$api5O}r5{_l zhpgGA-gq&bLZ1r4BsXCXp|^||ld}jU>>;nQ5r!LevBSRjGi;ai#XbdtUBN3my-K(C z(DYGSdb;6wGA%k;_D`cRb%jV)t-f_8zveQeaiqUvi-VR~MYX1f3gkn76N%@SgPY6Y z>~gcb<(XSQT{~2=j8$FYZ?qSV(JifUhyPw~SuX%Hv{ImNPfnb6~&V$t$%-=`WpD1B>0i7?2Xpa{KP z_YF$3{SBrZs1h6#sThurNchRlwbt%FNZl3YT^%%;VQaLwO;)E1Ce6L?G+C+FoX}|H zHnX%V3tLFcQx%}^Tt|N)|cI+*Xm4N9QX3SQEK)rVK*&$ zomE>lc01m7k_wtjy(g%2FD=TaB!$gUs;(>T2RgP!S~_v{M`@ntn5|)LTSO-03g`G! z=WtwLyJ`-U1i5gcmsb4K?K?%|l=dE;-}uvdkWi$Cst+AFb1Y5zh9b?)#0ZRz?6)Pi zU$U=Cx{q*T3K|69i?B%?-zuwL_GuzCx9K^>@T)B5(#z-0MQ>EL9ng)W;^tv%_)fe$ zX()41MLT>qxn#;3?6Y+9URR#m#-cxB?KJ9voXZEvDizq&n3UN#Ci)Hx2^z!zsb`Q& zXuJ$iUI>m&Rb&}mGzMC4UhxDc+Vm=HU{h$;^xnfg`1Zc0rSA0WJmJ@d@N|lN4jWfO z9^Nlf!e;ALA=5{F)EP_SRm>O}F9Pz=WVRDF>m`}*tUO^{yiw^?JIdgdFI1M!P%c16 za8}0lpU7G&0JbK^-FX*mP{-h* zCk8__A-{uVdBfKW^8mH8%CxaIQx>qs_%!TE5O3R**Vb!K>sYHfY^H|M`96UeoHl#R8C5ji2ku+Ux#{zQ^W1sHL(72+ z5<`mz^Z=NWd`FDT5%s9)rV|17^aal0O4&&WJmjP-Ci~SGqa#;t*>=4}zYy?YT0USR z9`Qxnohuz)`PD#0X*@LC$Un$q?e768HU2w{+cG_qfWp<87c1;X0shj(m-TS?Ve>T}d>t>L5=80Ch6qy@kD2Rd~ zaXcbG;(g|5lh6lCeu+RFLm9{vX3E9rV%?L`TniB;7$tJt53pR;XL@zBBmk;N@6At^ z70Di9)o-0n_C$a&$jVpr$F9cBM`A>5)S8Bx(o2`RnEZ3#pXLO|8ZUR8nk9W-%IFT0 z!e&w`W6-N&(yLIo_mMIPB@9aC5E?`o_ee_4;#xvg+@h<^GK?6<0Wh0_b*C0 zC!RBYC9-M`fA@k#J6ZLMlR;Bl<%R!D?U3;7P_19;pxG1Ry6;rvzr=W?bXj_hwo_A5 z3RmwT_e)7we{bp9Y9|j4ED*=03yX_-;sMD(d``2A(S_yOB(H8_*QZ%IApu!29r1@q zeugKJrcuTG4cmj|b$ztun`v+}I!nbIAceu0zZXU3*rCeOJ1u2 zd1sxSNMWeHn|}>2$1%UZpwWy~j29IMUYLoU_oB>`SNn?4iM*znFOuOW&Wq%ux~src z6OsIdOBZ;^DI^gXXY9Sld2)U{c{ldzD1S<=>y)p~!L7uXgb_F#hcAr5lL>%4vQ!Tg z#73i}rJE(L;Kgze6xg-8SzFq!5Q)QZSCT$bW!3byU=8?kI|;(8$(nKR;>|B`H-F+s zwndt5z&v319;1Rb({86yFfBCul6qV~vVWaX`82Q8pwVKQrOkik1=#5Fj}*D1=eqa0 ze3sK77Ox<5R5s@Ln-g51O#%=fHLTT!BsrZtFG*e_9bVVBsq?6*2Dg2@sO*pHNI8Ti zu>9ER{y;pxFDWB`v#g}_sP`JY)guIiZj%$8bd&fA8gwb>`p7_$C}`$knIJ(uSUYb` zx9OkojN;!~*HOK+#470ZzL=vQRU;yBi-5N0Qs4F_xbzj_!Oc)SU?3oIzmRd&!n1yL z!RvKLUOZdU`4cY(Di$><^40as`o$vb!xAk~W~TU6WG%n&Ed9{?p3cVEylDsF{Y%sn z!w}~xbQi_55#AF0Pb+!B(IaDH6H|ZQ$r?~eJ}9&cNI2+U)_9gfrj!|~&U|pBrO9(D zBHi50>5mpRc(s!~fD*mgi8r~LTFm~raa$vc`?bSKpY5z8c-?;G1v&Ny*V?_LUHr6F z3R@Z)6%}v0p7ya#?$5Vk6%@biImz;KqgYrNGn&q+&WUcEhaY>j$JRN(&~9q+>=!;j z7x2?1?!@v5_AMOEqHGJjR|%I#$9ZdFrVYIQETvqYu+mug0!PLssfRh$xi{%o>^lB! zr_*1kC3)dc%Gk&!0rV8yX!G2x2uhA(b0xF@(6ko2w@T*Q z(w+AQls*1CJ}*nG__hc{r=&x&hwbCr`Q9;ge9`xdhAIX|r~oVR-*x^us+LLV4N`Uo zmrX3r^iNKz0IRwLKfcB3g(;oJIao|4?OD}{)gLcv3?RV0mV-fzEQ`DxeaMAFsV9+Mn>!b;v$1 zo`Q6p*vG!1m6__Rh}kn##lJ zt6Ze7bK-g?ChC-(B-{B^D%^V zszGX@g)#JBHK5?-7p4CIo>NZLGVrXj9ai3A=t~yi9a-8SPq^`sY7gbcbkH+*^}zv3 z4{AKG{>)5(m-^uNj9RsG z^(%>XyWa@LXMP`LV-*Ca;@)*uRy>f5q;2&4MQNyI|D>rr`ePBGtPKFUc~dJZKQ+CN zSGxW(pPF#t5a}vls2^PQmOnTFjhGp5j#mvA=u!KcV>pg1kACjQjk%C6P{-$F!IHjK zBl%f9LVrUeH;&+&QoIW7)Qf)|L8YoJrFXr9Q=ENohf5UlEKx>6bs^{`(qy(CSYqzU z@=3B4MYRZ9g^nnFydcCI^pu3u;QU~267j^~PP1=TvDA6>h2P-F8y5oc2^|E?3SGO- z5VqjlyH658jS$%RNLx1)b(& zGf!K}QI@J~pYJt`_*`GRROLe}JWpU@-_7M(g%dB*_?{uY5}C=&aNbXFvNxJ}yV_vL zF>5Y-%)!@{H|@vlCtqUXQBni!3uaI)i@xtzOV6h+Y`i9azL@g)2+sd-64v&!5xm$J zlchwJa5S4N?4P`~WvOo{)VfVy)hO0jC&4E9UdO*>hPv_4k|%_%&Te&q)UmH~xT!|a zf*YrT%@Eo-YB5kpHgLtizQt3J!ed+a8NZ?TsnD@-UHJhR>5G4DQ!xgt811x6*T4`& z)Pn(VtrN>v-n#BnZg)^pw=aZ@e(ud65v=Jua@u6Ss#hiw-R>-*6erYnU3C{F+C?Sq zJUF_{Yk~L8!&i3eOSJ+kL9RicEtqQr#M(qH@AYFdIl{VS?WU04Z$aT?eqMcZ+2~t< zRloRo(JxY?Vg0^5FfcM8xrW)Vs$}q16seC$pXi@nb#SNBqHe1PS#kY=*>qyYc{xmR zyTcZz^guTTO%22Cw0ndOfj-%ZDT3)JX&1fvp;mw3Fkj{LLN+En1-~m6WVx5eRvw^5 zta4OnqW9*|Z?tPi6#YYx3#sU)pnwI(b?wMP!sVy+{a4COK;v8DxBJ#hR?uHMwKFJrF$=m78S?m1uZ2?hfZ9bNpd33KV16PpiE8>N4hErPEL&+8E$yA z%S&nci0GVf=0w}661(QhV;m*(v(bP0K}A!}4w3eM-%O^ynoNHK`DIuN5tiZ5m z0#-@}!rS!U%>505 zeLXQgI(>^_!E?XI#+HGcCLdPzXLfBO)5~U1*y@zRjt{(NqfqRlP#B@kq8f26;OEe? zhR~-(E#(6Vpm{sB5UmfAJD<1LlSPjmLKg#i#ji#OY-HC+(==G zwErj^lp9G=SyK$a3|AAuO~hqDO~7nwz-0Kk%Vc>=<-Gr(N*VZd-j?bn(~pVIE7PsU zN&HUyVnj~js`uV4SLn8yyeOcRfUqSiq46eJ9KO}Cclmv$7qdr=>w@@HY&&JxF$Cld z%pf!(>GMEBE()al2A$%3DR|ifbI$h9zECP4^ttU^`Ke&aQZI((_50Y^9Vp&XWw;9{ zBzeZ3OU!L;EIijy43d`B#JcYoV{zqcF5s4inC!i`n9f< zfcNLdDP=;1@KV@s3;a>)b3-aLU$Y^5rU|&VB+3_RasU@@kXp}@Pt$E+xaTsmM7cT+ zacGHAYbrxOmyfj|Vv3nZbam;;Fp6noL>cD47m9fa-vW{) z-d$X4eC>yr2N;WrdhY*HWf})&XZut6?Ns&8;vd#$weqor`%dxQQGMfwUi z+ERo1AA1M+0>TPbT+kxYu$a#Zc4H~XW+D02W&AbHU$Wo?E*vwEBeA)T&AYx zmGRZ@w$p1{G_;%YBRhYBh++x`He{hA_?ks1c3~o86k|2o-XV_rc}l>wZP3kNj%GhC zqsW=h=Rf5uih-1Ui0IP+>1(UD4>oI{1oh`y4bh5=$}Lh?5n6p8rfoaAW2sl|23z1Rk9?eXy}xZWUzeMBd0z~L_hZY2+t{NToqYAr#4!OEXHTmR+G1cg zpXxH^IS)Ox-3Cpe3h@H6Q4&R5!WdtKVITSMsIRK8x8nXtG6*FO-rbu*;g}t&vZ7AN5R^X2S?@jkpNLy% z)@_544EwUxdiwGMrUBtuj&A}XH!!X>1Sg< zKhepvv#ku+!{0x$nWC4v!waGi=#@ieMO8@05MXXS-;#2-OaJ+X=jMhvG?o7oMp9i1 zah><2^H^=)^6y)nDpJg#0&Ff;^o|566JJ`b3x?73J(enp?n&qeKK{o2xl{`?9%Rvn z*Fk6u5!LHpx8e`|TmSZKI?vM2fbX7(ifyrqla%{;$V!Hz$xEJ!j-sBXyq=SmJW6&0 z8vwjwe_w`%IzC=T;wmIStlr5yPy+iw?CT$?&NZbg!D`FeVX`*;-}%iWc87Y{fK50z z_;mm@!qDVRB(5eXi3~!T#_OQ;)2rety>qOofaXi`4UQtow?A0Vzv5KSMAuY-mFB;V z6`{{qsTwszC23(lx9N4V*(^7@R(NKre*;9uzXpdyjD2IfiNGg%|4-$;ZY*lSVyLQD zS%~;6&AvS5A?db|`9%%k;f{ZG1a~5<>`0Pa*7*j5VoJ zt!Q}QSF)e~cXAAVqbhUg7oBA~xH*Zh)E*sBIp!lJAqz6(#uIRt9!7CA93ZlD&ZKx( zza9UMzSmY9Jrl%V!VMTKe>6xLf2mER%SxE1^dW~~IE*RYvEcWS{_WYVhkRiOi%EbU zA0FT6g1e&S00Wvt1pQ1HxC9M%^(cXAWk9I*9%)t`zS(D)f&@~Mt6AwN?RBOoJPE=V z#@D7zPeb9+`?Uh6hCsmTcAn=Kl|`i)g|6QkZKtC|#C) zEkt(TDBH-5hVYKaLzC$Cy~dv^OWv5#47-`*)M%=dlAkw2ZLKvBiP1bRTmPsL+VD}+yiq{5R#YyF>8$!1Erl`YzW$1&H*r3GS?=%bYE_`^jNYAb*y>XH<;Ti+Jl z^#R3^s9E{CVgMiTOigL^D@>Q86+9jG^jPz@voU{o>vK1d>UY#)bHI6b1+iUjbV#dX zv!h?pEY?Dn;$TjYpR(o8W_lR|u9|0SJ6eZJ=rf+xZ@LQW>+2(1?k(0;mYBEI0=M;0 znRdipyW7_J`xECjud~X#ju~D@H!nRK(KNy5w%%8<-I)P; zhXGo+N*JS7l9K&yaF*{&K1^SHWi=!wdB$NwF*IoQ^HcC_8lo_Q*pm^r?sPH!Ui->R zYm75J0PUSY@#x;6gV}l$TdbH`wr=V+{}r3wsROD|3T8)SW^8HW4lKjzVJGxH2}@sz zhq9sVfjVgauUaYu(&xL0`Omh?|Ki2gyvEL#WRA3BNk%vgi}FATnW{&2wO#a%rF(aE zc>{BlMO-;}KxL6nMHu(&j$&$%XzvKmmUK}HSeE$O1ZJm(^Z>i$XN%3pvtCm$D+(ck zbTtKdWro+mMmb@?suYTI?s_?fHI{GCJH-iVL(I%8v6AUxpNrdz2K}>vO7K1fMHc02 z)~S-yFZ@*Z3sk9Qyw?0;*QYin+Bjc^(M(!*1VIl`4w5a5Y4^Tb#Vl`C+<40k>tpzV z3>xb6^&s`*(fUIy|l8~SHHc}Ed<{!k!WKBci6i#bC~&hU)e zrQpkiVxEXgNvr#o3E*+svs(`g?}hyZPG;&!jJ#m2o*YRA*q;?X5amrud^P<@-Tz+g z=lLr>OVh%_!9lsRddRK&wHMt@bK$-081+fhOy^2^xUh12+Sm4&-#cDSZ|opQoJN6NG14d(&efD*wf&BVD1exZ5re4U4~;v3T*LR! z4i41-oA4}J)T_f`XXrA!yT^k6!RpJ{1CCWVCdNG5E-jVO{7j-7=Ix@c=v3zde+D1F zh4Jw_E>WG;>l3*cTFAzEiXH<)WWH@OfWJcC@fI?SRH^Lp{?)zMpT}QbUMT2`sqE zx};>+UENsU@irRcN6f&fZsXP?m`^^^-2~a=1z%PXEvJWeIGTU}>zAcH2VT@$eoc)5 z-Iy~qwcKLj-N^8ktG}!5>O1(C3g87^ox9ARnRIUPBuWt4u~n^vnAswf+H0x%VrBN{ zXjN@_(xP@LW~ukxhFo6^t;O_`SU_J131asQ!C%Hl%%}Mf^=J__@St$Dij$Hrk}u6A z8XuQk-J$pW8p^`|5?2yXM`CF0*eh^x8dwF&Z@iGODK@4h~hC0K{Pju-FGa>D2> zwD(xk_4B#2E|>JnlTFi~-81%b>NjY=FmUx+_K&YMT^6Yo!=7o!D$ryR1J14Q8pG(G zNL~^`l7Y*V)^*Q18@5RSu3e8Znllj{sP4R_v5mt|3t;uaeX_-dH>Hl?ZvN%rKFNJb zcra4Q2MOOyJP_cS3bb=YD zCb*5aXYBAL`zR7XVhRgpF@}HmQF-z!Qwc|JYLBgc^m{+Q%q`1U z|FPDUFuibS-|cj;=Eq2@DZpaU(G z^=!e(cGshjVFF#R8bY@m9a4(L!^cz7ZBMkh!DjkJsZq>2 z@1$7Z2KL`M^1WhyoJd)zfbSzrk#IY?O-_7(toAhQfA+TmZqRo=36+ zrm-_A*M%iCCJGh!=u=!Jrg>U^>aUwAp{OFH79t^0$aQ`Q?$)xHZ`<4&@+|XMaM(?E zH}JJw&MWiDFRZrrQ)-*=#9OJWQzh4zMm{lJ@GMjs8`n(9h4G#XG}mmB0Uz!Nwhisr z*7?3l7BJ`fn`9js2Ur=t0Ci0?&Kbt#HK5A58Il$J?IZF&<`S!1%&UC^&8XjE)-j{? ztr^I?*Rrj3m%;hxs#ac=u%JI+RfTq6ig#W=F+Kg>z#ztOtk+}z%`5-)xfLw>1o@7A z`;b3>gq71%%{I>Q&UzOeHH{jKf-$r5?C0j1Q6Nvp&L60pt!r)U3An)V>-6hgZ~BVIMt zp3^LK7w2)6Y|7BFs_k=|50+NR90{Cox{N6B&{NNt*XXYyvkkKy39IPC|R6(WMgVFCr<_m0Ya1(?qBleCU>wM04sVIWey6yD7z? zXuO+mp+C|3B2cV~B7EwJj~|wrYReRm`|Kv)n;&yli{X$ZeY4U9-fV9w-QtAQ2iJ-N3>e#K&79 zNqsV`wN_{&q8QR?8CjPuZ7`N4ysao`b?_8g*~c4`IU_%6lRKoZW_%@RYk%AN6;wdU zqBn+<&_SRoZWI3J7dcf}zn7gdWbkxXhveU$i;&CEc9v`;wd4+QsO9QGV}TNB$!vx4 z8zn!oSK+I=cmN~n$X~9M4`@L#o@t*J#U1~qbXSoyLzsC*w9Vi%K?aURFx#woFZ2;1 zIY@g#m$7>m@u>`Y$Js%9VvULkp`2SD{;y$)#^wCMSncupl2DINTsoa%KB#W)X7t!I z#h+8XTa`bXkgYH&>VQLt3R2C5+lzu-zDQ7K5prt6L4dw15+78Mru}X#1TE*QUl=n! zNx{-YY$e%e^Mu|Vquu9|o2y5kSph?HkRdbr3~TU3z+5`@7kAXwAOm2=1>;o;-e#Qu zCj548X<8|5do_$c=j<#!J=4tHF~+R)L*bSNu5{vqpP2zA)Mc=dgWV_*naQACTl*(* z_$fvAHz0-LGE*)w)DFVQLn6}<4e_sv?V~ITbQ@;LO`=A;2I0{6Y+`hr2g+I4>0z^> zSK5?excK$PHf`CN!t8~Qw^-RX}vDZjqf>|G67tyt-0rTC#dnp`X*k8)Ll>RSIxwpVSG9h zH;}x8otLxmyUAO!^xr|!W6WgvGpnWd$#}FHkpn zv@LoxiIWdhR`ak+&qc_W_WF7zjrR;UA@2*ZcUb-)C9cPw?LWJa8=S2U>#DCy@1J)4 z6H~EWz_Yzw%4MSxNmHe2R-mpc6$`1A+&XzdHfLQFN;N>a}xTeW^eev`6iJf8?C zIQvH^!cuiQY&c9TsMok&usttSQd)(MAWOVGoAzJpU?lkb{J}>@Kg*s9S+|pdEWa^C z(NEOTfXcHisLnpSC{~hJWsLKWWe9Li!j@Vsz%F-;i$eidZNv?78(K7tkuqu5{^;7%B|Rmd&*2ArGbFV4muQfmD4PX85mP6byjaf zvir#+yPdOC()0@QCgP14yoy51uVUkAG8uF1r!E7@?G=3cm6Es_^SSc#Ov_N;&=tMw zejgMq+pLUgOpjJ7JoT2qU6n%GC&S5rvFL#kbwd%v-!&5AcP2qKGITr_*KY zN?>yjs6h;UrnqppIQRmvT)lLs6X0))W8o^|=Pi?-9yUFFgo9cSZ`zDm4mNYtGZ5uaZc2 z82h9P^_Oh{`b>V9OBDC~+=@&kGrHQ6A@16YJdsD@T|u=bz8i2>``wJ+EDtHw=#*$S z7Hv#d;eCICg9W)#mg8?oCs#kZLHiAXlp=}g#B-{j7FnOj4w`iJ2wB&&?XjX>UZQom zVrX&1M;6u0ac2le=0(ZGTTT9~9JD&v`D_~lOq9n#{*w|r7<*)44u%=}-P`-+%*a%5}9 ze8W$k`&ILDoe2j+$uCVwwpmFhOeyP^TE~Wa&uR|8a(dCkU($*|hn^S*xh)&-#cG>l zgF@yqPvO<0XJ{+(a3{GSZaMy1_EIyCe0tGACkwi(0f1zF^`)b5&#v8_0ijv!n=b2k5M?V70{FJ0RH)*%3kq1s$&(^dMo zSTF&?%4h7J+!zIFI2J7?g=-0DqwQ(*PCdM^QNFTRJbm#oRoCkIh>en#!F8RIxlPne zG9QQqJPxRRrI9i33iqe}LrtZx?W;9NN2Y;4-F8bycCmG`P{pz%6MdNgV){y?BLNZ! zeVd?-%8sXOKk)k9_(sEa-cH2w7lsR4crM3n@vEk0;yhIFF1qiN2l()cD>qKdFmF8iuC zHb|&NQ1hYImR1hb{T9DqS&u0$`Xw%Vy z9LiLm;!O=p(d{6T6=I7ULv4+H^o|Xl{WSh~9sej3oBSKH7kbYXav##EEL8if!=u%+ zc+lmUVH0_~x<&g7C;l^kCU0IimYU}vZSPH%HYEd&?6rQ)?(iGiXjk1_fqbBVLMEI+ zJyq9cEwnnc@0U`Pg;`Zk!_bOesNn~-0bO$%GHB(1NNT#>YNPg`TggsIj+NnJs)s$l zm7SL2?d^1uSGP_9Q8*{VU29MPso>YIeHg@D=jJ+S8kky&Dp3a}`XG@7Es-17ksV%% z?)(4-hUd_ONzG!?cLm%Z)+VOqT5zo_?{sg$_AJCr1|;noAOfoxLMI*vj+iuq7vITy3;s)CMrKG7+7`B z`1e;`YUpy-$hBfA^iBDC>KHuPv(u$(8lwxk?Wm`l#jdAW^>}T$r+xxZwL#xPWU^g= z^$TqUvicp3?iyO+**EVIL{35&ot*3n&HJLJS2ykIak zTDyPqmFO7=>ZPu{WvIJ0 z_<=3aDw)~c_(~YJsXc(5o%{ml2{@ecwn0zd)Xv!YjiGv>$5*e8D z<=E48qcDI9Cvu3?u`K8(P=2_+m79+J-g|6iv=LvW=eZ_`&$x0c*O2=4?bf@|-_uFj zTn-nLHpb<)|T4vj#-@Wxz z&!X~4i%ao?+EajyFfh7Y2Qg176>i4}LY`Xj#8ql8JU+!w*foE32u2iX7nvJa{;f?Q z`@pc>s4Wq-W=NFMLf7Xb8FBWGd^_&g3OgrxfP9TyvAg{qCuHV#4nF^55^7ha$9%2( zV6DesrRHEA@`dK$M3KFXmRxZFf5>TVm+Vs(%^n#6;HC|mp23K|2R)MhJ5RFj8#^4$ zR=u((iW*=Ci%JpiN|n}MiI}bKyuG30I*vlZoI77U5{h)Taf0)*BJ^Rv^Mn?Jk4Yb& zc4eXwhn!d6jL9BuYoWIuMltr!sfqD=u_-=n1#nq3m&OtLv%>1w&c3bHzS6%Aoi+_6 zVUn%;>bwz&7-zp$&us3%FYV_jA1c1DAn~2E-XKF)Gb3alNusD49{LYAAJis8BXWP* z*)8EQQ1yyX^)gWPF;L0uSlIKJ+Vi;Br;n5Ole2-N1mxJ09y(B80Xe=}-}%m-381%c zj<#~zXc#szk1bQ{D53TFs|Rpj6pFh3+BMGCqKH>g8Q3OfFYfw1>X=u>c3e93VCxeU zT>!2DN|a@MA7teI z@dUrqV`9Uv;}%L(SGHem=xf+{=B7-YpM)tY&?oRKnj3c-BUiKJ#e}?!{!{B*)Y3_X zkXZG$(6`K}xthNwK=EvwiRZ`qbw&Nt1Uy^R2_3)aoqy^Yj?x2Yc@n<7t;)i~MchTJ zCrKCz8o<)#qP$j0cs2aylO0MQ8?ODIst#?N5|cs;Ew<_g*#7g+ieS3Yx9k#!jVyY@ zX`JQL_nu`Dzifm&%@SH-L!ZYb;q^35Z3eABZxh6avSuOk63f;>4J}f{xo9$GV9k5G zQmzFi3)479+diPcJj3*-1zsIWr1^pAFk7ZOsq(d4?#n{1q7C7$4f9;71VggyT}G~2 z=PI8kx7VKvUlb%_P!!Yuo)WY=nV9O1?41Zsj!sB!pr6A?U%vCq8uwy{q(h&hOOMfB zG;OG}AJ%WeW1bg94j1dL;siVS@E%=|+A2!FFrP;hT!jPnc>cR)w084g`4uFXor({X z>ET+Y@-#7>Eurg|>P`*GkoA{|9gm&6&;QQ-H6V_8ftWn$%iqM6(ICRIE6qmA0@3gC zDutEHEBHf6?er2pWaK}qZrO3jU2$#IEFG(Tu$~Ju{Pj6E>E6HbT5P&xQ?mK#7o~MS z?zp+HNIxD}!7buo!~I&YA;}`%J*C6>(J^N%K+4vet-1?Zb`^hk$~C7OV6$|N!NRlk zvYv{$g-w!n%>r(POrC7&urk$dIQ)R?FNKXnr?k~fhxncnZ{xifF2wZ*+PLh^C2^3c z@6SNJ7gV-JWm$(7+nMWwI4qloDnnyLNaX%`FBAy8>j13a`377m!TZJC2Rpk3RGoo%ccd-uhZ5j9mbs#bC($K zeOWq$O20m4D={&Iway+EwjZ?d9Y0Zr^QeUy>=A) z?~nEXGW#pQPhsXP&o{MHbv`*l|#ZKkt=Ly6HnH@;_&X)vYQ!SnmB!N zO#A;!pUpSkIXD6|{H(TRVDtx!*C;DRqpklrWk`OV+PhJ2Z%+(na_YCKzMx_DpT_+9 z_=xoJC&dWM<||P8T{v!Hkl0=Nv{3~4vF{V$e;l0+oAqm4^1H>t;^Yj{o6-8jp#1;q z&-HV@&r@O@yYZ@Q^7Xa0a{^d(Oy7pHLm;|?WRmcCGB|S^%{97e%QM19=`qozBH5x~ z!EqHc%Fgb7ao(~*R^hUzP3!kBi;x^F;rvukM`t&h6&&S%x0Xu~1RU+{?7=IMn7wSK zCJb-+$63Som_bHwbl5=h#1-C!t-MLlhq=Uq#d2F17Lu)Nn~JGHoD-sLdsvh0&qvlE zxRJ)?f_+Q4EvV#teKjE74*C`Dl|4H6KL~YV>&GPZU1;6m(ziM`Mb2;K^x?r5Oz-4wx?8oVgL~t<3t>u zEmile8WU_5+{D*dyj8djoq3$0g^fSD!}YiPfv9R;51a8i)7Wwny(sGuwqOs;(OXCEv(7`uJUlMa)%Av z1An2S$X=NHeh8vKqSF~CAGDi1(k5Rl@8!KiE0c>RNFB}g^*!u7=HWE*I40E2SxCM+;}zGcN-4r z{1-+4_8mQJak{KcVN0I!ws$?;7eDl8-ecX=a|T8;dP*kY{SF6b`uY;436!kHh#sQm z20q|B-Key?Dq#Q}_M!F+)xKkgcW+IX5ALo7onGTfe}1uY)fT{i^iVdMp2BvLhfk@6 z%X~9RF4=`FbEfLnb+pN=qQXU5>ui4knCpAoT@tokSqZe_#Z=atiytx0I4pT1?)Q93 zmMc(J`Zs3PANM~2nn|vqZ|uaa73?k)?t^SChM!9k2E=CmD$kE}3S1BHp2vgIiH#gu zWuoP(>VHv3a-7Sl>t2cdyo4j*1=L^W)cI+j3uX_r+qbvGwPzI<zC2t}H zZ#BgL!o2BL+`rD#sr-EPHDUu=$xlBasr?SRMtyJEpl`Q_CM_-Pf8_Nbi>!>Y1;4xY zYfFOM{-9eKnL`&mT0r8Xb4mJol7%o|#Jp;|JYN55`rc265a(ji=_dB5iJ{2cNTAtq zsfJf)t|N79zbojwB13oi-Kf#u6UO5UjW1IGi-4NaJAQeoUv%$?q>0XMG%~0&wC}nN z;A|9_Vn}?Z@#!d&3|hlcN(kvz+VP*u(Xt_9@xQR5VFs42d5{SFSSXZs`>prr*Y#VhnV#{Ly`G2nPD=V->*UdDa5B|$g|Ff zT8B^f`Dvp^B{vC<&n>)(yRtA0V3at{YTvkexQ3==WOVkpJiA--<7s$0mr4;*#L* zC2ZRhovXDYGbLXWI@BtU^D(+CTX&KC{9d8kr1cp#QGaMKf;OA!1G9{dtmuvZVUfnXai?i{pQ)N%!M3pzW8+RK=Bup`BU?j{hpD5S$yY zP#V6^)_$QY3JFBGWSWqr*@3-{ywCN;xDNf+smgEd2FP%h;Gu#8PKA$`Y{;iSI3^O1 zG!#|&le^$m^nZ$LfFp8+5|rA|L1>&5yh6jCOH`FAc8? zn2Cs(lp19@UOjRVx;FUazuU@hBP9Hls*Pb&rb@5vXUdE-E zFeki#I`okP-pv2ay-hPMl5(tLGx|-VdysHqC{xOGs_L8lPpQJ*w*@s@dxVmG>&=ii z5idV?WGZ}F5;V!y-gU{NEal(a#@j@OiPW7gV$quoZg^Bbnk&qcueS^F4 z=Jqtn+vSH|Ldk1mYX=*-G=N`zCj{li1)PZ_X3s3@on5qr9?5BAvC$h~*s@yoXld%c zxPAePG}Q~YOeKDs2puIsK79+^({x%`rPrTl@u82nj8FBTgw5h8N_|z|Xz}YMRCzJ+9v=wo$WzX}OPm92` z%M#ykp)W>k>X8Op!ndw)!PY~5f0*b4u_FU68v!<;R?zzv`@Pygv9XJ2$bWmKDg`ga z-qL$pwcjt$v!8j%n}BW9NpiLyOVW*KvF_WtdBBQotvq%AKGOANRJqEVZhw8Jr|IJb zb6cvey|VIjgq{AR{`{YM6f%R$_VFl@*D8X1o?CzI{xY3UNYb+7M2f}vE#UYw$JjxC#qqK=Kb?+OtM+$LP@r&tj2 zYqz6C(T?LZ=;eo#Jv#Egbr#)zMlQ1l@72Q9BV=h|qmRd4LyR;@ zKk`Z~RHu(IyF8TaDb(mz=rX46FQL#MMaue)cUI~e&6x&k*2Co!@(+NJZTDI9le~9$ zT7QC`Rp;Z|pcpi21H0J80$*=Eb%I~O0M=^e?e^u*AYP-dLID$xg zt1AhChnaTV*4kaS9o^Jjl^ZwTraqA$T>856UV!0#sAABB_U@dR5ip1SFeOm>;2)Ts z_GDRsBQ>N}7Vd4#eaHFF`$IXNKz-3c*LDQl9V5`f zfGe+uf1KE85IXN!FJv*EMW5@+{OhUqz#oM>y*u}7OsKinWe?}2OP*(AklnqV+h{uu z{9L&0;OwB_Vh}uf)sf2%zs7VVJs3<*i*Y7A>j420yOoeDCld4rp+b(rAYK?v`fsD=NNu;UKd zS*O#_W32C^4dy%)Px${6Jks2A#Rfq0_MwD#Tb#pBUm>Rr-}@WML%0%6icHT!4*h?h zI(#oN@zaZV-<((RfjJaFhu!EIR_uWC<2R7`3Q6(y07O$XFYND}Hrku3`W2 zf2ofk|Hc7iDt+1T`FY7c-v8dRKR-A^{GoD{ZH)P{*VIrZU8$M)O7?%_oz98=2@#Zm z*Bc1R)>Y2}gnj=b1m*YxL2w-yyeJUk0DwgYEgm6wY_dQ8N95mddQ}v8=AY`;x#Rjd zwS85;sUF=M`m$<6a8zS9p)SzU@F=7T2ZhVJ#}9h=JKCm1_ve9Q89(mDdV7C0x(EG= zfZ-3qyN}bk1YmTKef+^EYK4MT5k&BduHtfElKgl5Aez!!^eALiq@de^$d@nND*s!f zAr=Z379NU0a9Z&6TPxpBbmQrv-5~5>7FD^UG`a4Trz@r=gwEvNBn5;0{|iGnpm$JL z(0B(>-qxY+1=Zm$mH*YNsOW;;2Kh~tL(r6SvyRnPhF>Ri+T7?qUN`hcO`w7xqm}6n z)`?+uASey9nN`@_Q?^Mf*ekd5XuV@QSh_>w^yI$)NJ|4s1r;(v!QOGex#~BNmlV)J zm}h#-y~5fd2~sg!Ti1IlDyMNCMg8(oeBVJ0Khf;b5V*(e9r|%BE7Kcse8ez(f8Jxg z73jyJ&t&>-(kKtK-o(d#&ybND@+CHyBv9>3Iw)Ta-|qzcH-{rrk_L)t)fY*CZ+KE~_T05-1Gn+nY_=)R((*You zM`jugCIa=^FOFfZnBThV`#Jrc-HOI*G>O3P=Enn@RUcU*sDk=8o&Vz?J{>3+I06Ok zQ%h2zA)?LWHp}x?6+XQ>vP(6u7}DstLUjUAV)>IpJFFUx-%mVh7}2Z~XsP;P#WHRe zCVz?2j&gTXwKvLt{?|WFA{-vXi3*eDvEERZwxyCgyRwoi%J^@PpwZ)?UkW@>SO<Z{|L`oI6j5D6&@ zkPuKxY2K8Qf=DUI=mueQh=hQ`9TG|_jg+7h0Y{JS6p;|5kCcvy3t!z`L-RZmnci3!s8JctZ3y##2Gg0~p`0s53Gn$Oc4-nBHMY=M@NhX2w8kPLigGAt&_TUj zz<>v)yv#4Fu{o>7lkBBM$ZXZ+npnZ=tuoT<(J{h+05u9N!rG%|O#q9j@|-yG9Gk<} zMcOO2;WfW{2jre;EMM68(0)#}%;R(BFhDp~t1DXK!8`9*7q=fuJ(SQo_O+Pco0`%r zjQSrNQA?i@7_D+?z=M`>xM?mOOt>FpZQ0^6dtp#>2=J8ZQD#yt_nBUDqTC`=WH4C7 zo}ZX62jp-=>4?ULQnPbIH$gsR~_-9ugSZA=qIO|UB6{2>QAnjj2)F+ ztmWCe=#Z+AeG5FCCJ7C$x9>^pDDH*SIG(jiC!D%7VIx7Rx9Z6-t_%B8mrIrYYJ^%P zZN*Eq7$wly%Ji&p%OpOILWQtng?WmiH&RtK&ZP$q;VB?A#@Ua=U_c-F1p66?w;qwf zMag+K|EV_^?nT(I)&xREc|Hf*B`Ah@UTBK6M?B^x1_-@9M~C7f5&;j?hLnowj*dYB znBdhy??Q%G$Lx5X{5XsLvRc!2eTzITn2FT}pA!3FBN!A}l?DNDPMGynQAi`tI-x14 zoTu+Qis!mi#uwde4}i*Mk7s?GDI{I;onc8^lLTFf^S)Xjz2M(3p1GuFwrYUXfO6tL za2kpsBkX`+&u{f+#f{JT#g?HKTIehnrhReF#qKdtnuBK#ZyS?N(f?&?Iu`o?1f)To zP)`yNIU6yNgejVtbb2Nl&B0}ITR1ZFBv^PC@Tpn|n}ur>U;e(G3=?)1n|6!2lR(=g z8^rNeiFptdzVAP|?|={Sj#&Yc5OYcdc*vElXgRbB+?9)9E}B?#f|cGt&9N(@xq;<1N!Lpxl>}#nWP~Jw_hYYZi_g;6+JpqMwp*10&Ph;| z)TcqS5ST!U5e2i#&5JSIadbBC7Fg_B(rUyRtDq$p9&rZm@2psPt!yr>a}g$Zf=*qA zHUvpTrAk1y_&3d8A_b5qh>Rqdj>5h(idiJFM9NGsY19e%0BP6?zvPuxx%M_>91Jq? z#nE+jba-V(Jg9i?9J5L`2{iAFU)99~b-RzbRgiQq%IAzyV&0e}Euy{<`JdO_q={X2 zx^8+}?ryZf(HY^RcWWyXX7hbmPnZfrwK+u#_Ia->hqiV^jCXYCJ!V($DAN>8y`a*~ z;*FQN`D%Xl@5^bwz9*v%pxiux9bc;UoM8EBOM>oqsR?F^z>oP`ZN0T`a-e?BC}VPy zlyDeLZ~5S4Te)!MsL7xN(oPja2+9rWFT6H`rWg=7j`*v-t`#O(9X z^6XNm3V^6ku~9%v(Vi%?g^DxrfeT^P*wvj-k$H<{3j!Anh$kamEEMv-bQ_*@vJDrK zn?+x*>GziGB3>e=AUzhL%e=wcQTC<3&&WRjAe-19rQ$fe-W|dbS$ZA_S1{@|@al$1B}?HMUtDXMqgLBF{^`MUbjH3!(h8 zqIBFtS@b|T7oFVN`hX@}ZpMx3yp^w;^HowV8ue!fAa?Lu9Y<6*EZH6# zlJsD4BIM}Dg6?t>tez_PD!V@=Leno+#fQwe1WZh$|cF;9%a<{(QsS;o0M7j zw|zh_sj}*kUgbK-%*@*L19R)OJI^F|dM6sc1f6aHvt-Fzd6eFLeQx8WAt@^(HOCG&H?Y`# z7N=T^fTYRue-|t4h|K%vFq@nDoKtRfNqcgon42(x`1|{J+0$b%M^(t~hFks%4obK$ zCwC*>F7L&$L&9lDSr`cy=UEvL@rq@ylwJRR*gvlDVyEo2+Q8?t6$Xp?B^T`M2%{on zO?O9G`BS8UE+6@mj;#Hsqdx2z~ipKV8LBZcK7erQxtnQ^oDB*eO0Y{1l$U*1pgHLxKwhp?Lgs> zU7eo8Sp{Z2onhLS<5X^pjm4z=m++|u1ot0)A9*gGjp(|32k

_^*Op=ehLem&S7wf^3n*plF6ce$4$fNAqRcjze3la?yN zeG++NPLMy7tdCDlrM_eJ7=lt6Pv>Pv>vLZ3z7`v_6oT@%+*ROwhHuj>H za367SaG*3(8L;P=J=utFHY^%>H8e#e&1OBDgQ6X~G?LkL6R4)UR`3wJInh z@@wfa+KStAb?Hkv{I)DtBFJo6^0jw(M_OTJVd6U~R8n0+IuElNq%%5`RY3ht<+Vwn zJW^lU_EyK}*vVpU;aN;f%=e6}^ocJ?Q4;CD>>##`KEOu6R&6!87x6wgd*N{NFWw00 ziww48S37Qeb=Y(-D$k;SPv<+Pbgt}DxH81uR8vhXuIzi-czNVP4TJv)%|>@1Q2&pW z8i?h7KBrnBLpG*r;_ZE@%Kd6YtBR5iSQh-yU%+ki{SN#0$Mjd#4SpK7Fm_7|T(VPM z|KdDR?hXu!WT)=$6dAc16peHuYE-P-B|nV`CZDHqsl4bewlotm0H-SVsgV?`%*EQK zFH^i}nQ1k?etrk_;AFEi|EYt`9H{^DYJRgF_}x@}U0tx}UW%|a)ZgWB>*YXSA2B~W zI~y}y>a}niqN{}a^HS%Djw035BBN$2lmU?47ifjcOL!0KkrX%kEF2qGx!0jTAc^tY zE3Rb|BSWYe72iknIvaG!< zU)B`WM+YIMZ*BE&20t@Q%qQbwLt2NUQ zlkH|dR75jxif(U0PtD@)y{A#&R?a2C*Myu?YEg2fV}M#JQ^%F7F>4?ADDn(qj&XceovT zxRPX3%5IXL$@elOdFaXHkjZ4-#u_JLB|zP!>C9~w&vE*9dJL(bLhZKWaq z`c}gtdI%+Ji8qI%Y+PP`wa3)Khv9r(&yVTZr&hB>X55MMcc|Gzqp&ESJ@}TKnna*f z)6I5HW$BF8y%!HZa4*zBe0=GH-L zg7@u)6GOI08hkChn+tdw_oU3-n*rF3WcW|P@>XguFQv)-!)JEbB3*KCG~bv1p_LUB zW!S1Eh$w98n1aWgSk`;|RkdZ5uY0*Y!>JfB-?4dPEXZ2jab>)7`eolFI)~=<*tF&LK=BnWnwHz2v)5Q8l?AV?=s% zqo+4mmF-4cmyP2P@tUbQp}D5znq(w^tVpfJTzk3@m|j!EeKgXZx+!a~5HChsAljj~ zr+PYfHQv@aS~jj!INDR*kE*+js-49hoX*@e^zm&w)l}8+<&lp9QC=2o+A>0Ysd3jl zUt679gB`xtAZVjvBf33eEAQJD7xYJtn>T=f&i(!U`eaz{g6zP(D$Dvz<`R+Hgb{wh zQLWtMN3Kg&U5XVYMc9}0aqbIs^eM>Sbh&+d<#fY}f^PiIz_J{m{GzQ% z57(9TA(&lzX1J!*9_%0_aZ#smY>M*lIKhm;R?B?6szU6;p~|53kp=S@gHDsVACU&5 zPB&0}^fpv+#g*hGkus0yApxYQbAR(v;mDdo@wy@!-kTz7u@!v~gyG=eu+&Ex;Vs63 z4q)5X08z`ia)>$OA$tPgH#8eaEKQYZG&2JOf?XZPuazHNQ_~g<7TkY8ver6$86kRM zT6gf_BDk+ffe$`AXm>u_`+>!RJVX1gkDblHn<6T&`0Hs`O*liodu0_H)(=5wQ6nmt zM#1{m))Ff=sAc`4k#C~}-jZBJ{@i_vb0aWL`z~GpNdOl2`{B4VawiXuQ*#t<8bmb! zlRljPR%AuZQ6l{XFlY*UlS4wEMhayET=;UhB{*z(%ydA`M2HC^t0?ZJUS{GJwQ!Wji8d>+ z2OJ$7U%5>wx=bBJnxU4hwp=x75QTv=)vw)A+xR!CU@<*JV{D7JVBD|>Tnec zH)=g%W^LfRIXKtuc{f!{Mn+01s0lzKmjb%ItenIIv-sJklq*4BclHqh%SN6HH(h*D z>yw;nUq7yu1*4hL83loqMe|M0Xq20Dz5PjQ*Jfvd;<-tqMcGm-xbtO`gJXeo>2A(6 zqRooj75uTY#J;{haMA0I0dIjksUw|1lW-T*?Q@ z2|iOf{GDP51GEf7xqevZriz!T9C_F1j2>IH79sTlJ)i+2agd5)=!n zfDsoEZ7M#xuan;cy$pzXwcFe&emmc&==bDg)aXZ8LD)*bV#9_CB$-s)s$$_thD9+n z1aIY>;j|yHzPBp15Ha@qFMUEbu0E8@BuEc;Ox+waO8C}_-L-;>nZgc$iSXIehQUvt=CmnS0M)Bi z5H)&IezJ7KPE1H>&MR;teqDR$SZ0#45e6~P628zRAfX&4Ze#lKWy8W|198)HA(WHA zb#~x+RyE&3hm_hTh@ORK_-)RQm)RNIO%5+`$P+MQ(SV^0QX1$@FPV`1;C|>Q@#Q3O z2V8O>8CZ_;Dk37HiTKPDSO?szi8ryYME3)RxqJ%e3Io-4>a6ECE!Mb;73XE*Zu9Z+ zCHx+|TQD>-wuN?&eyvDaxpJ&~cC2J)evq&<-zLVB*f%drzp$`y=`#?@#T93#g}egJ zYkmCWi7+iq0i~)bHOU*U*@D{0!(;B;u-hG57wDHHB#$!C=}2z?lweSxo;$HerMX!$ z3|&?2{jVj&{)s(G&rDS0-zc`MN=j;H_AtnrHFBW8)ApLU+`Y()5-DnY9Mo=*t7=Kh z%K|r4waupzG(W*Fl#*V14vH2!SECF&esy{_J3rfBa1`b1TpIdRGAc`SeJr)wVnS0|HVgc zTKw5Xr%@tYd}KT=k#0`3;X^yd6yL()Hgyq|E}BZ+E>-}Q4r^ITWxjr7;^t8Nw((?7 zHm<^+mDL$aLh<4XNR0Z1D(SJgsrvh{36aqEPfL=wda+fTb-I zep}O;0HD;8K>camoOlvVofJFpyM_!L>@xJ{Po+KNhYE|eMKV|w4EhT%AQxCz|8Xb= z1iI06RkGDA7a?$bns&eb3@UF^y?<33ri4r~yZMVI9BN+W#vKJNb~i~uKR30WSgB%lK~cDFg< z9KgYnhv%i{58P}<&{v?b;sSO$zAvk`wM%HMALRfjXR3zQm8PT4c7~t@w&pVL?l@Ey z2glzkcgIGz(SSoaOl=u6v~RfL7G$+Tfo}}gLNsS-pG*9G6k_`6Sq$~kd9k6t*M#v28UEG% z!`K*|Uo17Ici`5sGVmDc>+-{6fA3v1amM=B8m7`}!ciBKh7ifFDoQ6M!0_8v;-9x^f^)gZuT@e|S`gr7O@ z?Wz1HshQ|0P8WQYC%NTWV9=2SF_WaY*mxja;!c)B*moiH$UlCcmehhpE4tDYSpJ*9 z3_DkttOnLbL)}q!(0zPno6Ma9saH>#ff9q&I}N4Kdy^oxXh< zf5f%F7HX Date: Wed, 8 May 2013 14:34:46 -0700 Subject: [PATCH 008/412] Style router box --- ui/images/sprites.png | Bin 194177 -> 193277 bytes ui/modules/vpc/vpc.css | 33 +++++++++++++++++++++++++++++++-- ui/modules/vpc/vpc.js | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ui/images/sprites.png b/ui/images/sprites.png index 156df1ae30d6f813272804c48642f5af8da00af5..f3c8226b64a57b5bdafa114f8b2050de85a225a5 100644 GIT binary patch literal 193277 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

GK+At)*M#b~0!>{IqLqpp%Jp%Fu^Dl^JY0Vk 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

ctn4T|KFZS{Z6Z4!1U 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

3lBp;caQy?{Alb0+<=M(7OT< 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%0AEb%%{5pd`3)D369ccSoV}ms2z37 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 literal 194177 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

  • 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%0DH)j|ok;)WI5(qP6q?T2QO&du0Ks*!l)I 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

    DvSsN-8#Tf;UKP+P! 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%0A_Sn;f34nstiHp3t)+(N;%$Zu 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 diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index f6ede66bd9b..a7ef13eadee 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -248,17 +248,42 @@ } .vpc-network-chart .tier-item.router .header { - background: #908F8F; padding: 15px 0 14px; border-bottom: 1px solid #808080; + background: #C3C6C9; +/*Old browsers*/ + background: -moz-linear-gradient(top, #c3c6c9 0%, #909497 100%); +/*FF3.6+*/ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c3c6c9), color-stop(100%,#909497)); +/*Chrome,Safari4+*/ + background: -webkit-linear-gradient(top, #c3c6c9 0%,#909497 100%); +/*Chrome10+,Safari5.1+*/ + background: -o-linear-gradient(top, #c3c6c9 0%,#909497 100%); +/*Opera 11.10+*/ + background: -ms-linear-gradient(top, #c3c6c9 0%,#909497 100%); +/*IE10+*/ + background: linear-gradient(to bottom, #c3c6c9 0%,#909497 100%); +/*W3C*/ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c3c6c9', endColorstr='#909497',GradientType=0 ); +/*IE6-8*/ } .vpc-network-chart .tier-item.router .header .title { width: 212px; + margin-top: 3px; } .vpc-network-chart .tier-item.router .header .title span { - padding: 0 0 0 32px; + padding: 0 0 0 50px; +} + +.vpc-network-chart .tier-item.router .header span.icon { + background: url(../../images/sprites.png) -589px -1175px; + padding: 10px 10px 10px 20px; + float: left; + position: absolute; + top: 7px; + left: 10px; } .vpc-network-chart .tier-item.router .dashboard-item { @@ -278,3 +303,7 @@ text-shadow: 0px 1px #000000; } +.vpc-network-chart .tier-item.router .dashboard-item:hover { + background-color: #818181; +} + diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 0f0d3f909d7..2b01648e037 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -35,6 +35,7 @@ }).addClass('router'); $router.find('.info, .detail-link').remove(); + $router.find('.header').prepend($('').addClass('icon').html(' ')); return $router; }, From 3f22b6a3e584002f19f5fa1877c57158b65b598c Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 10 May 2013 15:15:45 -0700 Subject: [PATCH 009/412] Create panel on click of dashboard item --- ui/modules/vpc/vpc.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 2b01648e037..a3922b3ba4f 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -55,11 +55,29 @@ var $dashboardItem = $('

    ').addClass('dashboard-item'); var $name = $('
    ').addClass('name').append($('')); var $total = $('
    ').addClass('total').append($('')); + var id = dashboardItem.id; $name.find('span').html(dashboardItem.name); $total.find('span').html(dashboardItem.total); $dashboardItem.append($total, $name); $dashboardItem.appendTo($dashboard); + + $dashboardItem.click(function() { + $('#browser .container').cloudBrowser('addPanel', { + title: dashboardItem.name, + maximizeIfSelected: true, + complete: function($panel) { + var section = cloudStack.vpc.sections[id]; + var $section = $('
    '); + + if (section.listView) { + $section.listView(section); + } + + $section.appendTo($panel); + } + }); + }); }); return $dashboard; @@ -92,18 +110,22 @@ tier: tier, dashboardItems: [ { + id: 'tierLoadBalancers', name: 'Load balancers', total: 5 }, { + id: 'tierPortForwarders', name: 'Port forwarders', total: 4 }, { + id: 'tierStaticNATs', name: 'Static NATs', total: 3 }, { + id: 'tierVMs', name: 'Virtual Machines', total: 300 } @@ -123,18 +145,22 @@ $router = elems.router({ dashboardItems: [ { + id: 'privateGateways', name: 'Private gateways', total: 1 }, { + id: 'publicIPs', name: 'Public IP addresses', total: 2 }, { + id: 'siteToSiteVPNs', name: 'Site-to-site VPNs', total: 3 }, { + id: 'networkACLLists', name: 'Network ACL lists', total: 2 } From 55ac9c1b0f5fa4299f1b0613c25ce9f6d8415e31 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 10 May 2013 15:25:46 -0700 Subject: [PATCH 010/412] Add private gateway section to new chart --- ui/modules/vpc/vpc.js | 13 ++++++++++++- ui/scripts/vpc.js | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index a3922b3ba4f..ac596266d05 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -8,6 +8,7 @@ var $title = $('
    ').addClass('title').append($('')); var $content = $('
    ').addClass('content'); var $dashboard = elems.dashboard({ + context: args.context, dashboardItems: dashboardItems }); var $detailLink = $('
    ').addClass('detail-link'); @@ -28,6 +29,7 @@ router: function(args) { var $router = elems.tier({ + context: args.context, tier: { name: 'Router', }, @@ -50,6 +52,7 @@ dashboard: function(args) { var $dashboard = $('
    ').addClass('dashboard'); + var context = args.context; $(args.dashboardItems).map(function(index, dashboardItem) { var $dashboardItem = $('
    ').addClass('dashboard-item'); @@ -70,8 +73,14 @@ var section = cloudStack.vpc.sections[id]; var $section = $('
    '); + if ($.isFunction(section)) { + section = cloudStack.vpc.sections[id]() + } + if (section.listView) { - $section.listView(section); + $section.listView($.extend(true, {}, section, { + context: context + })); } $section.appendTo($panel); @@ -107,6 +116,7 @@ $(tiers).map(function(index, tier) { var $tier = elems.tier({ + context: context, tier: tier, dashboardItems: [ { @@ -143,6 +153,7 @@ // Router $router = elems.router({ + context: context, dashboardItems: [ { id: 'privateGateways', diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index dc26265c232..6033f50f6f1 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -277,6 +277,14 @@ }; cloudStack.vpc = { + // nTier sections + sections: { + // Private gateways + privateGateways: function() { + return cloudStack.vpc.gateways.listView() + } + }, + routerDetailView: function() { return { title: 'VPC router details', From daf392685ae5cb4afb5ade906e7eed0a088d2817 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 10 May 2013 15:41:44 -0700 Subject: [PATCH 011/412] Add tier detail view --- ui/modules/vpc/vpc.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index ac596266d05..cb712bc0254 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -2,13 +2,15 @@ var elems = { tier: function(args) { var tier = args.tier; + var context = args.context; var dashboardItems = args.dashboardItems; var $tier = $('
    ').addClass('tier-item'); var $header = $('
    ').addClass('header'); var $title = $('
    ').addClass('title').append($('')); var $content = $('
    ').addClass('content'); + var $browser = $('#browser .container'); var $dashboard = elems.dashboard({ - context: args.context, + context: context, dashboardItems: dashboardItems }); var $detailLink = $('
    ').addClass('detail-link'); @@ -16,6 +18,24 @@ var $cidrLabel = $('').addClass('cidr-label'); var $cidr = $('').addClass('cidr'); + $detailLink.click(function() { + $browser.cloudBrowser('addPanel', { + title: tier.displayname ? tier.displayname : tier.name, + complete: function($panel) { + var $detailView = $('
    ').detailView( + $.extend(true, {}, cloudStack.vpc.tiers.detailView, { + $browser: $browser, + context: $.extend(true, {}, context, { + networks: [tier] + }) + }) + ); + + $detailView.appendTo($panel); + } + }); + }); + $cidrLabel.html('CIDR: '); $cidr.html(tier.cidr); $title.find('span').html(tier.displayname ? tier.displayname : tier.name); @@ -104,7 +124,9 @@ var vpcItem = context.vpc[0]; var $chart = $('
    ').addClass('vpc-network-chart'); var $tiers = $('
    ').addClass('tiers'); + var $toolbar = $('
    ').addClass('toolbar'); + $toolbar.appendTo($chart); $tiers.appendTo($chart); // Get tiers From 2eb29a5d81d8948ccfabb987cb859a2c995091e3 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 10 May 2013 15:42:18 -0700 Subject: [PATCH 012/412] Tier details: disable view all link, ACL tab --- ui/scripts/vpc.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 6033f50f6f1..5109724e5e1 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -1483,6 +1483,9 @@ path: 'network.ipAddresses', label: 'label.menu.ipaddresses', preFilter: function(args) { + return false; + + /// Disabled if (args.context.networks[0].state == 'Destroyed') return false; @@ -1753,7 +1756,7 @@ } }); - var hiddenTabs = ['ipAddresses']; // Disable IP address tab; it is redundant with 'view all' button + var hiddenTabs = ['ipAddresses', 'acl']; // Disable IP address tab; it is redundant with 'view all' button if(networkOfferingHavingELB == false) hiddenTabs.push("addloadBalancer"); From 6ac4f79397193fb7130ab423e1d74a695f5fdc0f Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 10 May 2013 15:48:32 -0700 Subject: [PATCH 013/412] VPC chart: add link to public IP addresses list --- ui/scripts/vpc.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 5109724e5e1..7e1a8c3efc8 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -282,7 +282,10 @@ // Private gateways privateGateways: function() { return cloudStack.vpc.gateways.listView() - } + }, + publicIPs: function() { + return cloudStack.vpc.ipAddresses.listView() + } }, routerDetailView: function() { From df0919bef589bf44d23f9d9e056fe0da9a579034 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 10 May 2013 16:03:44 -0700 Subject: [PATCH 014/412] Add placeholder network ACL list view --- ui/scripts/vpc.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 7e1a8c3efc8..a009db4d45f 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -283,9 +283,24 @@ privateGateways: function() { return cloudStack.vpc.gateways.listView() }, + + // Public IP Addresses publicIPs: function() { return cloudStack.vpc.ipAddresses.listView() - } + }, + + // Network ACL lists + networkACLLists: { + listView: { + fields: { + name: { label: 'label.name' }, + total: { label: 'label.total' } + }, + dataProvider: function(args) { + args.response.success({ data: [] }); + } + } + } }, routerDetailView: function() { From 57d21051bd8828b8581d2ecf8937b9b84dc94de6 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 13 May 2013 11:16:15 -0700 Subject: [PATCH 015/412] Fix typo --- ui/modules/modules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/modules/modules.js b/ui/modules/modules.js index 32c135e3504..1e4cd45c833 100644 --- a/ui/modules/modules.js +++ b/ui/modules/modules.js @@ -16,7 +16,7 @@ // under the License. (function($, cloudStack) { cloudStack.modules = [ - 'vpc' + 'vpc', 'infrastructure', 'vnmcNetworkProvider', 'vnmcAsa1000v' From 40dc2211db4062d614ad8690c988ea451628c019 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 13 May 2013 14:03:58 -0700 Subject: [PATCH 016/412] Implement 'add tier' action handling --- ui/modules/vpc/vpc.js | 65 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index cb712bc0254..22a82509fbf 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -62,10 +62,69 @@ return $router; }, - tierPlaceholder: function() { + tierPlaceholder: function(args) { + var context = args.context; var $placeholder = $('
    ').addClass('tier-placeholder'); $placeholder.append($('').append('Create network')); + $placeholder.click(function() { + var addAction = cloudStack.vpc.tiers.actions.add; + var form = cloudStack.dialog.createForm({ + context: context, + form: addAction.createForm, + after: function(args) { + var $loading = $('
    ').addClass('loading-overlay') + .prependTo($placeholder); + + addAction.action({ + context: context, + data: args.data, + response: { + success: function(args) { + var tier = args.data; + + cloudStack.ui.notifications.add( + // Notification + { + desc: addAction.label + }, + + // Success + function(args) { + $loading.remove(); + // addNewTier({ + // ipAddresses: ipAddresses, + // $browser: $browser, + // tierDetailView: tierDetailView, + // context: $.extend(true, {}, context, { + // networks: [tier] + // }), + // tier: tier, + // acl: acl, + // $tiers: $tiers, + // actions: actions, + // actionPreFilter: actionPreFilter, + // vmListView: vmListView + // }); + }, + + {}, + + // Error + function(args) { + $loading.remove(); + } + ); + }, + error: function(errorMsg) { + cloudStack.dialog.notice({ message: _s(errorMsg) }); + $loading.remove(); + } + } + }); + } + }); + }); return $placeholder; }, @@ -168,7 +227,9 @@ }); // Add placeholder tier - $tiers.append(elems.tierPlaceholder()); + $tiers.append(elems.tierPlaceholder({ + context: context + })); } } }); From c819980338b852a54f411a385e8b979b4b23d578 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 13 May 2013 14:06:43 -0700 Subject: [PATCH 017/412] Add place holder for internal LB --- ui/scripts/vpc.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 6970d474e5f..ad03a2d4cd9 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -219,7 +219,7 @@ args.response.success({ _custom: { jobId: jobID, - getUpdatedItem: function() { + getUpdateIdtem: function() { $(window).trigger('cloudStack.fullRefresh'); } }, @@ -279,6 +279,19 @@ cloudStack.vpc = { // nTier sections sections: { + // Internal load balancers + tierLoadBalancers: { + listView: { + fields: { + name: { label: 'label.name' }, + total: { label: 'label.total' } + }, + dataProvider: function(args) { + args.response.success({ data: [] }); + } + } + } + // Private gateways privateGateways: function() { return cloudStack.vpc.gateways.listView() From 3c24380f9d52ebac5cc3d06cbeac5941c6c6cfe6 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 13 May 2013 14:54:35 -0700 Subject: [PATCH 018/412] Fix typo --- ui/scripts/vpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index ad03a2d4cd9..e4315b85e23 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -290,7 +290,7 @@ args.response.success({ data: [] }); } } - } + }, // Private gateways privateGateways: function() { From d6ceef2a29a0d32bec1965031eba23bfc4337759 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 13 May 2013 17:05:03 -0700 Subject: [PATCH 019/412] Implement refresh VPC chart --- ui/modules/vpc/vpc.js | 183 +++++++++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 74 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 22a82509fbf..283ae68e13f 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -27,7 +27,10 @@ $browser: $browser, context: $.extend(true, {}, context, { networks: [tier] - }) + }), + onActionComplete: function() { + $tier.closest('.vpc-network-chart').trigger('reload'); + } }) ); @@ -92,6 +95,8 @@ // Success function(args) { $loading.remove(); + $placeholder.closest('.vpc-network-chart').trigger('reload'); + // addNewTier({ // ipAddresses: ipAddresses, // $browser: $browser, @@ -181,90 +186,120 @@ var vpcChart = function(args) { var context = args.context; var vpcItem = context.vpc[0]; - var $chart = $('
    ').addClass('vpc-network-chart'); - var $tiers = $('
    ').addClass('tiers'); - var $toolbar = $('
    ').addClass('toolbar'); - - $toolbar.appendTo($chart); - $tiers.appendTo($chart); - // Get tiers - vpc.tiers.dataProvider({ - context: context, - response: { - success: function(data) { - var tiers = data.tiers; + var chart = function(args) { + args = args ? args : {}; - $(tiers).map(function(index, tier) { - var $tier = elems.tier({ - context: context, - tier: tier, - dashboardItems: [ - { - id: 'tierLoadBalancers', - name: 'Load balancers', - total: 5 - }, - { - id: 'tierPortForwarders', - name: 'Port forwarders', - total: 4 - }, - { - id: 'tierStaticNATs', - name: 'Static NATs', - total: 3 - }, - { - id: 'tierVMs', - name: 'Virtual Machines', - total: 300 - } - ] + var $chart = $('
    ').addClass('vpc-network-chart'); + var $tiers = $('
    ').addClass('tiers'); + var $toolbar = $('
    ').addClass('toolbar'); + var $refresh = $('
    ').addClass('button refresh'); + + $refresh.appendTo($toolbar); + $toolbar.appendTo($chart); + $tiers.appendTo($chart); + + $refresh.click(function() { + $('.vpc-network-chart').trigger('reload'); + }); + + // Get tiers + var $loading = $('
    ').addClass('loading-overlay').prependTo($chart); + vpc.tiers.dataProvider({ + context: context, + response: { + success: function(data) { + var tiers = data.tiers; + + $(tiers).map(function(index, tier) { + var $tier = elems.tier({ + context: context, + tier: tier, + dashboardItems: [ + { + id: 'tierLoadBalancers', + name: 'Load balancers', + total: 5 + }, + { + id: 'tierPortForwarders', + name: 'Port forwarders', + total: 4 + }, + { + id: 'tierStaticNATs', + name: 'Static NATs', + total: 3 + }, + { + id: 'tierVMs', + name: 'Virtual Machines', + total: 300 + } + ] + }); + + $tier.appendTo($tiers); }); - $tier.appendTo($tiers); - }); + // Add placeholder tier + $tiers.append(elems.tierPlaceholder({ + context: context + })); - // Add placeholder tier - $tiers.append(elems.tierPlaceholder({ - context: context - })); + $loading.remove(); + + if (args.complete) { + args.complete($chart); + } + } } - } - }); + }); + + // Router + $router = elems.router({ + context: context, + dashboardItems: [ + { + id: 'privateGateways', + name: 'Private gateways', + total: 1 + }, + { + id: 'publicIPs', + name: 'Public IP addresses', + total: 2 + }, + { + id: 'siteToSiteVPNs', + name: 'Site-to-site VPNs', + total: 3 + }, + { + id: 'networkACLLists', + name: 'Network ACL lists', + total: 2 + } + ] + }).appendTo($chart); + + $chart.bind('reload', function() { + chart({ + complete: function($newChart) { + $chart.replaceWith($newChart); + } + }); + }); + + return $chart; + }; - // Router - $router = elems.router({ - context: context, - dashboardItems: [ - { - id: 'privateGateways', - name: 'Private gateways', - total: 1 - }, - { - id: 'publicIPs', - name: 'Public IP addresses', - total: 2 - }, - { - id: 'siteToSiteVPNs', - name: 'Site-to-site VPNs', - total: 3 - }, - { - id: 'networkACLLists', - name: 'Network ACL lists', - total: 2 - } - ] - }).appendTo($chart); - $('#browser .container').cloudBrowser('addPanel', { title: vpcItem.displaytext ? vpcItem.displaytext : vpcItem.name, maximizeIfSelected: true, complete: function($panel) { + var $chart = chart(); + $chart.appendTo($panel); } }); From ce7802413af89720bcd89a33c3966ab639a3270d Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 13 May 2013 17:26:23 -0700 Subject: [PATCH 020/412] Remove refresh button --- ui/modules/vpc/vpc.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 283ae68e13f..f084affe087 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -193,16 +193,10 @@ var $chart = $('
    ').addClass('vpc-network-chart'); var $tiers = $('
    ').addClass('tiers'); var $toolbar = $('
    ').addClass('toolbar'); - var $refresh = $('
    ').addClass('button refresh'); - $refresh.appendTo($toolbar); $toolbar.appendTo($chart); $tiers.appendTo($chart); - $refresh.click(function() { - $('.vpc-network-chart').trigger('reload'); - }); - // Get tiers var $loading = $('
    ').addClass('loading-overlay').prependTo($chart); vpc.tiers.dataProvider({ From cb31791adf903e16abba720cf483edc1fb18e82c Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 14 May 2013 16:25:37 -0700 Subject: [PATCH 021/412] WIP ACL draggable multi-edit --- ui/scripts/ui/widgets/multiEdit.js | 1394 +++++++++++++++------------- ui/scripts/vpc.js | 85 +- 2 files changed, 819 insertions(+), 660 deletions(-) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index a1272fda31c..0f1515f5aec 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -30,6 +30,7 @@ var $tr; var $item = $('
    ').addClass('data-item'); var multiRule = data; + var reorder = options.reorder; $item.append($('').append($(''))); $tr = $('').appendTo($item.find('tbody')); @@ -38,339 +39,398 @@ $tr.data('multi-edit-data', itemData); } - // Setup columns - $.each(fields, function(fieldName, field) { - if (options.ignoreEmptyFields && !data[fieldName]) { - return true; - } + // Add reorder actions + if (reorder) { + var sort = function($tr, action) { + var $listView = $tr.closest('.data-body'); + var viewArgs = $listView.data('view-args'); + var context = $.extend( + true, {}, + options.context + ); + var rowIndex = $tr.closest('.data-body').find('.data-item').size() - ($tr.closest('.data-item').index()); - var $td = $(''); + var itemName = multiRule._itemName ? item[multiRule._itemName] : item.name; + var $itemName = $('').html(_s(itemName)); - // Show list view of selected VMs - $browser.cloudBrowser('addPanel', { - title: _l('label.item.listing'), - data: '', - noSelectPanel: true, - maximizeIfSelected: true, - complete: function($newPanel) { - return $newPanel.listView(listViewArgs); + $tr.append($(''); - var itemName = multiRule._itemName ? item[multiRule._itemName] : item.name; - var $itemName = $('').html(_s(itemName)); - - $tr.append($('').appendTo($('
    ').addClass(fieldName).appendTo($tr); - var $input, val; - var $addButton = $multi.find('form .button.add-vm:not(.custom-action)').clone(); - var newItemRows = []; - var addItemAction = function(data) { - var $loading = $('
    ').addClass('loading-overlay'); - var complete = function(args) { - var $tbody = $item.find('.expandable-listing tbody'); + context[viewArgs.activeSection] = $tr.data('json-obj'); - $loading.remove(); - $(data).each(function() { - var item = this; - var $itemRow = _medit.multiItem.itemRow(item, options.itemActions, multiRule, $tbody); - - $itemRow.appendTo($tbody); - newItemRows.push($itemRow); - - cloudStack.evenOdd($tbody, 'tr:visible', { - even: function($elem) { - $elem.removeClass('odd'); - $elem.addClass('even'); - }, - odd: function($elem) { - $elem.removeClass('even'); - $elem.addClass('odd'); - } - }); - }); - }; - var error = function() { - $(newItemRows).each(function() { - var $itemRow = this; - - $itemRow.remove(); - }); - $loading.remove(); - }; - - $loading.prependTo($item); - options.itemActions.add.action({ - context: options.context, - data: data, - multiRule: multiRule, + action.action({ + context: context, + index: rowIndex, response: { - success: function(args) { - var notificationError = function(args) { - error(); - }; - - cloudStack.ui.notifications.add(args.notification, - complete, {}, - notificationError, {}); - }, - error: error + success: function(args) {}, + error: function(args) { + // Move back to previous position + rowActions.moveTo($tr, rowIndex); + } } }); }; - if (!itemData) itemData = [{}]; + $('
    ').addClass('actions reorder').appendTo($tr).append(function() { + var $td = $(this); - if (!options.noSelect && - $multi.find('th,td').filter(function() { - return $(this).attr('rel') == fieldName; - }).is(':hidden')) { - return true; + $.each(reorder, function(actionName, action) { + var fnLabel = { + moveTop: _l('label.move.to.top'), + moveBottom: _l('label.move.to.bottom'), + moveUp: _l('label.move.up.row'), + moveDown: _l('label.move.down.row'), + moveDrag: _l('label.drag.new.position') + }; + + $('
    ') + .addClass('action reorder') + .addClass(actionName) + .append( + $('').addClass('icon').html(' ') + ) + .attr({ + title: _l(fnLabel[actionName]) + }) + .appendTo($td) + .click(function() { + if (actionName == 'moveDrag') return false; + + rowActions[actionName]($tr); + $tr.closest('.data-body').find('.data-item').each(function() { + sort($(this), action); + }); + + return false; + }); + }); + }); + + // Draggable action + var initDraggable = function($dataItem) { + var originalIndex; + + return $dataItem.sortable({ + handle: '.action.moveDrag', + start: function(event, ui) { + originalIndex = ui.item.index(); + }, + stop: function(event, ui) { + $dataItem.closest('.data-body').find('.data-item').each(function() { + sort($(this), reorder.moveDrag); + }); + } + }); + }; + + if (reorder && reorder.moveDrag) { + initDraggable($tr.closest('.data-item')); } + } - if (!field.isPassword) { - if (field.edit) { - // Edit fields append value of data - if (field.range) { - var start = _s(data[field.range[0]]); - var end = _s(data[field.range[1]]); - $td.append($('').html(start + ' - ' + end)); - } else { - var maxLengths = data['_maxLength']; + // Setup columns + $.each(fields, function(fieldName, field) { + if (!field || (options.ignoreEmptyFields && !data[fieldName])) { + return true; + } - if (maxLengths && - maxLengths[fieldName] && - data[fieldName].length >= maxLengths[fieldName]) { - $td.append($('').html(_s(data[fieldName].toString().substr(0, maxLengths[fieldName] - 3).concat('...')))); - } else { - $td.append($('').html(_s(data[fieldName]))); + var $td = $('
    ').addClass(fieldName).appendTo($tr); + var $input, val; + var $addButton = $multi.find('form .button.add-vm:not(.custom-action)').clone(); + var newItemRows = []; + var addItemAction = function(data) { + var $loading = $('
    ').addClass('loading-overlay'); + var complete = function(args) { + var $tbody = $item.find('.expandable-listing tbody'); + + $loading.remove(); + $(data).each(function() { + var item = this; + var $itemRow = _medit.multiItem.itemRow(item, options.itemActions, multiRule, $tbody); + + $itemRow.appendTo($tbody); + newItemRows.push($itemRow); + + cloudStack.evenOdd($tbody, 'tr:visible', { + even: function($elem) { + $elem.removeClass('odd'); + $elem.addClass('even'); + }, + odd: function($elem) { + $elem.removeClass('even'); + $elem.addClass('odd'); } - $td.attr('title', data[fieldName]); - } - } else if (field.select) { - // Get matching option text - var $matchingSelect = $multi.find('select') - .filter(function() { - return $(this).attr('name') == fieldName; - }); - var $matchingOption = $matchingSelect.find('option') - .filter(function() { - return $(this).val() == data[fieldName]; - }); + }); + }); + }; + var error = function() { + $(newItemRows).each(function() { + var $itemRow = this; - var matchingValue = $matchingOption.size() ? - $matchingOption.html() : data[fieldName]; - - $td.append($('').html(_s(matchingValue))); - } else if (field.addButton && !options.noSelect) { - if (options.multipleAdd) { - $addButton.click(function() { - if ($td.hasClass('disabled')) return false; - - _medit.vmList($multi, - options.listView, - options.context, - options.multipleAdd, _l('label.add.vms'), - addItemAction, - { - multiRule: multiRule - }); + $itemRow.remove(); + }); + $loading.remove(); + }; - return true; - }); - $td.append($addButton); + $loading.prependTo($item); + options.itemActions.add.action({ + context: options.context, + data: data, + multiRule: multiRule, + response: { + success: function(args) { + var notificationError = function(args) { + error(); + }; + + cloudStack.ui.notifications.add(args.notification, + complete, {}, + notificationError, {}); + }, + error: error + } + }); + }; + + if (!itemData) itemData = [{}]; + + if (!options.noSelect && + $multi.find('th,td').filter(function() { + return $(this).attr('rel') == fieldName; + }).is(':hidden')) { + return true; + } + + if (!field.isPassword) { + if (field.edit) { + // Edit fields append value of data + if (field.range) { + var start = _s(data[field.range[0]]); + var end = _s(data[field.range[1]]); + + $td.append($('').html(start + ' - ' + end)); + } else { + var maxLengths = data['_maxLength']; + + if (maxLengths && + maxLengths[fieldName] && + data[fieldName].length >= maxLengths[fieldName]) { + $td.append($('').html(_s(data[fieldName].toString().substr(0, maxLengths[fieldName] - 3).concat('...')))); } else { - // Show VM data - var itemName = data._itemName ? itemData[0][data._itemName] : itemData[0].name; - $td.html(options.multipleAdd ? - itemData.length + ' VMs' : itemName); - $td.click(function() { - var $browser = $(this).closest('.detail-view').data('view-args').$browser; - - if (options.multipleAdd) { - _medit.multiItem.details(itemData, $browser); - } else { - _medit.details(itemData[0], $browser, { - context: options.context, itemName: itemName - }); - } - }); + $td.append($('').html(_s(data[fieldName]))); } - } else if (field.custom) { - var $button = $('
    ').addClass('button add-vm custom-action'); + $td.attr('title', data[fieldName]); + } + } else if (field.select) { + // Get matching option text + var $matchingSelect = $multi.find('select') + .filter(function() { + return $(this).attr('name') == fieldName; + }); + var $matchingOption = $matchingSelect.find('option') + .filter(function() { + return $(this).val() == data[fieldName]; + }); - $td.data('multi-custom-data', data[fieldName]); - $button.html(data && data[fieldName] && data[fieldName]['_buttonLabel'] ? - _l(data[fieldName]['_buttonLabel']) : _l(field.custom.buttonLabel)); - $button.click(function() { + var matchingValue = $matchingOption.size() ? + $matchingOption.html() : data[fieldName]; + + $td.append($('').html(_s(matchingValue))); + } else if (field.addButton && !options.noSelect) { + if (options.multipleAdd) { + $addButton.click(function() { if ($td.hasClass('disabled')) return false; - var $button = $(this); - var context = $.extend(true, {}, - options.context ? - options.context : cloudStack.context, { - multiRules: [data] - }); - - field.custom.action({ - context: context, - data: $td.data('multi-custom-data'), - $item: $td, - response: { - success: function(args) { - if (args.data['_buttonLabel']) { - $button.html(_l(args.data['_buttonLabel'])); - } - $td.data('multi-custom-data', args.data); - } - } - }); + _medit.vmList($multi, + options.listView, + options.context, + options.multipleAdd, _l('label.add.vms'), + addItemAction, + { + multiRule: multiRule + }); return true; }); - $button.appendTo($td); - } - } + $td.append($addButton); + } else { + // Show VM data + var itemName = data._itemName ? itemData[0][data._itemName] : itemData[0].name; + $td.html(options.multipleAdd ? + itemData.length + ' VMs' : itemName); + $td.click(function() { + var $browser = $(this).closest('.detail-view').data('view-args').$browser; - // Add blank styling for empty fields - if ($td.html() == '') { - $td.addClass('blank'); - } - - // Align width to main header - _medit.refreshItemWidths($multi); - - if (data._hideFields && - $.inArray(fieldName, data._hideFields) > -1) { - $td.addClass('disabled'); - } - - return true; - }); - - // Actions column - var $actions = $('
    ').addClass('multi-actions').appendTo($item.find('tr')); - - // Align action column width - $actions.width($multi.find('th.multi-actions').width() + 4); - - // Action filter - var allowedActions = options.preFilter ? options.preFilter({ - actions: $.map(actions, function(value, key) { return key; }), - context: $.extend(true, {}, options.context, { - multiRule: [data], - actions: $.map(actions, function(value, key) { return key; }) - }) - }) : null; - - // Append actions - $.each(actions, function(actionID, action) { - if (allowedActions && $.inArray(actionID, allowedActions) == -1) return true; - - $actions.append( - $('
    ').addClass('action') - .addClass(actionID) - .append($('').addClass('icon')) - .attr({ title: _l(action.label) }) - .click(function() { - var performAction = function(actionOptions) { - if (!actionOptions) actionOptions = {}; - - action.action({ - context: $.extend(true, {}, options.context, { - multiRule: [data] - }), - data: actionOptions.data, - response: { - success: function(args) { - var notification = args ? args.notification : null; - var _custom = args ? args._custom : null; - if (notification) { - $('.notifications').notifications('add', { - section: 'network', - desc: notification.label, - interval: 3000, - _custom: _custom, - poll: function(args) { - var complete = args.complete; - var error = args.error; - - notification.poll({ - _custom: args._custom, - complete: function(args) { - if (isDestroy) { - $loading.remove(); - $dataItem.remove(); - } else { - $multi.trigger('refresh'); - } - - complete(); - - if (actionOptions.complete) actionOptions.complete(); - }, - error: function(args) { - error(args); - $loading.remove(); - $dataItem.show(); - - return cloudStack.dialog.error; - } - }); - } - }); - } else { - $loading.remove(); - if (isDestroy) { - $dataItem.remove(); - } - } - }, - error: function(message) { - cloudStack.dialog.notice({ message: message }); - $item.show(); - $loading.remove(); - } - } + if (options.multipleAdd) { + _medit.multiItem.details(itemData, $browser); + } else { + _medit.details(itemData[0], $browser, { + context: options.context, itemName: itemName }); - }; + } + }); + } + } else if (field.custom) { + var $button = $('
    ').addClass('button add-vm custom-action'); - var $target = $(this); - var $dataItem = $target.closest('.data-item'); - var $expandable = $dataItem.find('.expandable-listing'); - var isDestroy = $target.hasClass('destroy'); - var isEdit = $target.hasClass('edit'); - var createForm = action.createForm; + $td.data('multi-custom-data', data[fieldName]); + $button.html(data && data[fieldName] && data[fieldName]['_buttonLabel'] ? + _l(data[fieldName]['_buttonLabel']) : _l(field.custom.buttonLabel)); + $button.click(function() { + if ($td.hasClass('disabled')) return false; + + var $button = $(this); + var context = $.extend(true, {}, + options.context ? + options.context : cloudStack.context, { + multiRules: [data] + }); - if (isDestroy) { - var $loading = _medit.loadingItem($multi, _l('label.removing') + '...'); - - if ($expandable.is(':visible')) { - $expandable.slideToggle(function() { - $dataItem.hide(); - $dataItem.after($loading); - }); - } else { - // Loading appearance - $dataItem.hide(); - $dataItem.after($loading); + field.custom.action({ + context: context, + data: $td.data('multi-custom-data'), + $item: $td, + response: { + success: function(args) { + if (args.data['_buttonLabel']) { + $button.html(_l(args.data['_buttonLabel'])); + } + $td.data('multi-custom-data', args.data); } } + }); - if (!isEdit) { - if (createForm) { - cloudStack.dialog.createForm({ - form: createForm, - after: function(args) { - var $loading = $('
    ').addClass('loading-overlay').prependTo($dataItem); - performAction({ data: args.data, complete: function() { - $multi.trigger('refresh'); - } }); + return true; + }); + $button.appendTo($td); + } + } + + // Add blank styling for empty fields + if ($td.html() == '') { + $td.addClass('blank'); + } + + // Align width to main header + _medit.refreshItemWidths($multi); + + if (data._hideFields && + $.inArray(fieldName, data._hideFields) > -1) { + $td.addClass('disabled'); + } + + return true; + }); + + // Actions column + var $actions = $('
    ').addClass('multi-actions').appendTo($item.find('tr')); + + // Align action column width + $actions.width($multi.find('th.multi-actions').width() + 4); + + // Action filter + var allowedActions = options.preFilter ? options.preFilter({ + actions: $.map(actions, function(value, key) { return key; }), + context: $.extend(true, {}, options.context, { + multiRule: [data], + actions: $.map(actions, function(value, key) { return key; }) + }) + }) : null; + + // Append actions + $.each(actions, function(actionID, action) { + if (allowedActions && $.inArray(actionID, allowedActions) == -1) return true; + + $actions.append( + $('
    ').addClass('action') + .addClass(actionID) + .append($('').addClass('icon')) + .attr({ title: _l(action.label) }) + .click(function() { + var performAction = function(actionOptions) { + if (!actionOptions) actionOptions = {}; + + action.action({ + context: $.extend(true, {}, options.context, { + multiRule: [data] + }), + data: actionOptions.data, + response: { + success: function(args) { + var notification = args ? args.notification : null; + var _custom = args ? args._custom : null; + if (notification) { + $('.notifications').notifications('add', { + section: 'network', + desc: notification.label, + interval: 3000, + _custom: _custom, + poll: function(args) { + var complete = args.complete; + var error = args.error; + + notification.poll({ + _custom: args._custom, + complete: function(args) { + if (isDestroy) { + $loading.remove(); + $dataItem.remove(); + } else { + $multi.trigger('refresh'); + } + + complete(); + + if (actionOptions.complete) actionOptions.complete(); + }, + error: function(args) { + error(args); + $loading.remove(); + $dataItem.show(); + + return cloudStack.dialog.error; + } + }); + } + }); + } else { + $loading.remove(); + if (isDestroy) { + $dataItem.remove(); + } } - }); - } else { - performAction(); - } - } else { - // Get editable fields - var editableFields = {}; - - $.each(fields, function(key, field) { - if (field.isEditable) editableFields[key] = $.extend(true, {}, field, { - defaultValue: data[key] - }); - }); - - cloudStack.dialog.createForm({ - form: { - title: 'Edit rule', - desc: '', - fields: editableFields }, + error: function(message) { + cloudStack.dialog.notice({ message: message }); + $item.show(); + $loading.remove(); + } + } + }); + }; + + var $target = $(this); + var $dataItem = $target.closest('.data-item'); + var $expandable = $dataItem.find('.expandable-listing'); + var isDestroy = $target.hasClass('destroy'); + var isEdit = $target.hasClass('edit'); + var createForm = action.createForm; + var reorder = options.reorder; + + if (isDestroy) { + var $loading = _medit.loadingItem($multi, _l('label.removing') + '...'); + + if ($expandable.is(':visible')) { + $expandable.slideToggle(function() { + $dataItem.hide(); + $dataItem.after($loading); + }); + } else { + // Loading appearance + $dataItem.hide(); + $dataItem.after($loading); + } + } + + if (!isEdit) { + if (createForm) { + cloudStack.dialog.createForm({ + form: createForm, after: function(args) { var $loading = $('
    ').addClass('loading-overlay').prependTo($dataItem); performAction({ data: args.data, complete: function() { @@ -378,324 +438,336 @@ } }); } }); + } else { + performAction(); } - }) - ); - }); + } else { + // Get editable fields + var editableFields = {}; - // Add tagger action - if (options.tags) { - $actions.prepend( - $('
    ') - .addClass('action editTags') - .attr('title', _l('label.edit.tags')) - .append($('').addClass('icon')) - .click(function() { - $('
    ') - .dialog({ - dialogClass: 'editTags', - title: _l('label.edit.tags'), - width: 400, - buttons: [ - { - text: _l('label.done'), - 'class': 'ok', - click: function() { - $(this).dialog('destroy'); - $('div.overlay:last').remove(); + $.each(fields, function(key, field) { + if (field && field.isEditable) editableFields[key] = $.extend(true, {}, field, { + defaultValue: data[key] + }); + }); - return true; - } + cloudStack.dialog.createForm({ + form: { + title: 'Edit rule', + desc: '', + fields: editableFields + }, + after: function(args) { + var $loading = $('
    ').addClass('loading-overlay').prependTo($dataItem); + performAction({ data: args.data, complete: function() { + $multi.trigger('refresh'); + } }); + } + }); + } + }) + ); + }); + + // Add tagger action + if (options.tags) { + $actions.prepend( + $('
    ') + .addClass('action editTags') + .attr('title', _l('label.edit.tags')) + .append($('').addClass('icon')) + .click(function() { + $('
    ') + .dialog({ + dialogClass: 'editTags', + title: _l('label.edit.tags'), + width: 400, + buttons: [ + { + text: _l('label.done'), + 'class': 'ok', + click: function() { + $(this).dialog('destroy'); + $('div.overlay:last').remove(); + + return true; } - ] - }) - .append( - $('
    ').addClass('multi-edit-tags').tagger($.extend(true, {}, options.tags, { - context: $.extend(true, {}, options.context, { - multiRule: [multiRule] - }) - })) - ) - .closest('.ui-dialog').overlay(); + } + ] + }) + .append( + $('
    ').addClass('multi-edit-tags').tagger($.extend(true, {}, options.tags, { + context: $.extend(true, {}, options.context, { + multiRule: [multiRule] + }) + })) + ) + .closest('.ui-dialog').overlay(); + + return false; + }) + ) + } + + // Add expandable listing, for multiple-item + if (options.multipleAdd) { + // Create expandable box + _medit.multiItem.expandable($item.find('tr').data('multi-edit-data'), + options.itemActions, + multiRule).appendTo($item); + + // Expandable icon/action + $item.find('td:first').prepend( + $('
    ').addClass('expand').click(function() { + $item.closest('.data-item').find('.expandable-listing').slideToggle(); + })); + } + + return $item; + }, + + vmList: function($multi, listView, context, isMultipleAdd, label, complete, options) { + if (!options) options = {}; + + // Create a listing of instances, based on limited information + // from main instances list view + var $listView; + var instances = $.extend(true, {}, listView, { + context: $.extend(true, {}, context, { + multiData: getMultiData($multi), + multiRule: options.multiRule ? [options.multiRule] : null + }), + uiCustom: true + }); + + instances.listView.actions = { + select: { + label: 'Select instance', + type: isMultipleAdd ? 'checkbox' : 'radio', + action: { + uiCustom: function(args) { + var $item = args.$item; + var $input = $item.find('td.actions input:visible'); + + if ($input.attr('type') == 'checkbox') { + if ($input.is(':checked')) + $item.addClass('multi-edit-selected'); + else + $item.removeClass('multi-edit-selected'); + } else { + $item.siblings().removeClass('multi-edit-selected'); + $item.addClass('multi-edit-selected'); + } + } + } + } + }; + + $listView = $('
    ').listView(instances); + + // Change action label + $listView.find('th.actions').html(_l('Select')); + + var $dataList = $listView.addClass('multi-edit-add-list').dialog({ + dialogClass: 'multi-edit-add-list panel', + width: 825, + title: label, + buttons: [ + { + text: _l('label.apply'), + 'class': 'ok', + click: function() { + if (!$listView.find('input[type=radio]:checked, input[type=checkbox]:checked').size()) { + cloudStack.dialog.notice({ message: _l('message.select.item')}); return false; - }) - ) - } - - // Add expandable listing, for multiple-item - if (options.multipleAdd) { - // Create expandable box - _medit.multiItem.expandable($item.find('tr').data('multi-edit-data'), - options.itemActions, - multiRule).appendTo($item); - - // Expandable icon/action - $item.find('td:first').prepend( - $('
    ').addClass('expand').click(function() { - $item.closest('.data-item').find('.expandable-listing').slideToggle(); - })); - } - - return $item; - }, - - vmList: function($multi, listView, context, isMultipleAdd, label, complete, options) { - if (!options) options = {}; - - // Create a listing of instances, based on limited information - // from main instances list view - var $listView; - var instances = $.extend(true, {}, listView, { - context: $.extend(true, {}, context, { - multiData: getMultiData($multi), - multiRule: options.multiRule ? [options.multiRule] : null - }), - uiCustom: true - }); - - instances.listView.actions = { - select: { - label: 'Select instance', - type: isMultipleAdd ? 'checkbox' : 'radio', - action: { - uiCustom: function(args) { - var $item = args.$item; - var $input = $item.find('td.actions input:visible'); - - if ($input.attr('type') == 'checkbox') { - if ($input.is(':checked')) - $item.addClass('multi-edit-selected'); - else - $item.removeClass('multi-edit-selected'); - } else { - $item.siblings().removeClass('multi-edit-selected'); - $item.addClass('multi-edit-selected'); - } } + + $dataList.fadeOut(function() { + complete($.map( + $listView.find('tr.multi-edit-selected'), + + // Attach VM data to row + function(elem) { + var itemData = $(elem).data('json-obj'); + var $subselect = $(elem).find('.subselect select'); + + // Include subselect data + if ($subselect && $subselect.val()) { + return $.extend(itemData, { + _subselect: $subselect.val() + }); + } + + return itemData; + } + )); + $dataList.remove(); + }); + + $('div.overlay').fadeOut(function() { + $('div.overlay').remove(); + }); + + return true; + } + }, + { + text: _l('label.cancel'), + 'class': 'cancel', + click: function() { + $dataList.fadeOut(function() { + $dataList.remove(); + }); + $('div.overlay').fadeOut(function() { + $('div.overlay').remove(); + }); } } - }; + ] + }).parent('.ui-dialog').overlay(); + }, - $listView = $('
    ').listView(instances); + /** + * Align width of each data row to main header + */ + refreshItemWidths: function($multi) { + $multi.find('.data-body').width( + $multi.find('form > table.multi-edit').width() + ); - // Change action label - $listView.find('th.actions').html(_l('Select')); + $multi.find('.data tr').filter(function() { + return !$(this).closest('.expandable-listing').size(); + }).each(function() { + var $tr = $(this); - var $dataList = $listView.addClass('multi-edit-add-list').dialog({ - dialogClass: 'multi-edit-add-list panel', - width: 825, - title: label, - buttons: [ - { - text: _l('label.apply'), - 'class': 'ok', - click: function() { - if (!$listView.find('input[type=radio]:checked, input[type=checkbox]:checked').size()) { - cloudStack.dialog.notice({ message: _l('message.select.item')}); + $tr.find('td').each(function() { + var $td = $(this); - return false; - } - - $dataList.fadeOut(function() { - complete($.map( - $listView.find('tr.multi-edit-selected'), - - // Attach VM data to row - function(elem) { - var itemData = $(elem).data('json-obj'); - var $subselect = $(elem).find('.subselect select'); - - // Include subselect data - if ($subselect && $subselect.val()) { - return $.extend(itemData, { - _subselect: $subselect.val() - }); - } - - return itemData; - } - )); - $dataList.remove(); - }); - - $('div.overlay').fadeOut(function() { - $('div.overlay').remove(); - }); - - return true; - } - }, - { - text: _l('label.cancel'), - 'class': 'cancel', - click: function() { - $dataList.fadeOut(function() { - $dataList.remove(); - }); - $('div.overlay').fadeOut(function() { - $('div.overlay').remove(); - }); - } - } - ] - }).parent('.ui-dialog').overlay(); - }, - - /** - * Align width of each data row to main header - */ - refreshItemWidths: function($multi) { - $multi.find('.data-body').width( - $multi.find('form > table.multi-edit').width() - ); - - $multi.find('.data tr').filter(function() { - return !$(this).closest('.expandable-listing').size(); - }).each(function() { - var $tr = $(this); - - $tr.find('td').each(function() { - var $td = $(this); - - $td.width($($multi.find('th:visible')[$td.index()]).width() + 5); - }); + $td.width($($multi.find('th:visible')[$td.index()]).width() + 5); }); - }, + }); + }, - /** - * Create a fake 'loading' item box - */ - loadingItem: function($multi, label) { - var $loading = $('
    ').addClass('data-item loading'); + /** + * Create a fake 'loading' item box + */ + loadingItem: function($multi, label) { + var $loading = $('
    ').addClass('data-item loading'); - // Align height with existing items - var $row = $multi.find('.data-item:first'); + // Align height with existing items + var $row = $multi.find('.data-item:first'); - // Set label - if (label) { - $loading.append( - $('
    ').addClass('label').append( - $('').html(_l(label)) - ) - ); + // Set label + if (label) { + $loading.append( + $('
    ').addClass('label').append( + $('').html(_l(label)) + ) + ); + } + + return $loading; + }, + details: function(data, $browser, options) { + if (!options) options = {}; + + var detailViewArgs, $detailView; + + detailViewArgs = $.extend(true, {}, cloudStack.sections.instances.listView.detailView); + detailViewArgs.actions = null; + detailViewArgs.$browser = $browser; + detailViewArgs.id = data.id; + detailViewArgs.jsonObj = data; + detailViewArgs.context = options.context; + + $browser.cloudBrowser('addPanel', { + title: options.itemName ? options.itemName : data.name, + maximizeIfSelected: true, + complete: function($newPanel) { + $newPanel.detailView(detailViewArgs); } + }); + }, + multiItem: { + /** + * Show listing of load balanced VMs + */ + details: function(data, $browser) { + var listViewArgs, $listView; - return $loading; - }, - details: function(data, $browser, options) { - if (!options) options = {}; - - var detailViewArgs, $detailView; - - detailViewArgs = $.extend(true, {}, cloudStack.sections.instances.listView.detailView); - detailViewArgs.actions = null; - detailViewArgs.$browser = $browser; - detailViewArgs.id = data.id; - detailViewArgs.jsonObj = data; - detailViewArgs.context = options.context; + // Setup list view + listViewArgs = $.extend(true, {}, cloudStack.sections.instances); + listViewArgs.listView.actions = null; + listViewArgs.listView.filters = null; + listViewArgs.$browser = $browser; + listViewArgs.listView.detailView.actions = null; + listViewArgs.listView.dataProvider = function(args) { + setTimeout(function() { + args.response.success({ + data: data + }); + }, 50); + }; + $listView = $('
    ').listView(listViewArgs); + // Show list view of selected VMs $browser.cloudBrowser('addPanel', { - title: options.itemName ? options.itemName : data.name, + title: _l('label.item.listing'), + data: '', + noSelectPanel: true, maximizeIfSelected: true, complete: function($newPanel) { - $newPanel.detailView(detailViewArgs); + return $newPanel.listView(listViewArgs); } }); }, - multiItem: { - /** - * Show listing of load balanced VMs - */ - details: function(data, $browser) { - var listViewArgs, $listView; - // Setup list view - listViewArgs = $.extend(true, {}, cloudStack.sections.instances); - listViewArgs.listView.actions = null; - listViewArgs.listView.filters = null; - listViewArgs.$browser = $browser; - listViewArgs.listView.detailView.actions = null; - listViewArgs.listView.dataProvider = function(args) { - setTimeout(function() { - args.response.success({ - data: data - }); - }, 50); - }; - $listView = $('
    ').listView(listViewArgs); + itemRow: function(item, itemActions, multiRule, $tbody) { + var $tr = $('
    ').addClass('name').appendTo($tr).append($itemName)); + + $itemName.click(function() { + _medit.details(item, $('#browser .container'), { + itemName: itemName, + context: { + instances: [item] } }); - }, + }); - itemRow: function(item, itemActions, multiRule, $tbody) { - var $tr = $('
    ').addClass('name').appendTo($tr).append($itemName)); - - $itemName.click(function() { - _medit.details(item, $('#browser .container'), { - itemName: itemName, - context: { - instances: [item] - } - }); - }); - - var itemState=multiRule._itemState ? item[multiRule._itemState] :item.state; - var $itemState = $('').html(_s(itemState)); - $tr.append($('').addClass('state').appendTo($tr).append("Application State - ").append($itemState)); + var itemState=multiRule._itemState ? item[multiRule._itemState] :item.state; + var $itemState = $('').html(_s(itemState)); + $tr.append($('').addClass('state').appendTo($tr).append("Application State - ").append($itemState)); - if (itemActions) { - var $itemActions = $('').addClass('actions item-actions'); + if (itemActions) { + var $itemActions = $('').addClass('actions item-actions'); - $.each(itemActions, function(itemActionID, itemAction) { - if (itemActionID == 'add') - return true; - - if(item._hideActions != null && $.inArray(itemActionID, item._hideActions) > -1) - return true; + $.each(itemActions, function(itemActionID, itemAction) { + if (itemActionID == 'add') + return true; + + if(item._hideActions != null && $.inArray(itemActionID, item._hideActions) > -1) + return true; - var $itemAction = $('
    ').addClass('action').addClass(itemActionID); + var $itemAction = $('
    ').addClass('action').addClass(itemActionID); - $itemAction.click(function() { - itemAction.action({ - item: item, - multiRule: multiRule, - response: { - success: function(args) { - if (itemActionID == 'destroy') { - var notification = args.notification; - var success = function(args) { $tr.remove(); }; - var successArgs = {}; - var error = function(args) { - $tr.show(); - cloudStack.evenOdd($tbody, 'tr:visible', { - even: function($elem) { - $elem.removeClass('odd'); - $elem.addClass('even'); - }, - odd: function($elem) { - $elem.removeClass('even'); - $elem.addClass('odd'); - } - }); - }; - var errorArgs = {}; - - $tr.hide(); + $itemAction.click(function() { + itemAction.action({ + item: item, + multiRule: multiRule, + response: { + success: function(args) { + if (itemActionID == 'destroy') { + var notification = args.notification; + var success = function(args) { $tr.remove(); }; + var successArgs = {}; + var error = function(args) { + $tr.show(); cloudStack.evenOdd($tbody, 'tr:visible', { even: function($elem) { $elem.removeClass('odd'); @@ -706,83 +778,106 @@ $elem.addClass('odd'); } }); - cloudStack.ui.notifications.add(notification, - success, successArgs, - error, errorArgs); - } - }, - error: function(message) { - if (message) { - cloudStack.dialog.notice({ message: message }); - } + }; + var errorArgs = {}; + + $tr.hide(); + cloudStack.evenOdd($tbody, 'tr:visible', { + even: function($elem) { + $elem.removeClass('odd'); + $elem.addClass('even'); + }, + odd: function($elem) { + $elem.removeClass('even'); + $elem.addClass('odd'); + } + }); + cloudStack.ui.notifications.add(notification, + success, successArgs, + error, errorArgs); + } + }, + error: function(message) { + if (message) { + cloudStack.dialog.notice({ message: message }); } } - }); + } }); - $itemAction.append($('').addClass('icon')); - $itemAction.appendTo($itemActions); - - return true; }); + $itemAction.append($('').addClass('icon')); + $itemAction.appendTo($itemActions); - $itemActions.appendTo($tr); - } - - return $tr; - }, - - expandable: function(data, itemActions, multiRule) { - var $expandable = $('
    ').addClass('expandable-listing'); - var $tbody = $('
    ').appendTo($expandable)); - - $(data).each(function() { - var field = this; - var $tr = _medit.multiItem.itemRow(field, itemActions, multiRule, $tbody).appendTo($tbody); - - cloudStack.evenOdd($tbody, 'tr', { - even: function($elem) { - $elem.addClass('even'); - }, - odd: function($elem) { - $elem.addClass('odd'); - } - }); + return true; }); - return $expandable.hide(); + $itemActions.appendTo($tr); } + + return $tr; + }, + + expandable: function(data, itemActions, multiRule) { + var $expandable = $('
    ').addClass('expandable-listing'); + var $tbody = $('
    ').appendTo($('
    ').appendTo($expandable)); + + $(data).each(function() { + var field = this; + var $tr = _medit.multiItem.itemRow(field, itemActions, multiRule, $tbody).appendTo($tbody); + + cloudStack.evenOdd($tbody, 'tr', { + even: function($elem) { + $elem.addClass('even'); + }, + odd: function($elem) { + $elem.addClass('odd'); + } + }); + }); + + return $expandable.hide(); } - }; + } +}; - $.fn.multiEdit = function(args) { - var dataProvider = args.dataProvider; - var multipleAdd = args.multipleAdd; - var tags = args.tags; - var $multi = $('
    ').addClass('multi-edit').appendTo(this); - var $multiForm = $('
    ').appendTo($multi); - var $inputTable = $('
    ').addClass('multi-edit').appendTo($multiForm); - var $dataTable = $('
    ').addClass('data').appendTo($multi); - var $addVM; - var fields = args.fields; - var actions = args.actions; - var itemActions = multipleAdd ? args.itemActions : null; - var noSelect = args.noSelect; - var context = args.context; - var ignoreEmptyFields = args.ignoreEmptyFields; - var actionPreFilter = args.actionPreFilter; - var readOnlyCheck = args.readOnlyCheck; +$.fn.multiEdit = function(args) { + var dataProvider = args.dataProvider; + var multipleAdd = args.multipleAdd; + var tags = args.tags; + var $multi = $('
    ').addClass('multi-edit').appendTo(this); + var $multiForm = $('').appendTo($multi); + var $inputTable = $('
    ').addClass('multi-edit').appendTo($multiForm); + var $dataTable = $('
    ').addClass('data').appendTo($multi); + var $addVM; + var fields = args.fields; + var actions = args.actions; + var itemActions = multipleAdd ? args.itemActions : null; + var noSelect = args.noSelect; + var context = args.context; + var ignoreEmptyFields = args.ignoreEmptyFields; + var actionPreFilter = args.actionPreFilter; + var readOnlyCheck = args.readOnlyCheck; + var reorder = args.reorder; - var $thead = $('
    ').appendTo( - $('').appendTo($inputTable) - ); - var $inputForm = $('').appendTo( - $('').appendTo($inputTable) - ); - var $dataBody = $('
    ').addClass('data-body').appendTo($dataTable); + var $thead = $('
    ').appendTo( + $('').appendTo($inputTable) + ); + var $inputForm = $('').appendTo( + $('').appendTo($inputTable) + ); + var $dataBody = $('
    ').addClass('data-body').appendTo($dataTable); - // Setup input table headers - $.each(args.fields, function(fieldName, field) { - var $th = $('
    ').addClass(fieldName).html(_l(field.label.toString())); + // Setup input table headers + + if (reorder) { + $('').addClass('reorder').appendTo($thead); + $('').addClass('reorder').appendTo($inputForm); + } + + $.each(args.fields, function(fieldName, field) { + if (!field) return true; + + var $th = $('').addClass(fieldName).html(_l(field.label.toString())); $th.attr('rel', fieldName); $th.appendTo($thead); var $td = $('').addClass(fieldName); @@ -1055,7 +1150,8 @@ ignoreEmptyFields: ignoreEmptyFields, preFilter: actionPreFilter, listView: listView, - tags: tags + tags: tags, + reorder: reorder } ).appendTo($dataBody); }); diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 805a089dd5a..3235cd66c20 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -26,7 +26,8 @@ } return hiddenFields; // Returns fields to be hidden - }, + }, + reorder: cloudStack.api.actions.sort('updateAclRule', 'multiRule'), fields: { 'cidrlist': { edit: true, label: 'label.cidr' }, 'protocol': { @@ -45,28 +46,34 @@ var $otherFields = $inputs.filter(function() { var name = $(this).attr('name'); - return name != 'icmptype' && name != 'icmpcode' && name != 'cidrlist'; + return name != 'protocolnumber' && + name != 'icmptype' && + name != 'icmpcode' && + name != 'cidrlist'; }); - var $protocolinput = args.$form.find('th,td'); + var $protocolinput = args.$form.find('td input'); var $protocolFields = $protocolinput.filter(function(){ - var name = $(this).attr('rel'); + var name = $(this).attr('name'); return $.inArray(name,['protocolnumber']) > -1; }); - if($(this).val() == 'protocolnumber' ){ - - $protocolFields.show(); + if($(this).val() == 'protocolnumber' ){ + $icmpFields.hide(); + $otherFields.hide(); + $protocolFields.show(); } else{ - $protocolFields.hide(); + $protocolFields.hide(); + $icmpFields.show(); + $otherFields.show(); } - if ($(this).val() == 'icmp') { $icmpFields.show(); $icmpFields.attr('disabled', false); + $protocolFields.hide(); $otherFields.attr('disabled', 'disabled'); $otherFields.hide(); $otherFields.parent().find('label.error').hide(); @@ -77,6 +84,7 @@ $icmpFields.attr('disabled', 'disabled'); $icmpFields.hide(); $icmpFields.parent().find('label.error').hide(); + $protocolFields.hide(); } }); @@ -93,7 +101,7 @@ } }, - 'protocolnumber': {label:'Protocol Number',isDisabled:true,isHidden:true,edit:true}, + 'protocolnumber': {label:'Protocol Number',edit:true}, 'startport': { edit: true, label: 'label.start.port' }, 'endport': { edit: true, label: 'label.end.port' }, 'networkid': { @@ -305,12 +313,67 @@ // Network ACL lists networkACLLists: { listView: { + id: 'aclLists', fields: { name: { label: 'label.name' }, total: { label: 'label.total' } }, dataProvider: function(args) { - args.response.success({ data: [] }); + args.response.success({ + data: [ + { name: 'ACL list 1', total: 3 }, + { name: 'ACL list 2', total: 2 } + ] + }); + }, + + detailView: { + isMaximized: true, + tabs: { + details: { + title: 'label.details', + fields: [ + { + name: { label: 'label.name', isEditable: true } + } + ], + dataProvider: function(args) { + args.response.success({ data: args.context.aclLists[0] }); + } + }, + + aclRules: { + title: 'label.acl.rules', + custom: function(args) { + return $('
    ').multiEdit($.extend(true, {}, aclMultiEdit, { + context: args.context, + fields: { + networkid: false + }, + dataProvider: function(args) { + args.response.success({ + data: [ + { + cidrlist: '10.1.1.0/24', + protocol: 'TCP', + startport: 22, endport: 22, + networkid: 0, + traffictype: 'Egress' + }, + { + cidrlist: '10.2.1.0/24', + protocol: 'UDP', + startport: 56, endport: 72, + networkid: 0, + trafficType: 'Ingress' + } + ] + }); + } + })); + } + } + } } } } From 08bc6bf77889871f8fd33afab00c139864650f9a Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 11:57:14 -0700 Subject: [PATCH 022/412] Fix 'protocolnumber' change event --- ui/scripts/vpc.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 1d6160a232c..d06ff2ef1ec 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -59,21 +59,14 @@ return $.inArray(name,['protocolnumber']) > -1; }); - if($(this).val() == 'protocolnumber' ){ + if ($(this).val() == 'protocolnumber' ){ $icmpFields.hide(); $otherFields.hide(); - $protocolFields.show(); - } - else{ - $protocolFields.hide(); - $icmpFields.show(); - $otherFields.show(); - } - - if ($(this).val() == 'icmp') { + $protocolFields.show().addClass('required'); + } else if ($(this).val() == 'icmp') { $icmpFields.show(); $icmpFields.attr('disabled', false); - $protocolFields.hide(); + $protocolFields.hide().removeClass('required'); $otherFields.attr('disabled', 'disabled'); $otherFields.hide(); $otherFields.parent().find('label.error').hide(); @@ -84,7 +77,7 @@ $icmpFields.attr('disabled', 'disabled'); $icmpFields.hide(); $icmpFields.parent().find('label.error').hide(); - $protocolFields.hide(); + $protocolFields.hide().removeClass('required'); } }); @@ -98,6 +91,8 @@ ] }); + + setTimeout(function() { args.$select.trigger('change'); }, 100); } }, From 96cee01f705cde7247d2b87f929d14a6d1af3539 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 11:58:40 -0700 Subject: [PATCH 023/412] Add missing license header --- ui/modules/vpc/vpc.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index f084affe087..5c93e6cdbe6 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -1,3 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. (function($, cloudStack) { var elems = { tier: function(args) { From 7ed11ee91141cf8315516ea396ed2d3735051135 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 12:09:50 -0700 Subject: [PATCH 024/412] Add site-to-site VPN to VPC chart --- ui/scripts/vpc.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index d06ff2ef1ec..74c78222a10 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -383,6 +383,15 @@ } } } + }, + siteToSiteVPNs: function() { + return $.extend(true, {}, cloudStack.vpc.siteToSiteVPN, { + // siteToSiteVPN is multi-section so doesn't have an explicit + // 'listView' block + // + // -- use this as a flag for VPC chart to render as a list view + listView: true + }); } }, From 42ed9e13f3b14a42207501c80225df028d36eb39 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 12:28:03 -0700 Subject: [PATCH 025/412] Add placeholder tier LB list view + add action --- ui/scripts/vpc.js | 52 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 74c78222a10..96c983b6b02 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -298,11 +298,57 @@ tierLoadBalancers: { listView: { fields: { - name: { label: 'label.name' }, - total: { label: 'label.total' } + ipaddress: { label: 'label.ip.address' }, + type: { label: 'label.type' } }, dataProvider: function(args) { - args.response.success({ data: [] }); + args.response.success({ + data: [ + { ipaddress: '10.3.2.1', type: 'Internal' }, + { ipaddress: '10.3.2.3', type: 'Internal' }, + { ipaddress: '10.232.1.4', type: 'Public' } + ] + }); + }, + actions: { + add: { + label: 'Add new LB', + createForm: { + title: 'Add new LB', + desc: 'Please specify the type of load balancer you would like to create.', + fields: { + type: { + label: 'label.type', + select: function(args) { + args.response.success({ + data: [ + { id: 'internal', description: 'Internal' }, + { id: 'public', description: 'Public' } + ] + }); + } + } + } + }, + messages: { + notification: function(args) { + return 'Add LB to VPC network'; + } + }, + action: function(args) { + args.response.success(); + }, + notification: { + poll: function(args) { + args.complete({ + data: { + ipaddress: '10.0.3.2', + type: 'Internal' + } + }); + } + } + } } } }, From 3bb81313ad0896ca421991d14b5a3497423a6787 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Wed, 15 May 2013 13:59:37 -0700 Subject: [PATCH 026/412] Adnvace Zone with SG enabled: DB upgrade for Advance SG enabled zone - setup name/displayText for Guest Shared SG enabled network (it used to be system network, and name was never set for it in 2.2.x) --- setup/db/db/schema-40to410.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index b7b1c7a91dd..b4c30aebe88 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -1639,3 +1639,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Usage', 'DEFAULT', 'manageme INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (163, UUID(), 10, 'Ubuntu 12.04 (32-bit)'); INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (164, UUID(), 10, 'Ubuntu 12.04 (64-bit)'); + +#update shared sg enabled network with not null name in Advance Security Group enabled network +UPDATE `cloud`.`networks` set name='Shared SG enabled network', display_text='Shared SG enabled network' WHERE name IS null AND traffic_type='Guest' AND data_center_id IN (select id from data_center where networktype='Advanced' and is_security_group_enabled=1) AND acl_type='Domain'; + From f985e11c28f451183e64129a774edb4afe460bac Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 15 May 2013 11:01:43 -0700 Subject: [PATCH 027/412] CLOUDSTACK-747: internalLb in VPC - UI - create tier dialog - only one tier is allowed to have PublicLb in a VPC. --- ui/scripts/sharedFunctions.js | 34 +++++++++++----------------------- ui/scripts/vpc.js | 9 +++++---- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index a01840637e3..035299059c2 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -122,34 +122,22 @@ function createURL(apiName, options) { return urlString; } -/* -function fromdb(val) { - return sanitizeXSS(noNull(val)); -} -*/ - function todb(val) { return encodeURIComponent(val); } -/* -function noNull(val) { - if(val == null) - return ""; - else - return val; -} -*/ +//LB provider map +var lbProviderMap = { + "publicLb": { + "non-vpc": ["VirtualRouter", "Netscaler", "F5"], + "vpc": ["VpcVirtualRouter", "Netscaler"] + }, + "internalLb": { + "non-vpc": [], + "vpc": ["InternalLbVm"] + } +}; -/* -function sanitizeXSS(val) { // Prevent cross-site-script(XSS) attack - if(val == null || typeof(val) != "string") - return val; - val = val.replace(//g, ">"); //replace > whose unicode is \u003e - return unescape(val); -} -*/ // Role Functions function isAdmin() { diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 96c983b6b02..c54535dd932 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2499,15 +2499,16 @@ var items; if(networkSupportingLbExists == true) { items = $.grep(networkOfferings, function(networkOffering) { - var includingLbService = false; + var includingPublicLbService = false; $(networkOffering.service).each(function(){ var thisService = this; - if(thisService.name == "Lb") { - includingLbService = true; + //only one tier is allowed to have PublicLb provider in a VPC + if(thisService.name == "Lb" && lbProviderMap.publicLb.vpc.indexOf(thisService.provider[0].name) != -1) { + includingPublicLbService = true; return false; //break $.each() loop } }); - return !includingLbService; + return !includingPublicLbService; }); } else { From a4fc86ece9fd7c97d308a44b517ccef74053a624 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 14:28:41 -0700 Subject: [PATCH 028/412] Tier LB rules: Add load balancing multi-edit --- ui/scripts/vpc.js | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index c54535dd932..eb3a4b69675 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -297,6 +297,7 @@ // Internal load balancers tierLoadBalancers: { listView: { + id: 'ipAddresses', fields: { ipaddress: { label: 'label.ip.address' }, type: { label: 'label.type' } @@ -349,6 +350,121 @@ } } } + }, + detailView: { + isMaximized: true, + tabs: { + loadBalancing: { + title: 'label.load.balancing', + custom: function(args) { + var context = args.context; + var $multi = $('
    ').addClass('loadBalancer'); + + $multi.multiEdit({ + context: context, + listView: $.extend(true, {}, cloudStack.sections.instances, { + listView: { + fields: { + name: { label: 'label.name' }, + displayname: { label: 'label.display.name' }, + zonename: { label: 'label.zone.name' }, + state: { + label: 'label.state', + indicator: { + 'Running': 'on', + 'Stopped': 'off', + 'Destroyed': 'off', + 'Error': 'off' + } + } + }, + filters: false, + dataProvider: function(args) { + args.response.success({ data: [] }); + } + } + }), + + multipleAdd: true, + + fields: { + 'name': { edit: true, label: 'label.name', isEditable: true }, + 'publicport': { edit: true, label: 'label.public.port' }, + 'privateport': { edit: true, label: 'label.private.port' }, + 'algorithm': { + label: 'label.algorithm', + isEditable: true, + select: function(args) { + args.response.success({ + data: [ + { id: 'roundrobin', name: 'roundrobin', description: _l('label.round.robin') }, + { id: 'leastconn', name: 'leastconn', description: _l('label.least.connections') }, + { id: 'source', name: 'source', description: _l('label.source') } + ] + }); + } + }, + + 'sticky': { + label: 'label.stickiness', + custom: { + buttonLabel: 'label.configure', + action: cloudStack.lbStickyPolicy.dialog() + } + }, + + 'add-vm': { + label: 'label.add.vms', + addButton: true + } + }, + + tags: cloudStack.api.tags({ resourceType: 'LoadBalancer', contextId: 'multiRule' }), + + add: { + label: 'label.add.vms', + action: function(args) { + args.response.success(); + } + }, + actions: { + edit: { + label: 'label.edit', + action: function(args) { + args.response.success(); + } + }, + destroy: { + label: 'label.action.delete.load.balancer', + action: function(args) { + args.response.success(); + } + } + }, + + itemActions: { + add: { + label: 'label.add.vms.to.lb', + action: function(args) { + args.response.success(); + } + }, + destroy: { + label: 'label.remove.vm.from.lb', + action: function(args) { + args.response.success(); + } + } + }, + dataProvider: function(args) { + args.respons.success({ data: [] }); + } + }); + + return $multi; + } + } + } } } }, From b32d7c026c8d84b0b0c17f75cc4ea73bd932eca5 Mon Sep 17 00:00:00 2001 From: anthony Date: Wed, 23 Jan 2013 11:08:24 -0800 Subject: [PATCH 029/412] CLOUDSTACK-737, allow to add security group enabled networks in security group enabled zone --- .../ConfigurationManagerImpl.java | 10 +++--- .../consoleproxy/ConsoleProxyManagerImpl.java | 31 +++++++++++------- .../SecondaryStorageManagerImpl.java | 32 ++++++++++++------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index e1aaa5050f9..ef0a7845bf1 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -1774,13 +1774,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // check if zone has necessary trafficTypes before enabling try { PhysicalNetwork mgmtPhyNetwork; - if (NetworkType.Advanced == zone.getNetworkType()) { - // zone should have a physical network with public and management traffiType + // zone should have a physical network with management traffiType + mgmtPhyNetwork = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Management); + if (NetworkType.Advanced == zone.getNetworkType() && ! zone.isSecurityGroupEnabled() ) { + // advanced zone without SG should have a physical network with public Thpe _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Public); - mgmtPhyNetwork = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Management); - } else { - // zone should have a physical network with management traffiType - mgmtPhyNetwork = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Management); } try { diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 9a7a46faf3d..421e53f75be 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -687,19 +687,28 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); - TrafficType defaultTrafficType = TrafficType.Public; - if (dc.getNetworkType() == NetworkType.Basic || dc.isSecurityGroupEnabled()) { - defaultTrafficType = TrafficType.Guest; + NetworkVO defaultNetwork = null; + if (dc.getNetworkType() == NetworkType.Advanced && dc.isSecurityGroupEnabled()) { + List networks = _networkDao.listByZoneSecurityGroup(dataCenterId); + if (networks == null || networks.size() == 0) { + throw new CloudRuntimeException("Can not found security enabled network in SG Zone " + dc); + } + defaultNetwork = networks.get(0); + } else { + TrafficType defaultTrafficType = TrafficType.Public; + if (dc.getNetworkType() == NetworkType.Basic || dc.isSecurityGroupEnabled()) { + defaultTrafficType = TrafficType.Guest; + } + List defaultNetworks = _networkDao.listByZoneAndTrafficType(dataCenterId, defaultTrafficType); + + // api should never allow this situation to happen + if (defaultNetworks.size() != 1) { + throw new CloudRuntimeException("Found " + defaultNetworks.size() + " networks of type " + + defaultTrafficType + " when expect to find 1"); + } + defaultNetwork = defaultNetworks.get(0); } - List defaultNetworks = _networkDao.listByZoneAndTrafficType(dataCenterId, defaultTrafficType); - - if (defaultNetworks.size() != 1) { - throw new CloudRuntimeException("Found " + defaultNetworks.size() + " networks of type " + defaultTrafficType + " when expect to find 1"); - } - - NetworkVO defaultNetwork = defaultNetworks.get(0); - List offerings = _networkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemControlNetwork, NetworkOffering.SystemManagementNetwork); List> networks = new ArrayList>(offerings.size() + 1); NicProfile defaultNic = new NicProfile(); diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index 3cf9a7ef5a3..c3432863bec 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -538,19 +538,27 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); DataCenter dc = _dcDao.findById(plan.getDataCenterId()); - TrafficType defaultTrafficType = TrafficType.Public; - if (dc.getNetworkType() == NetworkType.Basic || dc.isSecurityGroupEnabled()) { - defaultTrafficType = TrafficType.Guest; + NetworkVO defaultNetwork = null; + if (dc.getNetworkType() == NetworkType.Advanced && dc.isSecurityGroupEnabled()) { + List networks = _networkDao.listByZoneSecurityGroup(dataCenterId); + if (networks == null || networks.size() == 0) { + throw new CloudRuntimeException("Can not found security enabled network in SG Zone " + dc); + } + defaultNetwork = networks.get(0); + } else { + TrafficType defaultTrafficType = TrafficType.Public; + + if (dc.getNetworkType() == NetworkType.Basic || dc.isSecurityGroupEnabled()) { + defaultTrafficType = TrafficType.Guest; + } + List defaultNetworks = _networkDao.listByZoneAndTrafficType(dataCenterId, defaultTrafficType); + // api should never allow this situation to happen + if (defaultNetworks.size() != 1) { + throw new CloudRuntimeException("Found " + defaultNetworks.size() + " networks of type " + + defaultTrafficType + " when expect to find 1"); + } + defaultNetwork = defaultNetworks.get(0); } - - List defaultNetworks = _networkDao.listByZoneAndTrafficType(dataCenterId, defaultTrafficType); - - //api should never allow this situation to happen - if (defaultNetworks.size() != 1) { - throw new CloudRuntimeException("Found " + defaultNetworks.size() + " networks of type " + defaultTrafficType + " when expect to find 1"); - } - - NetworkVO defaultNetwork = defaultNetworks.get(0); List offerings = _networkModel.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork, NetworkOfferingVO.SystemManagementNetwork, NetworkOfferingVO.SystemStorageNetwork); List> networks = new ArrayList>(offerings.size() + 1); From 297115cac56d9d211dd8a7ab0413973ed2e2df95 Mon Sep 17 00:00:00 2001 From: anthony Date: Thu, 24 Jan 2013 17:26:51 -0800 Subject: [PATCH 030/412] CLOUDSTACK-737 add xenserver support in UI only XenServer and KVM clusters are allowed in security enabled zone. only shared security enabled networks are allowed in security enabled zone. --- server/src/com/cloud/resource/ResourceManagerImpl.java | 6 ++++++ ui/scripts/zoneWizard.js | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 0ab35dd00a2..4bd62376b38 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -438,6 +438,12 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, + cmd.getHypervisor() + " to a supported "); } + if (zone.isSecurityGroupEnabled()) { + if( hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer ) { + throw new InvalidParameterValueException("Don't support hypervisor type " + hypervisorType + " in advanced security enabled zone"); + } + } + Cluster.ClusterType clusterType = null; if (cmd.getClusterType() != null && !cmd.getClusterType().isEmpty()) { clusterType = Cluster.ClusterType.valueOf(cmd.getClusterType()); diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 9b28c327b78..421c5911e4c 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -401,7 +401,6 @@ var nonSupportedHypervisors = {}; if(args.context.zones[0]['network-model'] == "Advanced" && args.context.zones[0]['zone-advanced-sg-enabled'] == "on") { firstOption = "KVM"; - nonSupportedHypervisors["XenServer"] = 1; //to developers: comment this line if you need to test Advanced SG-enabled zone with XenServer hypervisor nonSupportedHypervisors["VMware"] = 1; nonSupportedHypervisors["BareMetal"] = 1; nonSupportedHypervisors["Ovm"] = 1; From 81bce385bfd67a926cc87305aec7b42cb8d8af26 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 15:01:40 -0700 Subject: [PATCH 031/412] Fix typo --- ui/scripts/vpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index eb3a4b69675..cbb3a6ff1c3 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -457,7 +457,7 @@ } }, dataProvider: function(args) { - args.respons.success({ data: [] }); + args.response.success({ data: [] }); } }); From 612ce62cae8408cdf66aaab339c1cc387b4bf9b1 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 15:33:13 -0700 Subject: [PATCH 032/412] Add tier VM, PF, static NAT links --- ui/modules/vpc/vpc.js | 11 ++++++----- ui/scripts/instances.js | 8 ++++++++ ui/scripts/vpc.js | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 5c93e6cdbe6..64081a8a4d0 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -18,7 +18,9 @@ var elems = { tier: function(args) { var tier = args.tier; - var context = args.context; + var context = $.extend(true, {}, args.context, { + networks: [tier] + }); var dashboardItems = args.dashboardItems; var $tier = $('
    ').addClass('tier-item'); var $header = $('
    ').addClass('header'); @@ -41,9 +43,7 @@ var $detailView = $('
    ').detailView( $.extend(true, {}, cloudStack.vpc.tiers.detailView, { $browser: $browser, - context: $.extend(true, {}, context, { - networks: [tier] - }), + context: context, onActionComplete: function() { $tier.closest('.vpc-network-chart').trigger('reload'); } @@ -153,6 +153,7 @@ dashboard: function(args) { var $dashboard = $('
    ').addClass('dashboard'); var context = args.context; + var tier = context.networks[0]; $(args.dashboardItems).map(function(index, dashboardItem) { var $dashboardItem = $('
    ').addClass('dashboard-item'); @@ -167,7 +168,7 @@ $dashboardItem.click(function() { $('#browser .container').cloudBrowser('addPanel', { - title: dashboardItem.name, + title: tier.name + ' - ' + dashboardItem.name, maximizeIfSelected: true, complete: function($panel) { var section = cloudStack.vpc.sections[id]; diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index c76d843ed6e..31237a8855b 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -204,6 +204,14 @@ affinitygroupid: args.context.affinityGroups[0].id }); } + + if("vpc" in args.context && + "networks" in args.context) { + $.extend(data, { + vpcid: args.context.vpc[0].id, + networkid: args.context.networks[0].id + }); + } $.ajax({ url: createURL('listVirtualMachines'), diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index cbb3a6ff1c3..1e3efb71e2c 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -294,6 +294,26 @@ cloudStack.vpc = { // nTier sections sections: { + tierVMs: function() { + var list = $.extend(true, {}, cloudStack.sections.instances); + + list.listView.actions.add.action.custom = cloudStack.uiCustom.instanceWizard( + $.extend(true, {}, cloudStack.instanceWizard, { + pluginForm: { name: 'vpcTierInstanceWizard' } + }) + ); + + return list; + }, + + tierPortForwarders: function() { + return cloudStack.vpc.ipAddresses.listView(); + }, + + tierStaticNATs: function() { + return cloudStack.vpc.ipAddresses.listView(); + }, + // Internal load balancers tierLoadBalancers: { listView: { From 34d7014c88490c5de009128086f1c273275580cb Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 15:58:55 -0700 Subject: [PATCH 033/412] ACL multi-edit: Fix reorder drag handle --- ui/scripts/ui/widgets/multiEdit.js | 48 ++---------------------------- ui/scripts/vpc.js | 8 ++++- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 0f1515f5aec..14f379fcc2b 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -41,30 +41,6 @@ // Add reorder actions if (reorder) { - var sort = function($tr, action) { - var $listView = $tr.closest('.data-body'); - var viewArgs = $listView.data('view-args'); - var context = $.extend( - true, {}, - options.context - ); - var rowIndex = $tr.closest('.data-body').find('.data-item').size() - ($tr.closest('.data-item').index()); - - context[viewArgs.activeSection] = $tr.data('json-obj'); - - action.action({ - context: context, - index: rowIndex, - response: { - success: function(args) {}, - error: function(args) { - // Move back to previous position - rowActions.moveTo($tr, rowIndex); - } - } - }); - }; - $('
    ').addClass('actions reorder').appendTo($tr).append(function() { var $td = $(this); @@ -99,27 +75,6 @@ }); }); }); - - // Draggable action - var initDraggable = function($dataItem) { - var originalIndex; - - return $dataItem.sortable({ - handle: '.action.moveDrag', - start: function(event, ui) { - originalIndex = ui.item.index(); - }, - stop: function(event, ui) { - $dataItem.closest('.data-body').find('.data-item').each(function() { - sort($(this), reorder.moveDrag); - }); - } - }); - }; - - if (reorder && reorder.moveDrag) { - initDraggable($tr.closest('.data-item')); - } } @@ -872,6 +827,9 @@ $.fn.multiEdit = function(args) { if (reorder) { $('').addClass('reorder').appendTo($thead); $('').addClass('reorder').appendTo($inputForm); + $multi.find('.data-body').sortable({ + handle: '.action.moveDrag' + }); } $.each(args.fields, function(fieldName, field) { diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 1e3efb71e2c..8e393142ec9 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -27,7 +27,13 @@ return hiddenFields; // Returns fields to be hidden }, - reorder: cloudStack.api.actions.sort('updateAclRule', 'multiRule'), + reorder: { + moveDrag: { + action: function(args) { + args.response.success(); + } + } + }, fields: { 'cidrlist': { edit: true, label: 'label.cidr' }, 'protocol': { From 6b406f622088821f53353bc94c81e45ec2ea45c6 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 16:06:09 -0700 Subject: [PATCH 034/412] Lower width of draggable column --- ui/css/cloudstack3.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 7f6df22797e..9fde1b82a37 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -7974,6 +7974,15 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t .multi-edit .header-fields input[type=submit] { } +/* Sortable */ +.multi-edit table tbody tr td.reorder, +.multi-edit table thead tr th.reorder { + width: 30px !important; + min-width: 30px !important; + max-width: 30px !important; +} + + /*Security Rules*/ .security-rules .multi-edit input { width: 69px; From eb34d91afde1d1236a45a4b9088004fb08832430 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 15 May 2013 16:06:20 -0700 Subject: [PATCH 035/412] Multi-edit: Lower font size of table header --- ui/css/cloudstack3.css | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 9fde1b82a37..27841b03ece 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -7925,6 +7925,7 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t width: 87px !important; min-width: 87px !important; max-width: 87px !important; + font-size: 10px; } /** Header fields*/ From b5acb9034f2463c891fa424cd9deaae17501eebc Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 16 May 2013 11:34:38 -0700 Subject: [PATCH 036/412] CLOUDSTACK-747: UI - Internal LB - implement Add Internal LB action. --- ui/scripts/vpc.js | 66 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 8e393142ec9..7a1c50c10aa 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -323,7 +323,7 @@ // Internal load balancers tierLoadBalancers: { listView: { - id: 'ipAddresses', + id: 'internalLb', fields: { ipaddress: { label: 'label.ip.address' }, type: { label: 'label.type' } @@ -339,30 +339,72 @@ }, actions: { add: { - label: 'Add new LB', + label: 'Add Internal LB', createForm: { - title: 'Add new LB', - desc: 'Please specify the type of load balancer you would like to create.', - fields: { - type: { - label: 'label.type', + title: 'Add Internal LB', + fields: { + name: { label: 'label.name', validation: { required: true } }, + description: { label: 'label.description', validation: { required: false } }, + sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, + sourceport: { label: 'sourceport', validation: { required: true } }, + instanceport: { label: 'instanceport', validation: { required: true } }, + algorithm: { + label: 'label.algorithm', + validation: { required: true }, select: function(args) { args.response.success({ data: [ - { id: 'internal', description: 'Internal' }, - { id: 'public', description: 'Public' } + { id: 'source', description: 'source' }, + { id: 'roundrobin', description: 'roundrobin' }, + { id: 'leastconn', description: 'leastconn' } ] }); } - } + } } }, messages: { notification: function(args) { - return 'Add LB to VPC network'; + return 'Add Internal LB'; } }, - action: function(args) { + action: function(args) { + var data = { + name: args.data.name, + sourceport: args.data.sourceport, + instanceport: args.data.instanceport, + algorithm: args.data.algorithm, + networkid: args.context.networks[0].id, + sourceipaddressnetworkid: args.context.networks[0].id, + scheme: 'Internal' + }; + if(args.data.description != null && args.data.description.length > 0){ + $.extend(data, { + description: args.data.description + }); + } + if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ + $.extend(data, { + sourceipaddress: args.data.sourceipaddress + }); + } + $.ajax({ + url: createURL('createLoadBalancer'), + data: data, + success: function(json){ + var jid = json.createloadbalancerresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; + } + } + } + ); + } + }); + args.response.success(); }, notification: { From ff287c10d9c30b19d588e4c5153788a58f97ebf0 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Thu, 16 May 2013 13:45:17 -0700 Subject: [PATCH 037/412] Tier LB UI: Split list view into internal/public --- ui/modules/vpc/vpc.css | 2 +- ui/modules/vpc/vpc.js | 2 +- ui/scripts/vpc.js | 382 ++++++++++++++++++++--------------------- 3 files changed, 192 insertions(+), 194 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index a7ef13eadee..92ee80beb05 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -140,7 +140,7 @@ } .vpc-network-chart .tier-item .content .dashboard-item .total { - font-size: 28px; + font-size: 17px; /*+placement:shift 7px 5px;*/ position: relative; left: 7px; diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 64081a8a4d0..a3a571eb4c3 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -230,7 +230,7 @@ { id: 'tierLoadBalancers', name: 'Load balancers', - total: 5 + total: '5 Internal
    6 Public' }, { id: 'tierPortForwarders', diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 7a1c50c10aa..57786926686 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -322,214 +322,212 @@ // Internal load balancers tierLoadBalancers: { - listView: { - id: 'internalLb', - fields: { - ipaddress: { label: 'label.ip.address' }, - type: { label: 'label.type' } - }, - dataProvider: function(args) { - args.response.success({ - data: [ - { ipaddress: '10.3.2.1', type: 'Internal' }, - { ipaddress: '10.3.2.3', type: 'Internal' }, - { ipaddress: '10.232.1.4', type: 'Public' } - ] - }); - }, - actions: { - add: { - label: 'Add Internal LB', - createForm: { - title: 'Add Internal LB', - fields: { - name: { label: 'label.name', validation: { required: true } }, - description: { label: 'label.description', validation: { required: false } }, - sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, - sourceport: { label: 'sourceport', validation: { required: true } }, - instanceport: { label: 'instanceport', validation: { required: true } }, - algorithm: { - label: 'label.algorithm', - validation: { required: true }, - select: function(args) { - args.response.success({ - data: [ - { id: 'source', description: 'source' }, - { id: 'roundrobin', description: 'roundrobin' }, - { id: 'leastconn', description: 'leastconn' } - ] + listView: true, + sectionSelect: { + label: 'Select LB type: ' + }, + sections: { + internalLoadBalancers: { + type: 'select', + title: 'Internal LB', + listView: { + id: 'internalLoadBalancers', + fields: { + ipaddress: { label: 'label.ip.address' }, + type: { label: 'label.type' } + }, + dataProvider: function(args) { + args.response.success({ + data: [ + { ipaddress: '10.3.2.1', type: 'Internal' }, + { ipaddress: '10.3.2.3', type: 'Internal' }, + { ipaddress: '10.232.1.4', type: 'Public' } + ] + }); + }, + actions: { + add: { + label: 'Add Internal LB', + createForm: { + title: 'Add Internal LB', + fields: { + name: { label: 'label.name', validation: { required: true } }, + description: { label: 'label.description', validation: { required: false } }, + sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, + sourceport: { label: 'sourceport', validation: { required: true } }, + instanceport: { label: 'instanceport', validation: { required: true } }, + algorithm: { + label: 'label.algorithm', + validation: { required: true }, + select: function(args) { + args.response.success({ + data: [ + { id: 'source', description: 'source' }, + { id: 'roundrobin', description: 'roundrobin' }, + { id: 'leastconn', description: 'leastconn' } + ] + }); + } + } + } + }, + messages: { + notification: function(args) { + return 'Add Internal LB'; + } + }, + action: function(args) { + var data = { + name: args.data.name, + sourceport: args.data.sourceport, + instanceport: args.data.instanceport, + algorithm: args.data.algorithm, + networkid: args.context.networks[0].id, + sourceipaddressnetworkid: args.context.networks[0].id, + scheme: 'Internal' + }; + if(args.data.description != null && args.data.description.length > 0){ + $.extend(data, { + description: args.data.description + }); + } + if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ + $.extend(data, { + sourceipaddress: args.data.sourceipaddress + }); + } + $.ajax({ + url: createURL('createLoadBalancer'), + data: data, + success: function(json){ + var jid = json.createloadbalancerresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; + } + } + } + ); + } + }); + + args.response.success(); + }, + notification: { + poll: function(args) { + args.complete({ + data: { + ipaddress: '10.0.3.2', + type: 'Internal' + } }); } - } - } - }, - messages: { - notification: function(args) { - return 'Add Internal LB'; - } - }, - action: function(args) { - var data = { - name: args.data.name, - sourceport: args.data.sourceport, - instanceport: args.data.instanceport, - algorithm: args.data.algorithm, - networkid: args.context.networks[0].id, - sourceipaddressnetworkid: args.context.networks[0].id, - scheme: 'Internal' - }; - if(args.data.description != null && args.data.description.length > 0){ - $.extend(data, { - description: args.data.description - }); - } - if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ - $.extend(data, { - sourceipaddress: args.data.sourceipaddress - }); - } - $.ajax({ - url: createURL('createLoadBalancer'), - data: data, - success: function(json){ - var jid = json.createloadbalancerresponse.jobid; - args.response.success( - {_custom: - {jobId: jid, - getUpdatedItem: function(json) { - return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; - } - } - } - ); } - }); - - args.response.success(); - }, - notification: { - poll: function(args) { - args.complete({ - data: { - ipaddress: '10.0.3.2', - type: 'Internal' - } - }); } } } }, - detailView: { - isMaximized: true, - tabs: { - loadBalancing: { - title: 'label.load.balancing', - custom: function(args) { - var context = args.context; - var $multi = $('
    ').addClass('loadBalancer'); - $multi.multiEdit({ - context: context, - listView: $.extend(true, {}, cloudStack.sections.instances, { - listView: { - fields: { - name: { label: 'label.name' }, - displayname: { label: 'label.display.name' }, - zonename: { label: 'label.zone.name' }, - state: { - label: 'label.state', - indicator: { - 'Running': 'on', - 'Stopped': 'off', - 'Destroyed': 'off', - 'Error': 'off' - } - } - }, - filters: false, - dataProvider: function(args) { - args.response.success({ data: [] }); - } - } - }), - - multipleAdd: true, - - fields: { - 'name': { edit: true, label: 'label.name', isEditable: true }, - 'publicport': { edit: true, label: 'label.public.port' }, - 'privateport': { edit: true, label: 'label.private.port' }, - 'algorithm': { + publicLoadBalancers: { + type: 'select', + title: 'Public LB', + listView: { + id: 'publicLoadBalancers', + fields: { + ipaddress: { label: 'label.ip.address' }, + type: { label: 'label.type' } + }, + dataProvider: function(args) { + args.response.success({ + data: [ + { ipaddress: '10.3.2.1', type: 'Internal' }, + { ipaddress: '10.3.2.3', type: 'Internal' }, + { ipaddress: '10.232.1.4', type: 'Public' } + ] + }); + }, + actions: { + add: { + label: 'Add Public LB', + createForm: { + title: 'Add Public LB', + fields: { + name: { label: 'label.name', validation: { required: true } }, + description: { label: 'label.description', validation: { required: false } }, + sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, + sourceport: { label: 'sourceport', validation: { required: true } }, + instanceport: { label: 'instanceport', validation: { required: true } }, + algorithm: { label: 'label.algorithm', - isEditable: true, + validation: { required: true }, select: function(args) { args.response.success({ data: [ - { id: 'roundrobin', name: 'roundrobin', description: _l('label.round.robin') }, - { id: 'leastconn', name: 'leastconn', description: _l('label.least.connections') }, - { id: 'source', name: 'source', description: _l('label.source') } + { id: 'source', description: 'source' }, + { id: 'roundrobin', description: 'roundrobin' }, + { id: 'leastconn', description: 'leastconn' } ] }); } - }, - - 'sticky': { - label: 'label.stickiness', - custom: { - buttonLabel: 'label.configure', - action: cloudStack.lbStickyPolicy.dialog() - } - }, - - 'add-vm': { - label: 'label.add.vms', - addButton: true - } - }, - - tags: cloudStack.api.tags({ resourceType: 'LoadBalancer', contextId: 'multiRule' }), - - add: { - label: 'label.add.vms', - action: function(args) { - args.response.success(); - } - }, - actions: { - edit: { - label: 'label.edit', - action: function(args) { - args.response.success(); - } - }, - destroy: { - label: 'label.action.delete.load.balancer', - action: function(args) { - args.response.success(); - } - } - }, - - itemActions: { - add: { - label: 'label.add.vms.to.lb', - action: function(args) { - args.response.success(); - } - }, - destroy: { - label: 'label.remove.vm.from.lb', - action: function(args) { - args.response.success(); - } - } - }, - dataProvider: function(args) { - args.response.success({ data: [] }); + } } - }); - - return $multi; + }, + messages: { + notification: function(args) { + return 'Add Public LB'; + } + }, + action: function(args) { + var data = { + name: args.data.name, + sourceport: args.data.sourceport, + instanceport: args.data.instanceport, + algorithm: args.data.algorithm, + networkid: args.context.networks[0].id, + sourceipaddressnetworkid: args.context.networks[0].id, + scheme: 'Public' + }; + if(args.data.description != null && args.data.description.length > 0){ + $.extend(data, { + description: args.data.description + }); + } + if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ + $.extend(data, { + sourceipaddress: args.data.sourceipaddress + }); + } + $.ajax({ + url: createURL('createLoadBalancer'), + data: data, + success: function(json){ + var jid = json.createloadbalancerresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; + } + } + } + ); + } + }); + + args.response.success(); + }, + notification: { + poll: function(args) { + args.complete({ + data: { + ipaddress: '10.0.3.2', + type: 'Internal' + } + }); + } + } } } } From 7aaff0cfed028b853f6a72fc7d1b2bf589cbd500 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Thu, 16 May 2013 13:46:37 -0700 Subject: [PATCH 038/412] Fix typo --- ui/scripts/vpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 57786926686..df4664be3c1 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -324,7 +324,7 @@ tierLoadBalancers: { listView: true, sectionSelect: { - label: 'Select LB type: ' + label: 'Select LB type' }, sections: { internalLoadBalancers: { From 93e046a24ca22baf059ec141619dee6d2d6cb1a9 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 17 May 2013 10:12:49 -0700 Subject: [PATCH 039/412] Fix CSS styling for multi-line VPC dashboard items --- ui/modules/vpc/vpc.css | 6 +++++- ui/modules/vpc/vpc.js | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 92ee80beb05..51c3f7e05bd 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -140,7 +140,7 @@ } .vpc-network-chart .tier-item .content .dashboard-item .total { - font-size: 17px; + font-size: 30px; /*+placement:shift 7px 5px;*/ position: relative; left: 7px; @@ -154,6 +154,10 @@ text-shadow: 0px 1px 1px #FFFFFF; } +.vpc-network-chart .tier-item .content .dashboard-item .total.multiline { + font-size: 14px; +} + .vpc-network-chart .tier-item .content .dashboard-item .name { font-size: 11px; text-transform: uppercase; diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index a3a571eb4c3..f3cdaf040d5 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -162,7 +162,15 @@ var id = dashboardItem.id; $name.find('span').html(dashboardItem.name); - $total.find('span').html(dashboardItem.total); + + + if (dashboardItem.totalMultiLine) { + $total.find('span').html(dashboardItem.totalMultiLine); + $total.addClass('multiline'); + } else { + $total.find('span').html(dashboardItem.total); + } + $dashboardItem.append($total, $name); $dashboardItem.appendTo($dashboard); @@ -230,7 +238,7 @@ { id: 'tierLoadBalancers', name: 'Load balancers', - total: '5 Internal
    6 Public' + totalMultiLine: '5 Internal
    6 Public' }, { id: 'tierPortForwarders', From 981b5ddd1be845b3f73ce69325652cadabb72807 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 20 May 2013 23:42:55 +0530 Subject: [PATCH 040/412] ACL DENY RULES UI-API Integration --- ui/css/cloudstack3.css | 6 +- ui/scripts/sharedFunctions.js | 2 +- ui/scripts/vpc.js | 178 +++++++++++++++++++++++++++++----- 3 files changed, 159 insertions(+), 27 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 27841b03ece..549db3771ac 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11828,13 +11828,15 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .downloadVolume .icon, .downloadTemplate .icon, -.downloadISO .icon { +.downloadISO .icon, +.replaceacllist .icon { background-position: -35px -125px; } .downloadVolume:hover .icon, .downloadTemplate:hover .icon, -.downloadISO:hover .icon { +.downloadISO:hover .icon, +.replaceacllist:hover .icon { background-position: -35px -707px; } diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 035299059c2..d87f0dcb0ae 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -276,7 +276,7 @@ cloudStack.actionFilter = { guestNetwork: function(args) { var jsonObj = args.context.item; var allowedActions = []; - + allowedActions.push('replaceacllist'); if(jsonObj.type == 'Isolated') { allowedActions.push('edit'); //only Isolated network is allowed to upgrade to a different network offering (Shared network is not allowed to) allowedActions.push('restart'); diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index df4664be3c1..6ea41c99b6b 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -189,8 +189,7 @@ $.ajax({ url: createURL('createNetworkACL'), data: $.extend(args.data, { - networkid: args.context.networks ? - args.context.networks[0].id : args.data.networkid + aclid: args.context.aclLists[0].id }), dataType: 'json', success: function(data) { @@ -198,10 +197,11 @@ _custom: { jobId: data.createnetworkaclresponse.jobid, getUpdatedItem: function(json) { - var networkName = $multi.find('select[name=networkid] option[value=' + args.data.networkid + ']').html(); - var data = $.extend(json.queryasyncjobresultresponse.jobresult.networkacl, { + //var networkName = $multi.find('select[name=networkid] option[value=' + args.data.networkid + ']').html(); + /* var data = $.extend(json.queryasyncjobresultresponse.jobresult.networkacl, { networkid: networkName - }); + });*/ + var data = json.queryasyncjobresultresponse.jobresult.networkacl; var aclRules = $multi.data('acl-rules'); aclRules.push(data); @@ -551,34 +551,88 @@ id: 'aclLists', fields: { name: { label: 'label.name' }, - total: { label: 'label.total' } + id: { label: 'id' } }, dataProvider: function(args) { - args.response.success({ - data: [ - { name: 'ACL list 1', total: 3 }, - { name: 'ACL list 2', total: 2 } - ] - }); - }, + $.ajax({ + + url:createURL('listNetworkACLLists&vpc_id=' + args.context.vpc[0].id), + success:function(json){ + var items = json.listnetworkacllistsresponse.networkacllist; + + args.response.success({ + data:items + }); + } + }); + }, + + actions:{ + add:{ + label:'Add ACL List', + createForm:{ + label: 'Add ACL List', + fields:{ + name:{label:'ACL List Name',validation:{required:true}}, + description:{label:'Description',validation:{required:true}} + } + }, + messages: { + notification: function(args) { + return 'Add Network ACL List'; + } + }, + action:function(args){ + var data = { + name:args.data.name, + description:args.data.description + + }; + + $.ajax({ + url:createURL('createNetworkACLList&vpcid='+ args.context.vpc[0].id), + data:data, + success:function(json){ + var items = json.createnetworkacllistresponse; + args.response.success({ + data:items + }); + } + }); + } + } + }, + detailView: { isMaximized: true, tabs: { - details: { + /* details: { + title: 'label.details', fields: [ { - name: { label: 'label.name', isEditable: true } + name: { label: 'label.name', isEditable: true }, + id: {label:'id'} } + ], dataProvider: function(args) { - args.response.success({ data: args.context.aclLists[0] }); - } - }, + $.ajax({ + url:createURL('listNetworkACLs&aclid=' + args.context.aclLists[0].id), + success:function(json){ + var items = json.listnetworkaclsresponse.networkacl; + args.response.success({ + data:items + }); + } + }); + } + + },*/ aclRules: { - title: 'label.acl.rules', + title: 'ACL List Rules', custom: function(args) { return $('
    ').multiEdit($.extend(true, {}, aclMultiEdit, { context: args.context, @@ -586,9 +640,14 @@ networkid: false }, dataProvider: function(args) { + $.ajax({ + url:createURL('listNetworkACLs&aclid=' + args.context.aclLists[0].id), + success:function(json){ + var items = json.listnetworkaclsresponse.networkacl; + args.response.success({ - data: [ - { + data:items + /* { cidrlist: '10.1.1.0/24', protocol: 'TCP', startport: 22, endport: 22, @@ -602,9 +661,11 @@ networkid: 0, trafficType: 'Ingress' } - ] + ]*/ }); - } + } + }); + } })); } } @@ -2232,7 +2293,76 @@ notification: { poll: pollAsyncJobResult } - } + }, + + replaceacllist:{ + + label:'Replace ACL List', + createForm:{ + title:'Replace ACL List', + label:'Replace ACL List', + fields:{ + aclid:{ + label:'ACL', + select:function(args){ + $.ajax({ + url: createURL('listNetworkACLLists'), + dataType: 'json', + async: true, + success: function(json) { + var objs = json.listnetworkacllistsresponse.networkacllist; + var items = []; + $(objs).each(function() { + + items.push({id: this.id, description: this.name}); + }); + args.response.success({data: items}); + } + }); + } + } + } + }, + action: function(args) { + $.ajax({ + url: createURL("replaceNetworkACLList&networkid=" + args.context.networks[0].id + "&aclid=" + args.data.aclid ), + dataType: "json", + success: function(json) { + var jid = json.replacenetworkacllistresponse.jobid; + args.response.success( + + {_custom: + { + jobId: jid, + getUpdatedItem: function(json) { + var item = json.queryasyncjobresultresponse.jobresult.aclid; + return {data:item}; + } + } + } + + ) + }, + + error:function(json){ + + args.response.error(parseXMLHttpResponse(json)); + } + }); + }, + notification: { + poll: pollAsyncJobResult + }, + + messages: { + confirm: function(args) { + return 'Do you want to replace the ACL with a new one ?'; + }, + notification: function(args) { + return 'ACL replaced'; + } + } + } }, tabFilter: function(args) { From a1614a5f8f7c865e804706bd100f158e6abb5eaa Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 21 May 2013 01:25:10 +0530 Subject: [PATCH 041/412] ACL DENY RULES Network ACL List Detail View --- ui/scripts/vpc.js | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 6ea41c99b6b..08cf82dda61 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -551,6 +551,7 @@ id: 'aclLists', fields: { name: { label: 'label.name' }, + description: {label:'Description'}, id: { label: 'id' } }, dataProvider: function(args) { @@ -607,30 +608,27 @@ detailView: { isMaximized: true, tabs: { - /* details: { + details: { + title: 'label.details', fields: [ { name: { label: 'label.name', isEditable: true }, - id: {label:'id'} + description: {label:'Description'}, + id:{label:'id'} } - + ], dataProvider: function(args) { - $.ajax({ - url:createURL('listNetworkACLs&aclid=' + args.context.aclLists[0].id), - success:function(json){ - var items = json.listnetworkaclsresponse.networkacl; - args.response.success({ - data:items - }); - } - }); - } - - },*/ + var items = args.context.aclLists[0]; + args.response.success({ + data: items + }); + } + }, + aclRules: { title: 'ACL List Rules', custom: function(args) { From 034c21b6fc173deeb003d1ef2473c4c47c5bad4a Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 20 May 2013 13:32:46 -0700 Subject: [PATCH 042/412] ACL multi-edit: add 'action' field to specify rule as allow/deny --- ui/scripts/vpc.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 08cf82dda61..ce2334ce608 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -36,6 +36,17 @@ }, fields: { 'cidrlist': { edit: true, label: 'label.cidr' }, + action: { + label: 'Action', + select: function(args) { + args.response.success({ + data: [ + { name: 'Allow', description: 'Allow' }, + { name: 'Deny', description: 'Deny' } + ] + }); + } + }, 'protocol': { label: 'label.protocol', select: function(args) { From 9e24af77b9537d2e2126f2dde3e0b6a6cb871d60 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 20 May 2013 13:34:37 -0700 Subject: [PATCH 043/412] CLOUDSTACK-747: internalLb in VPC - UI - create network offering - system offering dropdown is for router only. Change its variable name to be more intuitive. --- ui/scripts/configuration.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 9a08c4c56b1..e3421a380ef 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1122,7 +1122,7 @@ title: 'label.add.network.offering', preFilter: function(args) { var $availability = args.$form.find('.form-item[rel=availability]'); - var $serviceOfferingId = args.$form.find('.form-item[rel=serviceOfferingId]'); + var $systemOfferingForRouter = args.$form.find('.form-item[rel=systemOfferingForRouter]'); var $conservemode = args.$form.find('.form-item[rel=conservemode]'); var $serviceSourceNatRedundantRouterCapabilityCheckbox = args.$form.find('.form-item[rel="service.SourceNat.redundantRouterCapabilityCheckbox"]'); var hasAdvancedZones = false; @@ -1185,10 +1185,10 @@ } }); if(havingVirtualRouterForAtLeastOneService == true) { - $serviceOfferingId.css('display', 'inline-block'); + $systemOfferingForRouter.css('display', 'inline-block'); } else { - $serviceOfferingId.hide(); + $systemOfferingForRouter.hide(); } @@ -1569,8 +1569,8 @@ }, //show or hide upon checked services and selected providers above (begin) - serviceOfferingId: { - label: 'label.system.offering', + systemOfferingForRouter: { + label: 'System Offering for Router', docID: 'helpNetworkOfferingSystemOffering', select: function(args) { $.ajax({ @@ -1829,8 +1829,8 @@ if(args.$form.find('.form-item[rel=availability]').css("display") == "none") inputData['availability'] = 'Optional'; - if(args.$form.find('.form-item[rel=serviceOfferingId]').css("display") == "none") - delete inputData.serviceOfferingId; + if(args.$form.find('.form-item[rel=systemOfferingForRouter]').css("display") == "none") + delete inputData.systemOfferingForRouter; inputData['traffictype'] = 'GUEST'; //traffic type dropdown has been removed since it has only one option ('Guest'). Hardcode traffic type value here. From bd764cc581a47e213350ef5749ae523f5b0398c2 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 20 May 2013 13:55:43 -0700 Subject: [PATCH 044/412] Pass dashboard data via dataProvider --- ui/modules/vpc/vpc.js | 58 ++++++------------------------------------- ui/scripts/vpc.js | 51 ++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index f3cdaf040d5..3c44a4cabca 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -234,28 +234,7 @@ var $tier = elems.tier({ context: context, tier: tier, - dashboardItems: [ - { - id: 'tierLoadBalancers', - name: 'Load balancers', - totalMultiLine: '5 Internal
    6 Public' - }, - { - id: 'tierPortForwarders', - name: 'Port forwarders', - total: 4 - }, - { - id: 'tierStaticNATs', - name: 'Static NATs', - total: 3 - }, - { - id: 'tierVMs', - name: 'Virtual Machines', - total: 300 - } - ] + dashboardItems: tier._dashboardItems }); $tier.appendTo($tiers); @@ -271,37 +250,16 @@ if (args.complete) { args.complete($chart); } + + // Router + $router = elems.router({ + context: context, + dashboardItems: data.routerDashboard + }).appendTo($chart); } - } + } }); - // Router - $router = elems.router({ - context: context, - dashboardItems: [ - { - id: 'privateGateways', - name: 'Private gateways', - total: 1 - }, - { - id: 'publicIPs', - name: 'Public IP addresses', - total: 2 - }, - { - id: 'siteToSiteVPNs', - name: 'Site-to-site VPNs', - total: 3 - }, - { - id: 'networkACLLists', - name: 'Network ACL lists', - total: 2 - } - ] - }).appendTo($chart); - $chart.bind('reload', function() { chart({ complete: function($newChart) { diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index ce2334ce608..a081ab85667 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2998,7 +2998,56 @@ }); } } - args.response.success({ tiers: networks }); + args.response.success({ + routerDashboard: [ + { + id: 'privateGateways', + name: 'Private gateways', + total: 0 + }, + { + id: 'publicIPs', + name: 'Public IP addresses', + total: 0 + }, + { + id: 'siteToSiteVPNs', + name: 'Site-to-site VPNs', + total: 0 + }, + { + id: 'networkACLLists', + name: 'Network ACL lists', + total: 0 + } + ], + tiers: $(networks).map(function(index, tier) { + return $.extend(tier, { + _dashboardItems: [ + { + id: 'tierLoadBalancers', + name: 'Load balancers', + totalMultiLine: '0 Internal
    0 Public' + }, + { + id: 'tierPortForwarders', + name: 'Port forwarders', + total: 0 + }, + { + id: 'tierStaticNATs', + name: 'Static NATs', + total: 0 + }, + { + id: 'tierVMs', + name: 'Virtual Machines', + total: 0 + } + ] + }); + }) + }); } }); } From 6b7098c990c014ac88ae2981c3c8f20a2a536239 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 20 May 2013 14:10:52 -0700 Subject: [PATCH 045/412] Add network ACL item: Fix add action --- ui/scripts/vpc.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index a081ab85667..710265c8d0f 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -208,15 +208,6 @@ _custom: { jobId: data.createnetworkaclresponse.jobid, getUpdatedItem: function(json) { - //var networkName = $multi.find('select[name=networkid] option[value=' + args.data.networkid + ']').html(); - /* var data = $.extend(json.queryasyncjobresultresponse.jobresult.networkacl, { - networkid: networkName - });*/ - var data = json.queryasyncjobresultresponse.jobresult.networkacl; - var aclRules = $multi.data('acl-rules'); - - aclRules.push(data); - $multi.data('acl-rules', aclRules); $(window).trigger('cloudStack.fullRefresh'); return data; From 933060dfeb88e5032da813b2fea12027757693a6 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 20 May 2013 19:17:21 -0700 Subject: [PATCH 046/412] CLOUDSTACK-747: internalLb in VPC - UI - create network offering - add LB Type dropdodwn which is shown when VPC is checked and LB service is checked, hidden otherwise. LB Type (publicLb, internalLb) will determine the options in LB Provider dropdown. --- ui/scripts/configuration.js | 85 +++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index e3421a380ef..058f44097b8 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1122,6 +1122,7 @@ title: 'label.add.network.offering', preFilter: function(args) { var $availability = args.$form.find('.form-item[rel=availability]'); + var $lbType = args.$form.find('.form-item[rel=lbType]'); var $systemOfferingForRouter = args.$form.find('.form-item[rel=systemOfferingForRouter]'); var $conservemode = args.$form.find('.form-item[rel=conservemode]'); var $serviceSourceNatRedundantRouterCapabilityCheckbox = args.$form.find('.form-item[rel="service.SourceNat.redundantRouterCapabilityCheckbox"]'); @@ -1147,18 +1148,18 @@ //check whether to show or hide availability field var $sourceNATField = args.$form.find('input[name=\"service.SourceNat.isEnabled\"]'); var $guestTypeField = args.$form.find('select[name=guestIpType]'); - + + var $useVpc = args.$form.find('.form-item[rel=\"useVpc\"]'); + var $useVpcCb = $useVpc.find("input[type=checkbox]"); if($guestTypeField.val() == 'Shared') { //Shared network offering - args.$form.find('.form-item[rel=\"useVpc\"]').hide(); - - var $useVpcCb = args.$form.find('.form-item[rel=\"useVpc\"]').find("input[type=checkbox]"); + $useVpc.hide(); if($useVpcCb.is(':checked')) { //if useVpc is checked, $useVpcCb.removeAttr("checked"); //remove "checked" attribute in useVpc $useVpcCb.trigger("click"); //trigger useVpc.onChange() } } else { //Isolated network offering - args.$form.find('.form-item[rel=\"useVpc\"]').css('display', 'inline-block'); + $useVpc.css('display', 'inline-block'); } @@ -1170,7 +1171,14 @@ $availability.hide(); } - + //when useVpc is checked and service.Lb.isEnabled is checked + if($useVpcCb.is(':checked') && $("input[name='service.Lb.isEnabled']").is(":checked") == true) { + $lbType.css('display', 'inline-block'); + } + else { + $lbType.hide(); + } + //when service(s) has Virtual Router as provider..... var havingVirtualRouterForAtLeastOneService = false; $(serviceCheckboxNames).each(function(){ @@ -1427,7 +1435,7 @@ label: 'VPC', docID: 'helpNetworkOfferingVPC', isBoolean: true, - onChange: function(args) { + onChange: function(args) { var $checkbox = args.$checkbox; var $selects = $checkbox.closest('form').find('.dynamic-input select'); var $vpcOptions = $selects.find('option[value=VpcVirtualRouter]'); @@ -1444,7 +1452,67 @@ } } }, - + + lbType: { //only shown when VPC is checked and LB service is checked + label: 'Load Balancer Type', + isHidden: true, + select: function(args) { + args.response.success({data: [ + {id: 'publicLb', description: 'Public LB'}, + {id: 'internalLb', description: 'Internal LB'} + ]}); + + args.$select.change(function() { + if($(this).is(':visible') == false) + return; //if lbType is not visible, do nothing. + + var $lbProvider = $(this).closest('form').find('.form-item[rel=\"service.Lb.provider\"]').find('select'); + var $lbProviderOptions = $lbProvider.find('option'); + + if($(this).val() == 'publicLb') { //disable all providers except the ones in lbProviderMap.publicLb.vpc => ["VpcVirtualRouter", "Netscaler"] + for(var i = 0; i < $lbProviderOptions.length; i++ ) { + var $option = $lbProviderOptions.eq(i); + var supportedProviders = lbProviderMap.publicLb.vpc; + var thisOpionIsSupported = false; + for(var k = 0; k < supportedProviders.length; k++ ) { + if($option.val() == supportedProviders[k]) { + thisOpionIsSupported = true; + break; + } + } + if(thisOpionIsSupported == true) { + $option.attr('disabled', false); + } + else { + $option.attr('disabled', true); + } + } + } + else if($(this).val() == 'internalLb') { //disable all providers except the ones in lbProviderMap.internalLb.vpc => ["InternalLbVm"] + for(var i = 0; i < $lbProviderOptions.length; i++ ) { + var $option = $lbProviderOptions.eq(i); + var supportedProviders = lbProviderMap.internalLb.vpc; + var thisOpionIsSupported = false; + for(var k = 0; k < supportedProviders.length; k++ ) { + if($option.val() == supportedProviders[k]) { + thisOpionIsSupported = true; + break; + } + } + if(thisOpionIsSupported == true) { + $option.attr('disabled', false); + } + else { + $option.attr('disabled', true); + } + } + } + + $lbProvider.val($lbProvider.find('option:first')); + }); + } + }, + supportedServices: { label: 'label.supported.services', @@ -1571,6 +1639,7 @@ //show or hide upon checked services and selected providers above (begin) systemOfferingForRouter: { label: 'System Offering for Router', + isHidden: true, docID: 'helpNetworkOfferingSystemOffering', select: function(args) { $.ajax({ From e5e41b0f454059511ea97a534331db7213bef67e Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 21 May 2013 16:35:14 +0530 Subject: [PATCH 047/412] Remove ACL List action item --- ui/scripts/vpc.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 710265c8d0f..2c954200b96 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -609,6 +609,44 @@ detailView: { isMaximized: true, + actions:{ + deleteacllist:{ + label:'Delete ACL List', + messages: { + confirm: function(args) { + return 'Are you sure you want to delete this ACL list ?'; + }, + notification: function(args) { + return 'Delete ACL list'; + } + }, + action:function(args){ + $.ajax({ + url:createURL('deleteNetworkACLList&id=' + args.context.aclLists[0].id), + success:function(json){ + var jid = json.deletenetworkacllistresponse.jobid; + args.response.success( + {_custom: + { jobId: jid + } + } + ); + + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + + } + }, + + tabs: { details: { From e6382b1896692e4e76afa7f7a7f7a2c304958006 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 21 May 2013 22:15:15 +0530 Subject: [PATCH 048/412] Delete ACL list filter and css --- ui/css/cloudstack3.css | 6 ++++-- ui/scripts/vpc.js | 11 ++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 549db3771ac..16584537905 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11736,13 +11736,15 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .destroy .icon, .remove .icon, .delete .icon, -.decline .icon { +.decline .icon, +.deleteacllist .icon { background-position: 1px -92px; } .destroy:hover .icon, .remove:hover .icon, -.delete:hover .icon { +.delete:hover .icon, +.deleteacllist:hover .icon { background-position: 1px -674px; } diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 2c954200b96..fa5c0a56c8e 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -663,7 +663,16 @@ dataProvider: function(args) { var items = args.context.aclLists[0]; args.response.success({ - data: items + data: items, + actionFilter: function(args) { + var allowedActions = []; + if(isAdmin()) { + allowedActions.push("deleteacllist"); + + } + return allowedActions; + } + }); } From ccf1ebc26b3aa361ceba837e669ab60053c0c0de Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 21 May 2013 10:57:12 -0700 Subject: [PATCH 049/412] CLOUDSTACK-747: internalLb in VPC - UI - create network offering - when VPC checkbox is checked, enable provider InternalLbVm, VpcVirtualRouter, Netscaler. When VPC checkbox is unchecked, disable provider InternalLbVm, VpcVirtualRouter. --- ui/scripts/configuration.js | 41 ++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 058f44097b8..7485898dc19 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1436,20 +1436,37 @@ docID: 'helpNetworkOfferingVPC', isBoolean: true, onChange: function(args) { - var $checkbox = args.$checkbox; - var $selects = $checkbox.closest('form').find('.dynamic-input select'); - var $vpcOptions = $selects.find('option[value=VpcVirtualRouter]'); + var $vpc = args.$checkbox; + var $providers = $vpc.closest('form').find('.dynamic-input select'); + var $optionsOfProviders = $providers.find('option'); - if ($checkbox.is(':checked')) { - $vpcOptions.siblings().attr('disabled', true); - $selects.val('VpcVirtualRouter'); - } else { - $vpcOptions.siblings().attr('disabled', false); - $vpcOptions.attr('disabled', true); - $selects.each(function() { - $(this).val($(this).find('option:first')); - }); + //p.s. Netscaler is supported in both vpc and non-vpc + + if ($vpc.is(':checked')) { //*** vpc *** + $optionsOfProviders.each(function(index) { + if($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter' || $(this).val() == 'Netscaler') { + $(this).attr('disabled', false); + } + else { + $(this).attr('disabled', true); + } + }); + } + else { //*** non-vpc *** + $optionsOfProviders.each(function(index) { + if($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter') { + $(this).attr('disabled', true); + } + else { + $(this).attr('disabled', false); + } + }); } + + $providers.each(function() { + $(this).val($(this).find('option:first')); + }); + } }, From 75df90663f72bb0b8c9c8de84de8684cb96a1ba6 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 21 May 2013 11:37:12 -0700 Subject: [PATCH 050/412] CLOUDSTACK-747: internalLb in VPC - UI - create network offering - when Lb service is checked and LB provider is InternalLbVm, pass capability type as lbSchemes and capability value as internal. --- ui/scripts/configuration.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 7485898dc19..92c3d00478e 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1834,7 +1834,13 @@ inputData['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilitytype'] = 'associatePublicIP'; inputData['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilityvalue'] = true; //because this checkbox's value == "on" serviceCapabilityIndex++; - } + } + else if((key == 'service.Lb.provider') && ("Lb" in serviceProviderMap) && (serviceProviderMap.Lb == "InternalLbVm")) { + inputData['servicecapabilitylist[' + serviceCapabilityIndex + '].service'] = 'lb'; + inputData['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilitytype'] = 'lbSchemes'; + inputData['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilityvalue'] = 'internal'; + serviceCapabilityIndex++; + } } else if (value != '') { // Normal data inputData[key] = value; From 0eceb0da603dfb93df9314b6f034ca32d45a14b6 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 21 May 2013 11:59:03 -0700 Subject: [PATCH 051/412] CLOUDSTACK-747: UI - create network offering - default sourceNat type as per account instead of per zone. --- ui/scripts/configuration.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 92c3d00478e..5762e648103 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1702,9 +1702,9 @@ dependsOn: 'service.SourceNat.isEnabled', select: function(args) { args.response.success({ - data: [ - { id: 'perzone', description: 'Per zone'}, - { id: 'peraccount', description: 'Per account'} + data: [ + { id: 'peraccount', description: 'Per account'}, + { id: 'perzone', description: 'Per zone'} ] }); } From 451c83c998a96f908a04d3329e1dc695c64849e8 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 12:52:55 -0700 Subject: [PATCH 052/412] Fix listView add/replace row for VPC section --- ui/scripts/ui/widgets/listView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/ui/widgets/listView.js b/ui/scripts/ui/widgets/listView.js index 0d5ef6fc751..031874dff1f 100644 --- a/ui/scripts/ui/widgets/listView.js +++ b/ui/scripts/ui/widgets/listView.js @@ -1873,7 +1873,7 @@ if (!options) options = {}; var viewArgs = listView.data('view-args'); - var listViewArgs = viewArgs.listView ? viewArgs.listView : viewArgs; + var listViewArgs = $.isPlainObject(viewArgs.listView) ? viewArgs.listView : viewArgs; var targetArgs = listViewArgs.activeSection ? listViewArgs.sections[ listViewArgs.activeSection ].listView : listViewArgs; @@ -1903,7 +1903,7 @@ var $newRow; var $listView = $row.closest('.list-view'); var viewArgs = $listView.data('view-args'); - var listViewArgs = viewArgs.listView ? viewArgs.listView : viewArgs; + var listViewArgs = $.isPlainObject(viewArgs.listView) ? viewArgs.listView : viewArgs; var targetArgs = listViewArgs.activeSection ? listViewArgs.sections[ listViewArgs.activeSection ].listView : listViewArgs; From 9fff2c4a666cfb0a330c4385812584c91fa49188 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 12:54:07 -0700 Subject: [PATCH 053/412] Add VM total to dashboard --- ui/scripts/vpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index fa5c0a56c8e..9cef91a0793 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3080,7 +3080,7 @@ { id: 'tierVMs', name: 'Virtual Machines', - total: 0 + total: $.isArray(tier.virtualMachines) ? tier.virtualMachines.length : 0 } ] }); From c89ca6a92f0eb9e30dbdaccca2b97ccad32f0a01 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 21 May 2013 13:15:09 -0700 Subject: [PATCH 054/412] CLOUDSTACK-747: internalLb in VPC - populate listView of internal LB. --- ui/scripts/vpc.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 9cef91a0793..d100aeb602b 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -335,17 +335,21 @@ listView: { id: 'internalLoadBalancers', fields: { - ipaddress: { label: 'label.ip.address' }, - type: { label: 'label.type' } + name: { label: 'label.name' }, + sourceipaddress: { label: 'Source IP Address' } }, - dataProvider: function(args) { - args.response.success({ - data: [ - { ipaddress: '10.3.2.1', type: 'Internal' }, - { ipaddress: '10.3.2.3', type: 'Internal' }, - { ipaddress: '10.232.1.4', type: 'Public' } - ] - }); + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + networkid: args.context.networks[0].id + }, + success: function(json) { + var items = json.listloadbalancerssresponse.loadbalancer; + args.response.success({ data: items }); + + } + }); }, actions: { add: { From 41f9a12d38b7f405a1424089dbdd640f9dd3651e Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 21 May 2013 13:48:27 -0700 Subject: [PATCH 055/412] CLOUDSTACK-747: internalLb in VPC - populate detailView of internal LB. --- ui/scripts/vpc.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index d100aeb602b..7778a2d0d6b 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -432,7 +432,41 @@ } } } - } + }, + + detailView: { + name: 'Internal Lb details', + actions: { + //assignVm: { + + //} + }, + tabs: { + details: { + title: 'label.details', + fields: [ + { + name: { label: 'label.name' } + }, + { + id: { label: 'label.id' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + id: args.context.internalLoadBalancers[0].id + }, + success: function(json) { + var item = json.listloadbalancerssresponse.loadbalancer[0]; + args.response.success({ data: item }); + } + }); + } + } + } + } } }, From dfa93b52a894930d847b4f39b69be990f2aacd4b Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 15:07:21 -0700 Subject: [PATCH 056/412] Detail view actions: Add support listView selection Adds a new dialog 'cloudStack.dialog.listView' -- Supports displaying a list view in a popup dialog, for selecting items for an action (i.e., selecting VMs for an LB rule). Arguments are a list view object and the 'type' of selection: either 'checkbox' or 'radio' Example: detailView: { name: 'Internal Lb details', actions: { assignVm: { label: 'Assign VMs to LB', messages: { notification: function(args) { return 'Assign VM to internal LB rule'; } }, listView: $.extend(true, {}, cloudStack.sections.instances.listView, { type: 'checkbox', filters: false }), action: function(args) { args.response.success(); }, notification: { poll: function(args) { args.complete(); } } } }, ... } --- ui/scripts/ui/dialog.js | 92 ++++++++++++++++++++++++++++- ui/scripts/ui/widgets/detailView.js | 16 ++++- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/ui/scripts/ui/dialog.js b/ui/scripts/ui/dialog.js index bb372fbf3d6..45f928e38a9 100644 --- a/ui/scripts/ui/dialog.js +++ b/ui/scripts/ui/dialog.js @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -(function($, cloudStack) { +(function($, cloudStack, _l) { cloudStack.dialog = { /** * Error message form @@ -506,6 +506,94 @@ } }, + // Dialog with list view selector + listView: function(args) { + var listView = args.listView; + var after = args.after; + var context = args.context; + var $listView = $('
    '); + + listView.actions = { + select: { + label: _l('label.select.instance'), + type: listView.type, + action: { + uiCustom: function(args) { + var $item = args.$item; + var $input = $item.find('td.actions input:visible'); + + if ($input.attr('type') == 'checkbox') { + if ($input.is(':checked')) + $item.addClass('multi-edit-selected'); + else + $item.removeClass('multi-edit-selected'); + } else { + $item.siblings().removeClass('multi-edit-selected'); + $item.addClass('multi-edit-selected'); + } + } + } + } + }; + + // Init list view + $listView = $('
    ').listView({ + context: context, + uiCustom: true, + listView: listView + }); + + // Change action label + $listView.find('th.actions').html(_l('label.select')); + + $listView.dialog({ + dialogClass: 'multi-edit-add-list panel', + width: 825, + title: _l('Select VM'), + buttons: [ + { + text: _l('label.apply'), + 'class': 'ok', + click: function() { + if (!$listView.find( + 'input[type=radio]:checked, input[type=checkbox]:checked' + ).size()) { + cloudStack.dialog.notice({ message: _l('message.select.instance')}); + + return false; + } + + after({ + context: $.extend(true, {}, context, { + instances: $listView.find('tr.multi-edit-selected').map(function(index, row) { + var $row = $(row); + + return $row.data('json-obj'); + }) + }) + }); + + $listView.remove(); + + $('div.overlay').remove(); + } + }, + { + text: _l('label.cancel'), + 'class': 'cancel', + click: function() { + $listView.fadeOut(function() { + $listView.remove(); + }); + $('div.overlay').fadeOut(function() { + $('div.overlay').remove(); + }); + } + } + ] + }).parent('.ui-dialog').overlay(); + }, + /** * to change a property(e.g. validation) of a createForm field after a createForm is rendered */ @@ -612,4 +700,4 @@ return false; } }; -})(window.jQuery, window.cloudStack); +})(window.jQuery, window.cloudStack, _l); diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index a721a44ec05..11e82b35f1c 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -70,7 +70,8 @@ action.notification : {}; var messages = action.messages; var id = args.id; - var context = args.context ? args.context : $detailView.data('view-args').context; + var context = $.extend(true, {}, + args.context ? args.context : $detailView.data('view-args').context); var _custom = $detailView.data('_custom'); var customAction = action.action.custom; var noAdd = action.noAdd; @@ -273,7 +274,7 @@ notification.desc = messages.notification(messageArgs); notification.section = 'instances'; - if (!action.createForm) { + if (!action.createForm && !action.listView) { if (messages && messages.confirm) { cloudStack.dialog.confirm({ message: messages.confirm(messageArgs), @@ -286,7 +287,7 @@ } else { performAction({ id: id }); } - } else { + } else if (action.createForm) { cloudStack.dialog.createForm({ form: action.createForm, after: function(args) { @@ -301,6 +302,15 @@ }, context: context }); + } else if (action.listView) { + cloudStack.dialog.listView({ + context: context, + listView: action.listView, + after: function(args) { + context = args.context; + performAction(); + } + }); } } }, From c6c85387fb69beec39357b7a4f217db4df8bd443 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 15:09:30 -0700 Subject: [PATCH 057/412] VPC: Use listView selector for assignVm action --- ui/css/cloudstack3.css | 6 ++++-- ui/scripts/vpc.js | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 16584537905..da647877f27 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11871,12 +11871,14 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it } .add .icon, -.addNew .icon { +.addNew .icon, +.assignVm .icon { background-position: -37px -61px; } .add:hover .icon, -.addNew:hover .icon { +.addNew:hover .icon, +.assignVm:hover .icon { background-position: -37px -643px; } diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 7778a2d0d6b..50fa5db708b 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -437,9 +437,24 @@ detailView: { name: 'Internal Lb details', actions: { - //assignVm: { - - //} + assignVm: { + label: 'Assign VMs to LB', + messages: { + notification: function(args) { return 'Assign VM to internal LB rule'; } + }, + listView: $.extend(true, {}, cloudStack.sections.instances.listView, { + type: 'checkbox', + filters: false + }), + action: function(args) { + args.response.success(); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + } }, tabs: { details: { From b88da4e1c108912fe8162cf2fd18be08eade64b4 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 15:36:50 -0700 Subject: [PATCH 058/412] VPC UI: Reload chart on perform detailView action --- ui/modules/vpc/vpc.js | 3 +++ ui/scripts/ui/widgets/listView.js | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 3c44a4cabca..cc0273fcbb1 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -188,6 +188,9 @@ if (section.listView) { $section.listView($.extend(true, {}, section, { + onActionComplete: function() { + $dashboardItem.closest('.vpc-network-chart').trigger('reload'); + }, context: context })); } diff --git a/ui/scripts/ui/widgets/listView.js b/ui/scripts/ui/widgets/listView.js index 031874dff1f..d68f91fac7c 100644 --- a/ui/scripts/ui/widgets/listView.js +++ b/ui/scripts/ui/widgets/listView.js @@ -104,6 +104,10 @@ cloudStack.ui.notifications.add( notification, function(args) { + if (listViewArgs.onActionComplete) { + listViewArgs.onActionComplete(); + } + if ($item.is(':visible') && !isHeader) { replaceItem( $item, @@ -175,6 +179,10 @@ if (additional && additional.success) additional.success(args); + if (listViewArgs.onActionComplete == true) { + listViewArgs.onActionComplete(); + } + cloudStack.ui.notifications.add( notification, @@ -213,6 +221,10 @@ if (options.complete) { options.complete(args); } + + if (listViewArgs.onActionComplete) { + listViewArgs.onActionComplete(); + } }, {}, @@ -1187,6 +1199,10 @@ $quickViewTooltip.hide(); }, onActionComplete: function() { + if (listViewArgs.onActionComplete) { + listViewArgs.onActionComplete(); + } + $tr.removeClass('loading').find('td:last .loading').remove(); $quickViewTooltip.remove(); } @@ -1798,6 +1814,8 @@ }); } + detailViewArgs.data.onActionComplete = listViewArgs.onActionComplete; + createDetailView( detailViewArgs, function($detailView) { //complete(), callback funcion From 24dd6fb66ff68c2969364340f72ea9aab5faf58e Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 15:40:00 -0700 Subject: [PATCH 059/412] Cleanup formatting --- ui/scripts/vpc.js | 208 +++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 105 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 50fa5db708b..450b5326ca8 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -610,99 +610,97 @@ id: { label: 'id' } }, dataProvider: function(args) { - $.ajax({ - - url:createURL('listNetworkACLLists&vpc_id=' + args.context.vpc[0].id), - success:function(json){ - var items = json.listnetworkacllistsresponse.networkacllist; - - args.response.success({ - data:items + url:createURL('listNetworkACLLists&vpc_id=' + args.context.vpc[0].id), + success:function(json){ + var items = json.listnetworkacllistsresponse.networkacllist; + + args.response.success({ + data:items }); - } - }); + } + }); }, - actions:{ - add:{ - label:'Add ACL List', - createForm:{ + actions:{ + add:{ + label:'Add ACL List', + createForm:{ label: 'Add ACL List', fields:{ name:{label:'ACL List Name',validation:{required:true}}, description:{label:'Description',validation:{required:true}} } }, - messages: { - notification: function(args) { - return 'Add Network ACL List'; - } - }, - action:function(args){ - var data = { - name:args.data.name, - description:args.data.description + messages: { + notification: function(args) { + return 'Add Network ACL List'; + } + }, + action:function(args){ + var data = { + name:args.data.name, + description:args.data.description - }; + }; - $.ajax({ - url:createURL('createNetworkACLList&vpcid='+ args.context.vpc[0].id), - data:data, - success:function(json){ - var items = json.createnetworkacllistresponse; - args.response.success({ - data:items - }); - } - }); + $.ajax({ + url:createURL('createNetworkACLList&vpcid='+ args.context.vpc[0].id), + data:data, + success:function(json){ + var items = json.createnetworkacllistresponse; + args.response.success({ + data:items + }); + } + }); } - } + } }, detailView: { isMaximized: true, - actions:{ - deleteacllist:{ - label:'Delete ACL List', - messages: { - confirm: function(args) { - return 'Are you sure you want to delete this ACL list ?'; - }, - notification: function(args) { - return 'Delete ACL list'; - } - }, - action:function(args){ - $.ajax({ - url:createURL('deleteNetworkACLList&id=' + args.context.aclLists[0].id), - success:function(json){ - var jid = json.deletenetworkacllistresponse.jobid; - args.response.success( - {_custom: - { jobId: jid - } - } - ); - - }, - error:function(json){ - args.response.error(parseXMLHttpResponse(json)); - - } - }); - }, - notification: { - poll: pollAsyncJobResult - } - + actions:{ + deleteacllist:{ + label:'Delete ACL List', + messages: { + confirm: function(args) { + return 'Are you sure you want to delete this ACL list ?'; + }, + notification: function(args) { + return 'Delete ACL list'; } }, + action:function(args){ + $.ajax({ + url:createURL('deleteNetworkACLList&id=' + args.context.aclLists[0].id), + success:function(json){ + var jid = json.deletenetworkacllistresponse.jobid; + args.response.success( + {_custom: + { jobId: jid + } + } + ); + + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + + } + }, tabs: { - details: { + details: { title: 'label.details', fields: [ @@ -714,22 +712,22 @@ ], dataProvider: function(args) { - var items = args.context.aclLists[0]; - args.response.success({ - data: items, - actionFilter: function(args) { - var allowedActions = []; - if(isAdmin()) { - allowedActions.push("deleteacllist"); + var items = args.context.aclLists[0]; + args.response.success({ + data: items, + actionFilter: function(args) { + var allowedActions = []; + if(isAdmin()) { + allowedActions.push("deleteacllist"); - } - return allowedActions; - } + } + return allowedActions; + } - }); + }); } - }, + }, aclRules: { title: 'ACL List Rules', @@ -740,32 +738,32 @@ networkid: false }, dataProvider: function(args) { - $.ajax({ - url:createURL('listNetworkACLs&aclid=' + args.context.aclLists[0].id), - success:function(json){ + $.ajax({ + url:createURL('listNetworkACLs&aclid=' + args.context.aclLists[0].id), + success:function(json){ var items = json.listnetworkaclsresponse.networkacl; - args.response.success({ - data:items - /* { - cidrlist: '10.1.1.0/24', - protocol: 'TCP', - startport: 22, endport: 22, - networkid: 0, - traffictype: 'Egress' - }, - { - cidrlist: '10.2.1.0/24', - protocol: 'UDP', - startport: 56, endport: 72, - networkid: 0, - trafficType: 'Ingress' - } - ]*/ + args.response.success({ + data:items + /* { + cidrlist: '10.1.1.0/24', + protocol: 'TCP', + startport: 22, endport: 22, + networkid: 0, + traffictype: 'Egress' + }, + { + cidrlist: '10.2.1.0/24', + protocol: 'UDP', + startport: 56, endport: 72, + networkid: 0, + trafficType: 'Ingress' + } + ]*/ + }); + } }); - } - }); - } + } })); } } From 62f7ab6ffe896b87be12a4048f9be88636a5bf3e Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 15:48:11 -0700 Subject: [PATCH 060/412] Delete ACL action: Rename to 'remove' to fix UI feedback --- ui/scripts/vpc.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 450b5326ca8..617d9faa0a5 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -661,7 +661,7 @@ detailView: { isMaximized: true, actions:{ - deleteacllist:{ + remove: { label:'Delete ACL List', messages: { confirm: function(args) { @@ -682,26 +682,20 @@ } } ); - }, error:function(json){ args.response.error(parseXMLHttpResponse(json)); - } }); }, notification: { poll: pollAsyncJobResult } - } }, - tabs: { - details: { - title: 'label.details', fields: [ { @@ -709,7 +703,6 @@ description: {label:'Description'}, id:{label:'id'} } - ], dataProvider: function(args) { var items = args.context.aclLists[0]; @@ -718,13 +711,11 @@ actionFilter: function(args) { var allowedActions = []; if(isAdmin()) { - allowedActions.push("deleteacllist"); + allowedActions.push("remove"); } return allowedActions; } - - }); } }, From 7c05bb13ccd3cb5be9e22ec472db4b8f368bd4f8 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 15:51:15 -0700 Subject: [PATCH 061/412] Fix quickview for ACL list --- ui/scripts/vpc.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 617d9faa0a5..e47cb1244b7 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -706,16 +706,18 @@ ], dataProvider: function(args) { var items = args.context.aclLists[0]; - args.response.success({ - data: items, - actionFilter: function(args) { - var allowedActions = []; - if(isAdmin()) { - allowedActions.push("remove"); + setTimeout(function() { + args.response.success({ + data: items, + actionFilter: function(args) { + var allowedActions = []; + if(isAdmin()) { + allowedActions.push("remove"); + } + return allowedActions; } - return allowedActions; - } + }); }); } }, From 3c2be57c35877c98d165c5f5be464815f6af2484 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 21 May 2013 15:57:58 -0700 Subject: [PATCH 062/412] CLOUDSTACK-747: internalLb in VPC - internal LB detail View - implement action assign VM to internal lb. --- ui/scripts/vpc.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index e47cb1244b7..23e7b0cc898 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -446,13 +446,33 @@ type: 'checkbox', filters: false }), - action: function(args) { - args.response.success(); + action: function(args) { + var vms = args.context.instances; + var array1 = []; + for(var i = 0; i < vms.length; i++) { + array1.push(vms[i].id); + } + var virtualmachineids = array1.join(','); + + $.ajax({ + url: createURL('assignToLoadBalancerRule'), + data: { + id: args.context.internalLoadBalancers[0].id, + virtualmachineids: virtualmachineids + }, + dataType: 'json', + async: true, + success: function(data) { + debugger; + var jid = data.assigntoloadbalancerruleresponse.jobid; + args.response.success({ + _custom: { jobId: jid } + }); + } + }); }, notification: { - poll: function(args) { - args.complete(); - } + poll: pollAsyncJobResult } } }, From 4ce76429e28a1e1da6ca14d79082da1f1cf0a74f Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 16:16:27 -0700 Subject: [PATCH 063/412] Add internal LB count to VPC chart --- ui/scripts/vpc.js | 161 ++++++++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 64 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 23e7b0cc898..f96d552b49d 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3084,72 +3084,105 @@ async: true, success: function(json) { var networks = json.listnetworksresponse.network; - if(networks != null && networks.length > 0) { - for(var i = 0; i < networks.length; i++) { - $.ajax({ - url: createURL("listVirtualMachines"), - dataType: "json", - data: { - networkid: networks[i].id, - listAll: true - }, - async: false, - success: function(json) { - networks[i].virtualMachines = json.listvirtualmachinesresponse.virtualmachine; - } - }); + var loadBalancers; + var error = false; + + // Get load balancers + $.ajax({ + url: createURL('listLoadBalancers'), + data: { details: 'min', vpcid: args.context.vpc[0].id }, + success: function(json) { + loadBalancers = json.listloadbalancerssresponse.loadbalancer ? + json.listloadbalancerssresponse.loadbalancer : [] + }, + error: function(json) { + error = true; } - } - args.response.success({ - routerDashboard: [ - { - id: 'privateGateways', - name: 'Private gateways', - total: 0 - }, - { - id: 'publicIPs', - name: 'Public IP addresses', - total: 0 - }, - { - id: 'siteToSiteVPNs', - name: 'Site-to-site VPNs', - total: 0 - }, - { - id: 'networkACLLists', - name: 'Network ACL lists', - total: 0 - } - ], - tiers: $(networks).map(function(index, tier) { - return $.extend(tier, { - _dashboardItems: [ - { - id: 'tierLoadBalancers', - name: 'Load balancers', - totalMultiLine: '0 Internal
    0 Public' - }, - { - id: 'tierPortForwarders', - name: 'Port forwarders', - total: 0 - }, - { - id: 'tierStaticNATs', - name: 'Static NATs', - total: 0 - }, - { - id: 'tierVMs', - name: 'Virtual Machines', - total: $.isArray(tier.virtualMachines) ? tier.virtualMachines.length : 0 - } - ] - }); - }) }); + + var dataTimer = setInterval(function() { + console.log('timer'); + + var complete = loadBalancers; + + if (complete) { + clearInterval(dataTimer); + + if(networks != null && networks.length > 0) { + for(var i = 0; i < networks.length; i++) { + $.ajax({ + url: createURL("listVirtualMachines"), + dataType: "json", + data: { + networkid: networks[i].id, + listAll: true + }, + async: false, + success: function(json) { + networks[i].virtualMachines = json.listvirtualmachinesresponse.virtualmachine; + } + }); + } + } + args.response.success({ + routerDashboard: [ + { + id: 'privateGateways', + name: 'Private gateways', + total: 0 + }, + { + id: 'publicIPs', + name: 'Public IP addresses', + total: 0 + }, + { + id: 'siteToSiteVPNs', + name: 'Site-to-site VPNs', + total: 0 + }, + { + id: 'networkACLLists', + name: 'Network ACL lists', + total: 0 + } + ], + tiers: $(networks).map(function(index, tier) { + var internalLoadBalancers = $.grep(loadBalancers, function(lb) { + return lb.networkid == tier.id; + }); + + return $.extend(tier, { + _dashboardItems: [ + { + id: 'tierLoadBalancers', + name: 'Load balancers', + totalMultiLine: internalLoadBalancers.length + ' Internal
    0 Public' + }, + { + id: 'tierPortForwarders', + name: 'Port forwarders', + total: 0 + }, + { + id: 'tierStaticNATs', + name: 'Static NATs', + total: 0 + }, + { + id: 'tierVMs', + name: 'Virtual Machines', + total: $.isArray(tier.virtualMachines) ? tier.virtualMachines.length : 0 + } + ] + }); + }) + }); + } else if (error) { + clearInterval(dataTimer); + cloudStack.dialog.notice({ message: 'Error loading dashboard data.' }); + } + }, 500); } }); } From a3e97bbc3f677426e6649d20af7747678d0d2fcc Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 16:17:12 -0700 Subject: [PATCH 064/412] Remove console.log --- ui/scripts/vpc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index f96d552b49d..e5ba23119ea 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3101,8 +3101,6 @@ }); var dataTimer = setInterval(function() { - console.log('timer'); - var complete = loadBalancers; if (complete) { From 531a2f713f6cf8712b2f0def45e6a95cc9128e97 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 21 May 2013 16:28:57 -0700 Subject: [PATCH 065/412] Add ACL lists count to VPC chart --- ui/scripts/vpc.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index e5ba23119ea..bf136b8de66 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3084,7 +3084,7 @@ async: true, success: function(json) { var networks = json.listnetworksresponse.network; - var loadBalancers; + var loadBalancers, networkACLLists; var error = false; // Get load balancers @@ -3100,8 +3100,21 @@ } }); + // Get network ACL lists + $.ajax({ + url: createURL('listNetworkACLLists'), + data: { details: 'min', 'vpc_id': args.context.vpc[0].id }, + success: function(json) { + networkACLLists = json.listnetworkacllistsresponse.networkacllist ? + json.listnetworkacllistsresponse.networkacllist : [] + }, + error: function(json) { + error = true; + } + }); + var dataTimer = setInterval(function() { - var complete = loadBalancers; + var complete = loadBalancers && networkACLLists; if (complete) { clearInterval(dataTimer); @@ -3142,7 +3155,7 @@ { id: 'networkACLLists', name: 'Network ACL lists', - total: 0 + total: networkACLLists.length } ], tiers: $(networks).map(function(index, tier) { From ba08af936150be06b9e843f91613d57b62122221 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 22 May 2013 10:50:15 +0530 Subject: [PATCH 066/412] ACL List - Addition in the detail view for Tier --- ui/scripts/vpc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index bf136b8de66..a3dadaace73 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2635,6 +2635,8 @@ isEditable: true }, + aclid:{label:'ACL id'}, + domain: { label: 'label.domain' }, account: { label: 'label.account' } } From 1f420ec085b075e74a4b2bd45cb0a6e58473f918 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 22 May 2013 10:42:49 -0700 Subject: [PATCH 067/412] CLOUDSTACK-747: internalLb in VPC - Infrastructure menu - network service provider - add InternalLbVm. Clicking it will lead to a screen that can enable/disable provider and have instances tab that can start/stop LB Instance. --- ui/scripts/system.js | 385 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 383 insertions(+), 2 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 11365c1603d..32feb6dd50c 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -2708,7 +2708,363 @@ } } }, - + + InternalLbVm: { + id: 'InternalLbVm', + label: 'InternalLbVm', + isMaximized: true, + type: 'detailView', + fields: { + name: { label: 'label.name' }, + ipaddress: { label: 'label.ip.address' }, + state: { label: 'label.status', indicator: { 'Enabled': 'on' } } + }, + tabs: { + network: { + title: 'label.network', + fields: [ + { + name: { label: 'label.name' } + }, + { + id: { label: 'label.id' }, + state: { label: 'label.state' }, + physicalnetworkid: { label: 'label.physical.network.ID' }, + destinationphysicalnetworkid: { label: 'label.destination.physical.network.id' }, + supportedServices: { label: 'label.supported.services' } + } + ], + dataProvider: function(args) { + refreshNspData("InternalLbVm"); + args.response.success({ + actionFilter: virtualRouterProviderActionFilter, + data: $.extend(nspMap["InternalLbVm"], { + supportedServices: nspMap["InternalLbVm"].servicelist.join(', ') + }) + }); + } + }, + + instances: { + title: 'label.instances', + listView: { + label: 'label.virtual.appliances', + id: 'internallbinstances', + fields: { + name: { label: 'label.name' }, + zonename: { label: 'label.zone' }, + routerType: { + label: 'label.type' + }, + state: { + converter: function(str) { + // For localization + return str; + }, + label: 'label.status', + indicator: { + 'Running': 'on', + 'Stopped': 'off', + 'Error': 'off' + } + } + }, + dataProvider: function(args) { + var array1 = []; + if(args.filterBy != null) { + if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { + switch(args.filterBy.search.by) { + case "name": + if(args.filterBy.search.value.length > 0) + array1.push("&keyword=" + args.filterBy.search.value); + break; + } + } + } + + var routers = []; + $.ajax({ + url: createURL("listInternalLoadBalancerVMs&zoneid=" + selectedZoneObj.id + "&listAll=true&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), + success: function(json) { + var items = json.listinternallbvmssresponse.internalloadbalancervm ? + json.listinternallbvmssresponse.internalloadbalancervm : []; + + $(items).map(function(index, item) { + routers.push(item); + }); + + // Get project routers + $.ajax({ + url: createURL("listInternalLoadBalancerVMs&zoneid=" + selectedZoneObj.id + "&listAll=true&page=" + args.page + "&pagesize=" + pageSize + array1.join("") + "&projectid=-1"), + success: function(json) { + var items = json.listinternallbvmssresponse.internalloadbalancervm ? + json.listinternallbvmssresponse.internalloadbalancervm : []; + + $(items).map(function(index, item) { + routers.push(item); + }); + args.response.success({ + actionFilter: internallbinstanceActionfilter, + data: $(routers).map(mapRouterType) + }); + } + }); + } + }); + }, + detailView: { + name: 'Virtual applicance details', + actions: { + start: { + label: 'Start LB VM', + messages: { + confirm: function(args) { + return 'Please confirm you want to start LB VM'; + }, + notification: function(args) { + return 'Start LB VM'; + } + }, + action: function(args) { + $.ajax({ + url: createURL('startInternalLoadBalancerVM&id=' + args.context.internallbinstances[0].id), + dataType: 'json', + async: true, + success: function(json) { + var jid = json.startinternallbvmresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.internalloadbalancervm; + }, + getActionFilter: function() { + return internallbinstanceActionfilter; + } + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + }, + + stop: { + label: 'Stop LB VM', + createForm: { + title: 'Please confirm you want to stop LB VM', + desc: 'Stop LB VM', + fields: { + forced: { + label: 'force.stop', + isBoolean: true, + isChecked: false + } + } + }, + messages: { + notification: function(args) { + return 'Stop LB VM'; + } + }, + action: function(args) { + var array1 = []; + array1.push("&forced=" + (args.data.forced == "on")); + $.ajax({ + url: createURL('stopInternalLoadBalancerVM&id=' + args.context.internallbinstances[0].id + array1.join("")), + dataType: 'json', + async: true, + success: function(json) { + var jid = json.stopinternallbvmresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.internalloadbalancervm; + }, + getActionFilter: function() { + return internallbinstanceActionfilter; + } + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + tabs: { + details: { + title: 'label.details', + preFilter: function(args) { + var hiddenFields = []; + if (!args.context.internallbinstances[0].project) { + hiddenFields.push('project'); + hiddenFields.push('projectid'); + } + if(selectedZoneObj.networktype == 'Basic') { + hiddenFields.push('publicip'); //In Basic zone, guest IP is public IP. So, publicip is not returned by listRouters API. Only guestipaddress is returned by listRouters API. + } + return hiddenFields; + }, + fields: [ + { + name: { label: 'label.name' }, + project: { label: 'label.project' } + }, + { + id: { label: 'label.id' }, + projectid: { label: 'label.project.id' }, + state: { label: 'label.state' }, + guestnetworkid: { label: 'label.network.id' }, + publicip: { label: 'label.public.ip' }, + guestipaddress: { label: 'label.guest.ip' }, + linklocalip: { label: 'label.linklocal.ip' }, + hostname: { label: 'label.host' }, + serviceofferingname: { label: 'label.compute.offering' }, + networkdomain: { label: 'label.network.domain' }, + domain: { label: 'label.domain' }, + account: { label: 'label.account' }, + created: { label: 'label.created', converter: cloudStack.converters.toLocalDate }, + isredundantrouter: { + label: 'label.redundant.router', + converter: cloudStack.converters.toBooleanText + }, + redundantRouterState: { label: 'label.redundant.state' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL("listInternalLoadBalancerVMs&id=" + args.context.internallbinstances[0].id), + dataType: 'json', + async: true, + success: function(json) { + var jsonObj = json.listinternallbvmssresponse.internalloadbalancervm[0]; + addExtraPropertiesToRouterInstanceObject(jsonObj); + args.response.success({ + actionFilter: internallbinstanceActionfilter, + data: jsonObj + }); + } + }); + } + }, + nics: { + title: 'label.nics', + multiple: true, + fields: [ + { + name: { label: 'label.name', header: true }, + type: { label: 'label.type' }, + traffictype: { label: 'label.traffic.type' }, + networkname: { label: 'label.network.name' }, + netmask: { label: 'label.netmask' }, + ipaddress: { label: 'label.ip.address' }, + id: { label: 'label.id' }, + networkid: { label: 'label.network.id' }, + isolationuri: { label: 'label.isolation.uri' }, + broadcasturi: { label: 'label.broadcast.uri' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL("listInternalLoadBalancerVMs&id=" + args.context.internallbinstances[0].id), + dataType: 'json', + async: true, + success: function(json) { + var jsonObj = json.listinternallbvmssresponse.internalloadbalancervm[0].nic; + + args.response.success({ + actionFilter: internallbinstanceActionfilter, + data: $.map(jsonObj, function(nic, index) { + var name = 'NIC ' + (index + 1); + if (nic.isdefault) { + name += ' (' + _l('label.default') + ')'; + } + return $.extend(nic, { + name: name + }); + }) + }); + } + }); + } + } + } + } + } + } + }, + actions: { + enable: { + label: 'label.enable.provider', + action: function(args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["InternalLbVm"].id + "&state=Enabled"), + dataType: "json", + success: function(json) { + var jid = json.updatenetworkserviceproviderresponse.jobid; + args.response.success( + {_custom: + { + jobId: jid, + getUpdatedItem: function(json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + } + ); + } + }); + }, + messages: { + confirm: function(args) { + return 'message.confirm.enable.provider'; + }, + notification: function() { + return 'label.enable.provider'; + } + }, + notification: { poll: pollAsyncJobResult } + }, + disable: { + label: 'label.disable.provider', + action: function(args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["InternalLbVm"].id + "&state=Disabled"), + dataType: "json", + success: function(json) { + var jid = json.updatenetworkserviceproviderresponse.jobid; + args.response.success( + {_custom: + { + jobId: jid, + getUpdatedItem: function(json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + } + ); + } + }); + }, + messages: { + confirm: function(args) { + return 'message.confirm.disable.provider'; + }, + notification: function() { + return 'label.disable.provider'; + } + }, + notification: { poll: pollAsyncJobResult } + } + } + }, + vpcVirtualRouter: { id: 'vpcVirtualRouterProviders', label: 'VPC Virtual Router', @@ -12020,7 +12376,20 @@ } return allowedActions; } + + var internallbinstanceActionfilter = function(args) { + var jsonObj = args.context.item; + var allowedActions = []; + if (jsonObj.state == 'Running') { + allowedActions.push("stop"); + } + else if (jsonObj.state == 'Stopped') { + allowedActions.push("start"); + } + return allowedActions; + } + var systemvmActionfilter = function(args) { var jsonObj = args.context.item; var allowedActions = []; @@ -12102,6 +12471,9 @@ case "VirtualRouter": nspMap["virtualRouter"] = items[i]; break; + case "InternalLbVm": + nspMap["InternalLbVm"] = items[i]; + break; case "VpcVirtualRouter": nspMap["vpcVirtualRouter"] = items[i]; break; @@ -12178,7 +12550,16 @@ state: nspMap.midoNet? nspMap.midoNet.state : 'Disabled' } ); - nspHardcodingArray.push( + + nspHardcodingArray.push( + { + id: 'InternalLbVm', + name: 'InternalLbVm', + state: nspMap.InternalLbVm ? nspMap.InternalLbVm.state : 'Disabled' + } + ); + + nspHardcodingArray.push( { id: 'vpcVirtualRouter', name: 'VPC Virtual Router', From 8acdd6f4369c334727ac44b519015cd9bd4d25e2 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 22 May 2013 11:03:56 -0700 Subject: [PATCH 068/412] CLOUDSTACK-747: internal LB in VPC - remove module internalLbProvider since internalLbVm section has been added in system.js --- .../internalLbProvider/internalLbProvider.js | 182 ------------------ ui/modules/modules.js | 3 +- 2 files changed, 1 insertion(+), 184 deletions(-) delete mode 100644 ui/modules/internalLbProvider/internalLbProvider.js diff --git a/ui/modules/internalLbProvider/internalLbProvider.js b/ui/modules/internalLbProvider/internalLbProvider.js deleted file mode 100644 index aaa386ee246..00000000000 --- a/ui/modules/internalLbProvider/internalLbProvider.js +++ /dev/null @@ -1,182 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with 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. -(function($, cloudStack) { - cloudStack.modules.internalLbProvider = function(module) { - var internalLbDeviceViewAll = [ - { - label: 'Devices', - path: '_zone.internalLbDevices' - } - ]; - - var internalLbListView = { - id: 'internalLbDevices', - fields: { - resourcename: { label: 'Resource Name' }, - provider: { label: 'Provider' } - }, - dataProvider: function(args) { - args.response.success({ data: [] }); - }, - actions: { - add: { - label: 'Add internal LB device', - - messages: { - notification: function(args) { - return 'Add internal LB device'; - } - }, - - createForm: { - title: 'Add internal LB device', - fields: { - hostname: { - label: 'label.host', - validation: { required: true } - }, - username: { - label: 'label.username', - validation: { required: true } - }, - password: { - label: 'label.password', - isPassword: true, - validation: { required: true } - } - } - }, - - action: function(args) { - args.response.success(); - }, - - notification: { - poll: function(args) { - args.complete(); - } - } - } - }, - - detailView: { - name: 'Internal LB resource details', - actions: { - remove: { - label: 'delete Internal LB resource', - messages: { - confirm: function(args) { - return 'Please confirm you want to delete Internal LB resource'; - }, - notification: function(args) { - return 'delete Internal LB resource'; - } - }, - action: function(args) { - args.response.success(); - }, - notification: { - poll: function(args) { - args.complete(); - } - } - } - }, - - tabs: { - details: { - title: 'label.details', - fields: [ - { - resourcename: { label: 'Resource Name' } - }, - { - resourceid: { label: 'Resource ID'}, - provider: { label: 'Provider' }, - RESOURCE_NAME: { label: 'Resource Name'} - } - ], - dataProvider: function(args) { - args.response.success({ data: args.context.internalLbDevices[0] }); - } - } - } - } - }; - - var internalLbProviderDetailView = { - id: 'internalLbProvider', - label: 'internal LB', - viewAll: internalLbDeviceViewAll, - tabs: { - details: { - title: 'label.details', - fields: [ - { - name: { label: 'label.name' } - }, - { - state: { label: 'label.state' }, - id: { label: 'label.id' }, - servicelist: { - label: 'Services', - converter: function(args){ - if(args) - return args.join(', '); - else - return ''; - } - } - } - ], - dataProvider: function(args) { - $.ajax({ - url: createURL('listNetworkServiceProviders'), - data: { - name: 'InternalLb', - physicalnetworkid: args.context.physicalNetworks[0].id - }, - success: function(json){ - var items = json.listnetworkserviceprovidersresponse.networkserviceprovider; - if(items != null && items.length > 0) { - args.response.success({ data: items[0] }); - } - else { - args.response.success({ - data: { - name: 'InternalLb', - state: 'Disabled' - } - }) - } - } - }); - } - } - } - }; - - module.infrastructure.networkServiceProvider({ - id: 'internalLb', - name: 'Internal LB', - //state: 'Disabled', //don't know state until log in and visit Infrastructure menu > zone detail > physical network > network service providers - listView: internalLbListView, - - detailView: internalLbProviderDetailView - }); - }; -}(jQuery, cloudStack)); diff --git a/ui/modules/modules.js b/ui/modules/modules.js index 1ff69ef1f15..1e4cd45c833 100644 --- a/ui/modules/modules.js +++ b/ui/modules/modules.js @@ -19,7 +19,6 @@ 'vpc', 'infrastructure', 'vnmcNetworkProvider', - 'vnmcAsa1000v', - 'internalLbProvider' + 'vnmcAsa1000v' ]; }(jQuery, cloudStack)); From b11c44f91d418d498c994f7fbf2b4791fe89dcab Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 11:11:29 -0700 Subject: [PATCH 069/412] Internal LB: Pre-select existing VMs on LB rule --- ui/scripts/vpc.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index a3dadaace73..be7fdd72cbc 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -444,7 +444,34 @@ }, listView: $.extend(true, {}, cloudStack.sections.instances.listView, { type: 'checkbox', - filters: false + filters: false, + dataProvider: function(args) { + $.ajax({ + url: createURL('listVirtualMachines'), + data: { + networkid: args.context.networks[0].id, + listAll: true + }, + success: function(json) { + var instances = json.listvirtualmachinesresponse.virtualmachine; + + // Pre-select existing instances in LB rule + $(instances).map(function(index, instance) { + instance._isSelected = $.grep( + args.context.internalLoadBalancers[0].loadbalancerinstance, + + function(lbInstance) { + return lbInstance.id == instance.id; + } + ).length ? true : false; + }); + + args.response.success({ + data: instances + }); + } + }); + } }), action: function(args) { var vms = args.context.instances; From a0e75c12cd62541f750e9ff556d7b570483dbf72 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 22 May 2013 11:18:26 -0700 Subject: [PATCH 070/412] CLOUDSTACK-747: internal LB in VPC - fix a bug that Source IP Address column was not filled after Add Internal LB action was complete. --- ui/scripts/vpc.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index be7fdd72cbc..f3347358675 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -410,8 +410,8 @@ args.response.success( {_custom: {jobId: jid, - getUpdatedItem: function(json) { - return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.loadbalancer; } } } @@ -422,14 +422,7 @@ args.response.success(); }, notification: { - poll: function(args) { - args.complete({ - data: { - ipaddress: '10.0.3.2', - type: 'Internal' - } - }); - } + poll: pollAsyncJobResult } } }, From f2dd65c39771ed2ba7a1248351a8d8bd3fb4af03 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 12:01:22 -0700 Subject: [PATCH 071/412] Add dashboard counts for private gateways, ip addresses and vpns --- ui/scripts/vpc.js | 59 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index f3347358675..a9e839c8efc 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3106,16 +3106,16 @@ async: true, success: function(json) { var networks = json.listnetworksresponse.network; - var loadBalancers, networkACLLists; + var loadBalancers, networkACLLists, publicIpAddresses, privateGateways, vpnGateways; var error = false; // Get load balancers $.ajax({ url: createURL('listLoadBalancers'), - data: { details: 'min', vpcid: args.context.vpc[0].id }, + data: { vpcid: args.context.vpc[0].id }, success: function(json) { loadBalancers = json.listloadbalancerssresponse.loadbalancer ? - json.listloadbalancerssresponse.loadbalancer : [] + json.listloadbalancerssresponse.loadbalancer : []; }, error: function(json) { error = true; @@ -3125,18 +3125,57 @@ // Get network ACL lists $.ajax({ url: createURL('listNetworkACLLists'), - data: { details: 'min', 'vpc_id': args.context.vpc[0].id }, + data: { 'vpc_id': args.context.vpc[0].id }, success: function(json) { networkACLLists = json.listnetworkacllistsresponse.networkacllist ? - json.listnetworkacllistsresponse.networkacllist : [] + json.listnetworkacllistsresponse.networkacllist : []; }, error: function(json) { error = true; } - }); + }); + + // Get public IPs + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: { 'vpcid': args.context.vpc[0].id }, + success: function(json) { + publicIpAddresses = json.listpublicipaddressesresponse.publicipaddress ? + json.listpublicipaddressesresponse.publicipaddress : []; + }, + error: function(json) { + error = true; + } + }); + + // Get private gateways + $.ajax({ + url: createURL('listPrivateGateways'), + data: { 'vpcid': args.context.vpc[0].id }, + success: function(json) { + privateGateways = json.listprivategatewaysresponse.privategateway ? + json.listprivategatewaysresponse.privategateway : []; + }, + error: function(json) { + error = true; + } + }); + + // Get VPN gateways + $.ajax({ + url: createURL('listVpnGateways'), + data: { 'vpcid': args.context.vpc[0].id }, + success: function(json) { + vpnGateways = json.listvpngatewaysresponse.vpngateway ? + json.listvpngatewaysresponse.vpngateway : []; + }, + error: function(json) { + error = true; + } + }); var dataTimer = setInterval(function() { - var complete = loadBalancers && networkACLLists; + var complete = loadBalancers && networkACLLists && publicIpAddresses && privateGateways && vpnGateways; if (complete) { clearInterval(dataTimer); @@ -3162,17 +3201,17 @@ { id: 'privateGateways', name: 'Private gateways', - total: 0 + total: privateGateways.length }, { id: 'publicIPs', name: 'Public IP addresses', - total: 0 + total: publicIpAddresses.length }, { id: 'siteToSiteVPNs', name: 'Site-to-site VPNs', - total: 0 + total: vpnGateways.length }, { id: 'networkACLLists', From 763f6fd47b8179fd173f035d55f4b6609a9d2963 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 12:15:45 -0700 Subject: [PATCH 072/412] Add dashboard counts for static NAT IPs --- ui/scripts/vpc.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index a9e839c8efc..eeb69503f71 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3239,7 +3239,9 @@ { id: 'tierStaticNATs', name: 'Static NATs', - total: 0 + total: $.grep(publicIpAddresses, function(ip) { + return ip.associatednetworkid == tier.id && ip.isstaticnat; + }).length }, { id: 'tierVMs', From 9e0733c83e2204f6ca313cfa210a7ffd008524eb Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 12:28:02 -0700 Subject: [PATCH 073/412] Add dashboard count for IPs with port forwarding --- ui/scripts/vpc.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index eeb69503f71..fe3ce13e5f1 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3106,7 +3106,7 @@ async: true, success: function(json) { var networks = json.listnetworksresponse.network; - var loadBalancers, networkACLLists, publicIpAddresses, privateGateways, vpnGateways; + var loadBalancers, networkACLLists, publicIpAddresses, privateGateways, vpnGateways, portForwardingRules; var error = false; // Get load balancers @@ -3148,6 +3148,19 @@ } }); + // Get port forwarding rules + $.ajax({ + url: createURL('listPortForwardingRules'), + data: { 'vpcid': args.context.vpc[0].id }, + success: function(json) { + portForwardingRules = json.listportforwardingrulesresponse.portforwardingrule ? + json.listportforwardingrulesresponse.portforwardingrule : []; + }, + error: function(json) { + error = true; + } + }); + // Get private gateways $.ajax({ url: createURL('listPrivateGateways'), @@ -3234,7 +3247,14 @@ { id: 'tierPortForwarders', name: 'Port forwarders', - total: 0 + total: $.grep(publicIpAddresses, function(ip) { + return $.grep( + portForwardingRules, + function(pf) { + return pf.ipaddressid == ip.id; + } + ).length ? true : false; + }).length }, { id: 'tierStaticNATs', From ff58052d2cd5f2ed821bef04c18960ef0d18fac2 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 22 May 2013 13:18:24 -0700 Subject: [PATCH 074/412] CLOUDSTACK-747: internal LB in VPC - internal LB detailView - add rules tab, assignedVMs tab. --- ui/scripts/vpc.js | 49 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index fe3ce13e5f1..5c2f0266d1b 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -482,8 +482,7 @@ }, dataType: 'json', async: true, - success: function(data) { - debugger; + success: function(data) { var jid = data.assigntoloadbalancerruleresponse.jobid; args.response.success({ _custom: { jobId: jid } @@ -519,7 +518,51 @@ } }); } - } + }, + rules: { + title: 'label.rules', + multiple: true, + fields: [ + { + sourceport: { label: 'Source Port' }, + instanceport: { label: 'Instance Port' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + id: args.context.internalLoadBalancers[0].id + }, + success: function(json) { + var item = json.listloadbalancerssresponse.loadbalancer[0]; + args.response.success({ data: item.loadbalancerrule }); + } + }); + } + } , + assignedVms: { + title: 'Assigned VMs', + multiple: true, + fields: [ + { + name: { label: 'label.name' }, + ipaddress: { label: 'label.ip.address' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + id: args.context.internalLoadBalancers[0].id + }, + success: function(json) { + var item = json.listloadbalancerssresponse.loadbalancer[0]; + args.response.success({ data: item.loadbalancerinstance }); + } + }); + } + } } } } From e6863c612bfeda957c4e2acf14647c029a48d2c8 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 22 May 2013 15:39:22 -0700 Subject: [PATCH 075/412] Integration test for advanced zone with security groups Description: This patch includes three parts for advancedsg: (1) Marvin support. (2) devcloud support. (3) integration test scripts Testing Done: devcloud testing ok. Signed-off-by: Anthony Xu --- setup/dev/advancedsg.cfg | 185 +++++ .../component/test_advancedsg_networks.py | 753 ++++++++++++++++++ .../component/test_egress_rules.py | 20 +- .../component/test_security_groups.py | 22 +- tools/devcloud/devcloud-advancedsg.cfg | 119 +++ tools/marvin/marvin/configGenerator.py | 123 +++ tools/marvin/marvin/deployDataCenter.py | 32 +- .../marvin/sandbox/advancedsg/__init__.py | 18 + .../sandbox/advancedsg/advancedsg_env.py | 150 ++++ .../sandbox/advancedsg/setup.properties | 61 ++ tools/marvin/setup.py | 2 +- 11 files changed, 1461 insertions(+), 24 deletions(-) create mode 100644 setup/dev/advancedsg.cfg create mode 100644 test/integration/component/test_advancedsg_networks.py create mode 100644 tools/devcloud/devcloud-advancedsg.cfg create mode 100644 tools/marvin/marvin/sandbox/advancedsg/__init__.py create mode 100644 tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py create mode 100644 tools/marvin/marvin/sandbox/advancedsg/setup.properties diff --git a/setup/dev/advancedsg.cfg b/setup/dev/advancedsg.cfg new file mode 100644 index 00000000000..e6922b639e5 --- /dev/null +++ b/setup/dev/advancedsg.cfg @@ -0,0 +1,185 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. + +{ + "zones": [ + { + "name": "Sandbox-Simulator", + "dns1": "10.147.28.6", + "physical_networks": [ + { + "name": "Sandbox-pnet", + "tags": [ + "cloud-simulator-pnet" + ], + "broadcastdomainrange": "Zone", + "providers": [ + { + "broadcastdomainrange": "ZONE", + "name": "VirtualRouter" + }, + { + "broadcastdomainrange": "ZONE", + "name": "SecurityGroupProvider" + } + ], + "traffictypes": [ + { + "typ": "Guest" + }, + { + "typ": "Management", + "simulator": "cloud-simulator-mgmt" + } + ], + "isolationmethods": [ + "VLAN" + ] + } + ], + "securitygroupenabled": "true", + "ipranges": [ + { + "startip": "10.147.31.150", + "endip": "10.147.31.159", + "netmask": "255.255.255.0", + "vlan": "31", + "gateway": "10.147.31.1" + } + ], + "networktype": "Advanced", + "pods": [ + { + "endip": "10.147.29.159", + "name": "POD0", + "startip": "10.147.29.150", + "netmask": "255.255.255.0", + "clusters": [ + { + "clustername": "C0", + "hypervisor": "Simulator", + "hosts": [ + { + "username": "root", + "url": "http://simulator0", + "password": "password" + } + ], + "clustertype": "CloudManaged", + "primaryStorages": [ + { + "url": "nfs://10.147.28.6:/export/home/sandbox/primary", + "name": "PS0" + } + ] + } + ], + "gateway": "10.147.29.1" + } + ], + "internaldns1": "10.147.28.6", + "secondaryStorages": [ + { + "url": "nfs://10.147.28.6:/export/home/sandbox/sstor" + } + ] + } + ], + "dbSvr": { + "dbSvr": "localhost", + "passwd": "cloud", + "db": "cloud", + "port": 3306, + "user": "cloud" + }, + "logger": [ + { + "name": "TestClient", + "file": "testclient.log" + }, + { + "name": "TestCase", + "file": "testcase.log" + } + ], + "globalConfig": [ + { + "name": "storage.cleanup.interval", + "value": "300" + }, + { + "name": "direct.agent.load.size", + "value": "1000" + }, + { + "name": "default.page.size", + "value": "10000" + }, + { + "name": "instance.name", + "value": "QA" + }, + { + "name": "workers", + "value": "10" + }, + { + "name": "vm.op.wait.interval", + "value": "5" + }, + { + "name": "account.cleanup.interval", + "value": "600" + }, + { + "name": "guest.domain.suffix", + "value": "sandbox.simulator" + }, + { + "name": "expunge.delay", + "value": "60" + }, + { + "name": "vm.allocation.algorithm", + "value": "random" + }, + { + "name": "expunge.interval", + "value": "60" + }, + { + "name": "expunge.workers", + "value": "3" + }, + { + "name": "secstorage.allowed.internal.sites", + "value": "10.147.28.0/24" + }, + { + "name": "check.pod.cidrs", + "value": "true" + } + ], + "mgtSvr": [ + { + "mgtSvrIp": "localhost", + "passwd": "password", + "user": "root", + "port": 8096 + } + ] +} diff --git a/test/integration/component/test_advancedsg_networks.py b/test/integration/component/test_advancedsg_networks.py new file mode 100644 index 00000000000..e24254d4b90 --- /dev/null +++ b/test/integration/component/test_advancedsg_networks.py @@ -0,0 +1,753 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. + +""" P1 tests for networks in advanced zone with security groups +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime +import netaddr + +class Services: + """ Test networks in advanced zone with security groups""" + + def __init__(self): + self.services = { + "domain": { + "name": "DOM", + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "account": { + "email": "admin-XABU1@test.com", + "firstname": "admin-XABU1", + "lastname": "admin-XABU1", + "username": "admin-XABU1", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "shared_network_offering_sg": { + "name": 'MySharedOffering-sg', + "displaytext": 'MySharedOffering-sg', + "guestiptype": 'Shared', + "supportedservices": 'Dhcp,Dns,UserData,SecurityGroup', + "specifyVlan" : "False", + "specifyIpRanges" : "False", + "traffictype": 'GUEST', + "serviceProviderList" : { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "UserData": 'VirtualRouter', + "SecurityGroup": 'SecurityGroupProvider' + }, + }, + "shared_network_offering": { + "name": 'MySharedOffering', + "displaytext": 'MySharedOffering', + "guestiptype": 'Shared', + "supportedservices": 'Dhcp,Dns,UserData', + "specifyVlan" : "False", + "specifyIpRanges" : "False", + "traffictype": 'GUEST', + "serviceProviderList" : { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "UserData": 'VirtualRouter' + }, + }, + "shared_network_sg": { + "name": "MyIsolatedNetwork - Test", + "displaytext": "MyIsolatedNetwork", + "networkofferingid":"1", + "vlan" :1200, + "gateway" :"172.16.15.1", + "netmask" :"255.255.255.0", + "startip" :"172.16.15.2", + "endip" :"172.16.15.20", + "acltype" : "Domain", + "scope":"all", + }, + "shared_network": { + "name": "MySharedNetwork - Test", + "displaytext": "MySharedNetwork", + "vlan" :1201, + "gateway" :"172.16.15.1", + "netmask" :"255.255.255.0", + "startip" :"172.16.15.21", + "endip" :"172.16.15.41", + "acltype" : "Domain", + "scope":"all", + }, + "isolated_network_offering": { + "name": 'Network offering-DA services', + "displaytext": 'Network offering-DA services', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList": { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + "Vpn": 'VirtualRouter', + "Firewall": 'VirtualRouter', + "Lb": 'VirtualRouter', + "UserData": 'VirtualRouter', + "StaticNat": 'VirtualRouter', + }, + }, + "isolated_network": { + "name": "Isolated Network", + "displaytext": "Isolated Network", + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.3 (64-bit)', + # Cent OS 5.3 (64 bit) + "sleep": 90, + "timeout": 10, + "mode": 'advanced', + "securitygroupenabled": 'true' + } + +class TestNetworksInAdvancedSG(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestSharedNetworks, + cls + ).getClsTestClient().getApiClient() + + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.api_client = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + self.cleanup_networks = [] + self.cleanup_accounts = [] + self.cleanup_domains = [] + self.cleanup_projects = [] + self.cleanup_vms = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created network offerings + cleanup_resources(self.api_client, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + #below components is not a part of cleanup because to mandate the order and to cleanup network + try: + for vm in self.cleanup_vms: + vm.delete(self.api_client) + except Exception as e: + raise Exception("Warning: Exception during virtual machines cleanup : %s" % e) + + try: + for project in self.cleanup_projects: + project.delete(self.api_client) + except Exception as e: + raise Exception("Warning: Exception during project cleanup : %s" % e) + + try: + for account in self.cleanup_accounts: + account.delete(self.api_client) + except Exception as e: + raise Exception("Warning: Exception during account cleanup : %s" % e) + + try: + for domain in self.cleanup_domains: + domain.delete(self.api_client) + except Exception as e: + raise Exception("Warning: Exception during domain cleanup : %s" % e) + + #Wait till all resources created are cleaned up completely and then attempt to delete Network + time.sleep(self.services["sleep"]) + + try: + for network in self.cleanup_networks: + network.delete(self.api_client) + except Exception as e: + raise Exception("Warning: Exception during network cleanup : %s" % e) + return + + def test_createIsolatedNetwork(self): + """ Test Isolated Network """ + + # Steps, + # 1. create an Admin Account - admin-XABU1 + # 2. listPhysicalNetworks in available zone + # 3. createNetworkOffering: + # 4. Enable network offering - updateNetworkOffering - state=Enabled + # 5. createNetwork + # Validations, + # 1. listAccounts name=admin-XABU1, state=enabled returns your account + # 2. listPhysicalNetworks should return at least one active physical network + # 4. listNetworkOfferings - name=myisolatedoffering, should list enabled offering + # 5. network creation should FAIL since isolated network is not supported in advanced zone with security groups. + + #Create admin account + self.admin_account = Account.create( + self.api_client, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + self.cleanup_accounts.append(self.admin_account) + + #verify that the account got created with state enabled + list_accounts_response = Account.list( + self.api_client, + id=self.admin_account.account.id, + listall=True + ) + self.assertEqual( + isinstance(list_accounts_response, list), + True, + "listAccounts returned invalid object in response." + ) + self.assertNotEqual( + len(list_accounts_response), + 0, + "listAccounts returned empty list." + ) + self.assertEqual( + list_accounts_response[0].state, + "enabled", + "The admin account created is not enabled." + ) + + self.debug("Admin type account created: %s" % self.admin_account.name) + + #Create an user account + self.user_account = Account.create( + self.api_client, + self.services["account"], + admin=False, + domainid=self.domain.id + ) + + self.cleanup_accounts.append(self.user_account) + + #verify that the account got created with state enabled + list_accounts_response = Account.list( + self.api_client, + id=self.user_account.account.id, + listall=True + ) + self.assertEqual( + isinstance(list_accounts_response, list), + True, + "listAccounts returned invalid object in response." + ) + self.assertNotEqual( + len(list_accounts_response), + 0, + "listAccounts returned empty list." + ) + self.assertEqual( + list_accounts_response[0].state, + "enabled", + "The user account created is not enabled." + ) + + self.debug("User type account created: %s" % self.user_account.name) + + #Verify that there should be at least one physical network present in zone. + list_physical_networks_response = PhysicalNetwork.list( + self.api_client, + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_physical_networks_response, list), + True, + "listPhysicalNetworks returned invalid object in response." + ) + self.assertNotEqual( + len(list_physical_networks_response), + 0, + "listPhysicalNetworks should return at least one physical network." + ) + + physical_network = list_physical_networks_response[0] + + self.debug("Physical network found: %s" % physical_network.id) + + #Create Network Offering + self.isolated_network_offering = NetworkOffering.create( + self.api_client, + self.services["isolated_network_offering"], + conservemode=False + ) + + self.cleanup.append(self.isolated_network_offering) + + #Verify that the network offering got created + list_network_offerings_response = NetworkOffering.list( + self.api_client, + id=self.isolated_network_offering.id + ) + self.assertEqual( + isinstance(list_network_offerings_response, list), + True, + "listNetworkOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_network_offerings_response), + 0, + "listNetworkOfferings returned empty list." + ) + self.assertEqual( + list_network_offerings_response[0].state, + "Disabled", + "The network offering created should be bydefault disabled." + ) + + self.debug("Isolated Network offering created: %s" % self.isolated_network_offering.id) + + #Update network offering state from disabled to enabled. + network_offering_update_response = NetworkOffering.update( + self.isolated_network_offering, + self.api_client, + id=self.isolated_network_offering.id, + state="enabled" + ) + + #Verify that the state of the network offering is updated + list_network_offerings_response = NetworkOffering.list( + self.api_client, + id=self.isolated_network_offering.id + ) + self.assertEqual( + isinstance(list_network_offerings_response, list), + True, + "listNetworkOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_network_offerings_response), + 0, + "listNetworkOfferings returned empty list." + ) + self.assertEqual( + list_network_offerings_response[0].state, + "Enabled", + "The network offering state should get updated to Enabled." + ) + + #create network using the isolated network offering created + try: + self.isolated_network = Network.create( + self.api_client, + self.services["isolated_network"], + networkofferingid=self.isolated_network_offering.id, + zoneid=self.zone.id, + ) + self.cleanup_networks.append(self.isolated_network) + self.fail("Create isolated network is invalid in advanced zone with security groups.") + except Exception as e: + self.debug("Network creation failed because create isolated network is invalid in advanced zone with security groups.") + + def test_createSharedNetwork_withoutSG(self): + """ Test Shared Network with used vlan 01 """ + + # Steps, + # 1. create an Admin account + # 2. create a shared NetworkOffering + # 3. enable the network offering + # 4. listPhysicalNetworks + # 5. createNetwork + # Validations, + # 1. listAccounts state=enabled returns your account + # 2. listNetworkOfferings - name=mysharedoffering , should list offering in disabled state + # 3. listNetworkOfferings - name=mysharedoffering, should list enabled offering + # 4. listPhysicalNetworks should return at least one active physical network + # 5. network creation should FAIL since there is no SecurityProvide in the network offering + + #Create admin account + self.admin_account = Account.create( + self.api_client, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + self.cleanup_accounts.append(self.admin_account) + + #verify that the account got created with state enabled + list_accounts_response = Account.list( + self.api_client, + id=self.admin_account.account.id, + listall=True + ) + self.assertEqual( + isinstance(list_accounts_response, list), + True, + "listAccounts returned invalid object in response." + ) + self.assertNotEqual( + len(list_accounts_response), + 0, + "listAccounts returned empty list." + ) + self.assertEqual( + list_accounts_response[0].state, + "enabled", + "The admin account created is not enabled." + ) + + self.debug("Domain admin account created: %s" % self.admin_account.account.id) + + #Verify that there should be at least one physical network present in zone. + list_physical_networks_response = PhysicalNetwork.list( + self.api_client, + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_physical_networks_response, list), + True, + "listPhysicalNetworks returned invalid object in response." + ) + self.assertNotEqual( + len(list_physical_networks_response), + 0, + "listPhysicalNetworks should return at least one physical network." + ) + + physical_network = list_physical_networks_response[0] + + self.debug("Physical Network found: %s" % physical_network.id) + + self.services["shared_network_offering"]["specifyVlan"] = "True" + self.services["shared_network_offering"]["specifyIpRanges"] = "True" + + #Create Network Offering + self.shared_network_offering = NetworkOffering.create( + self.api_client, + self.services["shared_network_offering"], + conservemode=False + ) + + self.cleanup.append(self.shared_network_offering) + + #Verify that the network offering got created + list_network_offerings_response = NetworkOffering.list( + self.api_client, + id=self.shared_network_offering.id + ) + self.assertEqual( + isinstance(list_network_offerings_response, list), + True, + "listNetworkOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_network_offerings_response), + 0, + "listNetworkOfferings returned empty list." + ) + self.assertEqual( + list_network_offerings_response[0].state, + "Disabled", + "The network offering created should be bydefault disabled." + ) + + self.debug("Shared Network Offering created: %s" % self.shared_network_offering.id) + + #Update network offering state from disabled to enabled. + network_offering_update_response = NetworkOffering.update( + self.shared_network_offering, + self.api_client, + id=self.shared_network_offering.id, + state="enabled" + ) + + #Verify that the state of the network offering is updated + list_network_offerings_response = NetworkOffering.list( + self.api_client, + id=self.shared_network_offering.id + ) + self.assertEqual( + isinstance(list_network_offerings_response, list), + True, + "listNetworkOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_network_offerings_response), + 0, + "listNetworkOfferings returned empty list." + ) + self.assertEqual( + list_network_offerings_response[0].state, + "Enabled", + "The network offering state should get updated to Enabled." + ) + + #create network using the shared network offering created + self.services["shared_network"]["acltype"] = "domain" + self.services["shared_network"]["networkofferingid"] = self.shared_network_offering.id + self.services["shared_network"]["physicalnetworkid"] = physical_network.id + + try: + self.shared_network = Network.create( + self.api_client, + self.services["shared_network"], + networkofferingid=self.shared_network_offering.id, + zoneid=self.zone.id + ) + self.cleanup_networks.append(self.shared_network) + self.fail("Network created without SecurityProvider , which is invalid") + except Exception as e: + self.debug("Network creation failed because there is no SecurityProvider in the network offering.") + + def test_deployVM_SharedwithSG(self): + """ Test VM deployment in shared networks with SecurityProvider """ + + # Steps, + # 0. create a user account + # 1. Create one shared Network (scope=ALL, different IP ranges) + # 2. deployVirtualMachine in the above networkid within the user account + # 3. delete the user account + # Validations, + # 1. shared network should be created successfully + # 2. VM should deploy successfully + + #Create admin account + self.admin_account = Account.create( + self.api_client, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + self.cleanup_accounts.append(self.admin_account) + + #verify that the account got created with state enabled + list_accounts_response = Account.list( + self.api_client, + id=self.admin_account.account.id, + liistall=True + ) + self.assertEqual( + isinstance(list_accounts_response, list), + True, + "listAccounts returned invalid object in response." + ) + self.assertNotEqual( + len(list_accounts_response), + 0, + "listAccounts returned empty list." + ) + self.assertEqual( + list_accounts_response[0].state, + "enabled", + "The admin account created is not enabled." + ) + + self.debug("Admin type account created: %s" % self.admin_account.name) + + self.services["shared_network_offering_sg"]["specifyVlan"] = "True" + self.services["shared_network_offering_sg"]["specifyIpRanges"] = "True" + + #Create Network Offering + self.shared_network_offering_sg = NetworkOffering.create( + self.api_client, + self.services["shared_network_offering_sg"], + conservemode=False + ) + + self.cleanup.append(self.shared_network_offering_sg) + + #Verify that the network offering got created + list_network_offerings_response = NetworkOffering.list( + self.api_client, + id=self.shared_network_offering_sg.id + ) + self.assertEqual( + isinstance(list_network_offerings_response, list), + True, + "listNetworkOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_network_offerings_response), + 0, + "listNetworkOfferings returned empty list." + ) + self.assertEqual( + list_network_offerings_response[0].state, + "Disabled", + "The network offering created should be bydefault disabled." + ) + + self.debug("Shared Network offering created: %s" % self.shared_network_offering_sg.id) + + #Update network offering state from disabled to enabled. + network_offering_update_response = NetworkOffering.update( + self.shared_network_offering_sg, + self.api_client, + id=self.shared_network_offering_sg.id, + state="enabled" + ) + + #Verify that the state of the network offering is updated + list_network_offerings_response = NetworkOffering.list( + self.api_client, + id=self.shared_network_offering_sg.id + ) + self.assertEqual( + isinstance(list_network_offerings_response, list), + True, + "listNetworkOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_network_offerings_response), + 0, + "listNetworkOfferings returned empty list." + ) + self.assertEqual( + list_network_offerings_response[0].state, + "Enabled", + "The network offering state should get updated to Enabled." + ) + + physical_network = list_physical_networks_response[0] + + #create network using the shared network offering created + self.services["shared_network_sg"]["acltype"] = "domain" + self.services["shared_network_sg"]["networkofferingid"] = self.shared_network_offering_sg.id + self.services["shared_network_sg"]["physicalnetworkid"] = physical_network.id + self.shared_network_sg = Network.create( + self.api_client, + self.services["shared_network_sg"], + domainid=self.admin_account.account.domainid, + networkofferingid=self.shared_network_offering_sg.id, + zoneid=self.zone.id + ) + + self.cleanup_networks.append(self.shared_network_sg) + + list_networks_response = Network.list( + self.api_client, + id=self.shared_network_sg.id + ) + self.assertEqual( + isinstance(list_networks_response, list), + True, + "listNetworks returned invalid object in response." + ) + self.assertNotEqual( + len(list_networks_response), + 0, + "listNetworks returned empty list." + ) + self.assertEqual( + list_networks_response[0].specifyipranges, + True, + "The network is created with ip range but the flag is set to False." + ) + + self.debug("Shared Network created: %s" % self.shared_network_sg.id) + + self.shared_network_admin_account_virtual_machine = VirtualMachine.create( + self.api_client, + self.services["virtual_machine"], + accountid=self.admin_account.name, + domainid=self.admin_account.account.domainid, + networkids=self.shared_network_sg.id, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.api_client, + id=self.shared_network_admin_account_virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "listVirtualMachines returned invalid object in response." + ) + self.assertNotEqual( + len(vms), + 0, + "listVirtualMachines returned empty list." + ) + self.debug("Virtual Machine created: %s" % self.shared_network_admin_account_virtual_machine.id) + + ip_range = list(netaddr.iter_iprange(unicode(self.services["shared_network_sg"]["startip"]), unicode(self.services["shared_network_sg"]["endip"]))) + if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: + self.fail("Virtual machine ip should be from the ip range assigned to network created.") + diff --git a/test/integration/component/test_egress_rules.py b/test/integration/component/test_egress_rules.py index 872ca2c7b5d..607bac86325 100644 --- a/test/integration/component/test_egress_rules.py +++ b/test/integration/component/test_egress_rules.py @@ -194,7 +194,7 @@ class TestDefaultSecurityGroupEgress(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_deployVM_InDefaultSecurityGroup(self): """Test deploy VM in default security group with no egress rules """ @@ -351,7 +351,7 @@ class TestAuthorizeIngressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_authorizeIngressRule(self): """Test authorize ingress rule """ @@ -509,7 +509,7 @@ class TestDefaultGroupEgress(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_default_group_with_egress(self): """Test default group with egress rule before VM deploy and ping, ssh """ @@ -710,7 +710,7 @@ class TestDefaultGroupEgressAfterDeploy(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_default_group_with_egress(self): """ Test default group with egress rule added after vm deploy and ping, ssh test @@ -893,7 +893,7 @@ class TestRevokeEgressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_revoke_egress_rule(self): """Test revoke security group egress rule """ @@ -1155,7 +1155,7 @@ class TestInvalidAccountAuthroize(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_invalid_account_authroize(self): """Test invalid account authroize """ @@ -1283,7 +1283,7 @@ class TestMultipleAccountsEgressRuleNeg(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_multiple_account_egress_rule_negative(self): """Test multiple account egress rules negative case """ @@ -1531,7 +1531,7 @@ class TestMultipleAccountsEgressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_multiple_account_egress_rule_positive(self): """Test multiple account egress rules positive case """ @@ -1822,7 +1822,7 @@ class TestStartStopVMWithEgressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_start_stop_vm_egress(self): """ Test stop start Vm with egress rules """ @@ -2034,7 +2034,7 @@ class TestInvalidParametersForEgress(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_invalid_parameters(self): """ Test invalid parameters for egress rules """ diff --git a/test/integration/component/test_security_groups.py b/test/integration/component/test_security_groups.py index 2ed27fe0c5d..3c25e25a34f 100644 --- a/test/integration/component/test_security_groups.py +++ b/test/integration/component/test_security_groups.py @@ -164,7 +164,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_deployVM_InDefaultSecurityGroup(self): """Test deploy VM in default security group """ @@ -243,7 +243,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): ) return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_02_listSecurityGroups(self): """Test list security groups for admin account """ @@ -278,7 +278,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): ) return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_03_accessInDefaultSecurityGroup(self): """Test access in default security group """ @@ -435,7 +435,7 @@ class TestAuthorizeIngressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_authorizeIngressRule(self): """Test authorize ingress rule """ @@ -571,7 +571,7 @@ class TestRevokeIngressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_revokeIngressRule(self): """Test revoke ingress rule """ @@ -868,7 +868,7 @@ class TestdeployVMWithUserData(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_deployVMWithUserData(self): """Test Deploy VM with User data""" @@ -1044,7 +1044,7 @@ class TestDeleteSecurityGroup(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_delete_security_grp_running_vm(self): """Test delete security group with running VM""" @@ -1128,7 +1128,7 @@ class TestDeleteSecurityGroup(cloudstackTestCase): ) return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_02_delete_security_grp_withoout_running_vm(self): """Test delete security group without running VM""" @@ -1290,7 +1290,7 @@ class TestIngressRule(cloudstackTestCase): return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_01_authorizeIngressRule_AfterDeployVM(self): """Test delete security group with running VM""" @@ -1402,7 +1402,7 @@ class TestIngressRule(cloudstackTestCase): % (ingress_rule_2["id"], e)) return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_02_revokeIngressRule_AfterDeployVM(self): """Test Revoke ingress rule after deploy VM""" @@ -1556,7 +1556,7 @@ class TestIngressRule(cloudstackTestCase): % (icmp_rule["ruleid"], e)) return - @attr(tags = ["sg", "eip"]) + @attr(tags = ["sg", "eip", "advancedsg"]) def test_03_stopStartVM_verifyIngressAccess(self): """Test Start/Stop VM and Verify ingress rule""" diff --git a/tools/devcloud/devcloud-advancedsg.cfg b/tools/devcloud/devcloud-advancedsg.cfg new file mode 100644 index 00000000000..6c26b15f5da --- /dev/null +++ b/tools/devcloud/devcloud-advancedsg.cfg @@ -0,0 +1,119 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. +# +# This configuration is meant for running advanced networking with security groups, with management server on the laptop. +# It requires that the user run a DNS resolver within devcloud via 'apt-get install dnsmasq' + +{ + "zones": [ + { + "localstorageenabled": "true", + "name": "testzone", + "dns1": "8.8.8.8", + "physical_networks": [ + { + "broadcastdomainrange": "Zone", + "name": "shared", + "traffictypes": [ + { + "typ": "Management" + }, + { + "typ": "Guest" + } + ], + "providers": [ + { + "broadcastdomainrange": "ZONE", + "name": "VirtualRouter" + }, + { + "broadcastdomainrange": "ZONE", + "name": "SecurityGroupProvider" + } + ], + "isolationmethods": [ + "VLAN" + ] + } + ], + "securitygroupenabled": "true", + "ipranges": [ + { + "startip": "10.0.3.100", + "endip": "10.0.3.199", + "netmask": "255.255.255.0", + "vlan": "1003", + "gateway": "10.0.3.2" + } + ], + "networktype": "Advanced", + "pods": [ + { + "endip": "192.168.56.249", + "name": "testpod", + "startip": "192.168.56.200", + "netmask": "255.255.255.0", + "clusters": [ + { + "clustername": "testcluster", + "hypervisor": "XenServer", + "hosts": [ + { + "username": "root", + "url": "http://192.168.56.10/", + "password": "password" + } + ], + "clustertype": "CloudManaged" + } + ], + "gateway": "192.168.56.1" + } + ], + "internaldns1": "192.168.56.10", + "secondaryStorages": [ + { + "url": "nfs://192.168.56.10/opt/storage/secondary" + } + ] + } + ], + "dbSvr": { + "dbSvr": "localhost", + "passwd": "cloud", + "db": "cloud", + "port": 3306, + "user": "cloud" + }, + "logger": [ + { + "name": "TestClient", + "file": "/var/log/testclient.log" + }, + { + "name": "TestCase", + "file": "/var/log/testcase.log" + } + ], + "mgtSvr": [ + { + "mgtSvrIp": "127.0.0.1", + "port": 8096 + } + ] +} diff --git a/tools/marvin/marvin/configGenerator.py b/tools/marvin/marvin/configGenerator.py index 4e82bbe387d..c970ada850a 100644 --- a/tools/marvin/marvin/configGenerator.py +++ b/tools/marvin/marvin/configGenerator.py @@ -638,6 +638,126 @@ def describe_setup_in_advanced_mode(): return zs +'''sample code to generate setup configuration file''' +def describe_setup_in_advancedsg_mode(): + zs = cloudstackConfiguration() + + for l in range(1): + z = zone() + z.dns1 = "8.8.8.8" + z.dns2 = "4.4.4.4" + z.internaldns1 = "192.168.110.254" + z.internaldns2 = "192.168.110.253" + z.name = "test"+str(l) + z.networktype = 'Advanced' + z.vlan = "100-2000" + z.securitygroupenabled = "true" + + pn = physical_network() + pn.name = "test-network" + pn.traffictypes = [traffictype("Guest"), traffictype("Management")] + + #If security groups are reqd + sgprovider = provider() + sgprovider.broadcastdomainrange = 'ZONE' + sgprovider.name = 'SecurityGroupProvider' + + pn.providers.append(sgprovider) + z.physical_networks.append(pn) + + '''create 10 pods''' + for i in range(2): + p = pod() + p.name = "test" +str(l) + str(i) + p.gateway = "192.168.%d.1"%i + p.netmask = "255.255.255.0" + p.startip = "192.168.%d.200"%i + p.endip = "192.168.%d.220"%i + + '''add 10 clusters''' + for j in range(2): + c = cluster() + c.clustername = "test"+str(l)+str(i) + str(j) + c.clustertype = "CloudManaged" + c.hypervisor = "Simulator" + + '''add 10 hosts''' + for k in range(2): + h = host() + h.username = "root" + h.password = "password" + memory = 8*1024*1024*1024 + localstorage=1*1024*1024*1024*1024 + #h.url = "http://sim/%d%d%d%d/cpucore=1&cpuspeed=8000&memory=%d&localstorage=%d"%(l,i,j,k,memory,localstorage) + h.url = "http://sim/%d%d%d%d"%(l,i,j,k) + c.hosts.append(h) + + '''add 2 primary storages''' + for m in range(2): + primary = primaryStorage() + primary.name = "primary"+str(l) + str(i) + str(j) + str(m) + #primary.url = "nfs://localhost/path%s/size=%d"%(str(l) + str(i) + str(j) + str(m), size) + primary.url = "nfs://localhost/path%s"%(str(l) + str(i) + str(j) + str(m)) + c.primaryStorages.append(primary) + + p.clusters.append(c) + + z.pods.append(p) + + '''add two secondary''' + for i in range(5): + secondary = secondaryStorage() + secondary.url = "nfs://localhost/path"+str(l) + str(i) + z.secondaryStorages.append(secondary) + + '''add default guest network''' + ips = iprange() + ips.vlan = "26" + ips.startip = "172.16.26.2" + ips.endip = "172.16.26.100" + ips.gateway = "172.16.26.1" + ips.netmask = "255.255.255.0" + z.ipranges.append(ips) + + + zs.zones.append(z) + + '''Add one mgt server''' + mgt = managementServer() + mgt.mgtSvrIp = "localhost" + zs.mgtSvr.append(mgt) + + '''Add a database''' + db = dbServer() + db.dbSvr = "localhost" + + zs.dbSvr = db + + '''add global configuration''' + global_settings = {'expunge.delay': '60', + 'expunge.interval': '60', + 'expunge.workers': '3', + } + for k,v in global_settings.iteritems(): + cfg = configuration() + cfg.name = k + cfg.value = v + zs.globalConfig.append(cfg) + + ''''add loggers''' + testClientLogger = logger() + testClientLogger.name = "TestClient" + testClientLogger.file = "/tmp/testclient.log" + + testCaseLogger = logger() + testCaseLogger.name = "TestCase" + testCaseLogger.file = "/tmp/testcase.log" + + zs.logger.append(testClientLogger) + zs.logger.append(testCaseLogger) + + return zs + def generate_setup_config(config, file=None): describe = config if file is None: @@ -666,6 +786,7 @@ if __name__ == "__main__": parser.add_option("-i", "--input", action="store", default=None , dest="inputfile", help="input file") parser.add_option("-a", "--advanced", action="store_true", default=False, dest="advanced", help="use advanced networking") + parser.add_option("-s", "--advancedsg", action="store_true", default=False, dest="advancedsg", help="use advanced networking with security groups") parser.add_option("-o", "--output", action="store", default="./datacenterCfg", dest="output", help="the path where the json config file generated, by default is ./datacenterCfg") (options, args) = parser.parse_args() @@ -674,6 +795,8 @@ if __name__ == "__main__": config = get_setup_config(options.inputfile) if options.advanced: config = describe_setup_in_advanced_mode() + elif options.advancedsg: + config = describe_setup_in_advancedsg_mode() else: config = describe_setup_in_basic_mode() diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py index 7059059beb1..cf5cf782676 100644 --- a/tools/marvin/marvin/deployDataCenter.py +++ b/tools/marvin/marvin/deployDataCenter.py @@ -300,7 +300,8 @@ class deployDataCenters(): createzone.securitygroupenabled = zone.securitygroupenabled createzone.localstorageenabled = zone.localstorageenabled createzone.networktype = zone.networktype - createzone.guestcidraddress = zone.guestcidraddress + if zone.securitygroupenabled != "true": + createzone.guestcidraddress = zone.guestcidraddress zoneresponse = self.apiClient.createZone(createzone) zoneId = zoneresponse.id @@ -334,10 +335,37 @@ class deployDataCenters(): self.createVlanIpRanges(zone.networktype, zone.ipranges, \ zoneId, forvirtualnetwork=True) - if zone.networktype == "Advanced": + if zone.networktype == "Advanced" and zone.securitygroupenabled != "true": self.createpods(zone.pods, zoneId) self.createVlanIpRanges(zone.networktype, zone.ipranges, \ zoneId) + elif zone.networktype == "Advanced" and zone.securitygroupenabled == "true": + listnetworkoffering = listNetworkOfferings.listNetworkOfferingsCmd() + listnetworkoffering.name = "DefaultSharedNetworkOfferingWithSGService" + if zone.networkofferingname is not None: + listnetworkoffering.name = zone.networkofferingname + + listnetworkofferingresponse = \ + self.apiClient.listNetworkOfferings(listnetworkoffering) + + networkcmd = createNetwork.createNetworkCmd() + networkcmd.displaytext = "Shared SG enabled network" + networkcmd.name = "Shared SG enabled network" + networkcmd.networkofferingid = listnetworkofferingresponse[0].id + networkcmd.zoneid = zoneId + + ipranges = zone.ipranges + if ipranges: + iprange = ipranges.pop() + networkcmd.startip = iprange.startip + networkcmd.endip = iprange.endip + networkcmd.gateway = iprange.gateway + networkcmd.netmask = iprange.netmask + networkcmd.vlan = iprange.vlan + + networkcmdresponse = self.apiClient.createNetwork(networkcmd) + networkId = networkcmdresponse.id + self.createpods(zone.pods, zoneId, networkId) self.createSecondaryStorages(zone.secondaryStorages, zoneId) diff --git a/tools/marvin/marvin/sandbox/advancedsg/__init__.py b/tools/marvin/marvin/sandbox/advancedsg/__init__.py new file mode 100644 index 00000000000..57823fcc162 --- /dev/null +++ b/tools/marvin/marvin/sandbox/advancedsg/__init__.py @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. + + diff --git a/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py b/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py new file mode 100644 index 00000000000..f9edf4d5803 --- /dev/null +++ b/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. +''' +############################################################ +# Experimental state of scripts +# * Need to be reviewed +# * Only a sandbox +############################################################ +''' +import random +import marvin +from ConfigParser import SafeConfigParser +from optparse import OptionParser +from marvin.configGenerator import * + + +def getGlobalSettings(config): + for k, v in dict(config.items('globals')).iteritems(): + cfg = configuration() + cfg.name = k + cfg.value = v + yield cfg + + +def describeResources(config): + zs = cloudstackConfiguration() + + z = zone() + z.dns1 = config.get('environment', 'dns') + z.internaldns1 = config.get('environment', 'dns') + z.name = 'Sandbox-%s'%(config.get('cloudstack', 'hypervisor')) + z.networktype = 'Advanced' + z.securitygroupenabled = 'true' + + sgprovider = provider() + sgprovider.broadcastdomainrange = 'ZONE' + sgprovider.name = 'SecurityGroupProvider' + + pn = physical_network() + pn.name = "Sandbox-pnet" + pn.tags = ["cloud-simulator-pnet"] + pn.traffictypes = [traffictype("Guest"), + traffictype("Management", {"simulator" : "cloud-simulator-mgmt"})] + pn.isolationmethods = ["VLAN"] + pn.providers.append(sgprovider) + + z.physical_networks.append(pn) + + p = pod() + p.name = 'POD0' + p.gateway = config.get('cloudstack', 'private.gateway') + p.startip = config.get('cloudstack', 'private.pod.startip') + p.endip = config.get('cloudstack', 'private.pod.endip') + p.netmask = config.get('cloudstack', 'private.netmask') + + v = iprange() + v.gateway = config.get('cloudstack', 'guest.gateway') + v.startip = config.get('cloudstack', 'guest.vlan.startip') + v.endip = config.get('cloudstack', 'guest.vlan.endip') + v.netmask = config.get('cloudstack', 'guest.netmask') + v.vlan = config.get('cloudstack', 'guest.vlan') + z.ipranges.append(v) + + c = cluster() + c.clustername = 'C0' + c.hypervisor = config.get('cloudstack', 'hypervisor') + c.clustertype = 'CloudManaged' + + h = host() + h.username = 'root' + h.password = config.get('cloudstack', 'host.password') + h.url = 'http://%s'%(config.get('cloudstack', 'host')) + c.hosts.append(h) + + ps = primaryStorage() + ps.name = 'PS0' + ps.url = config.get('cloudstack', 'primary.pool') + c.primaryStorages.append(ps) + + p.clusters.append(c) + z.pods.append(p) + + secondary = secondaryStorage() + secondary.url = config.get('cloudstack', 'secondary.pool') + z.secondaryStorages.append(secondary) + + '''Add zone''' + zs.zones.append(z) + + '''Add mgt server''' + mgt = managementServer() + mgt.mgtSvrIp = config.get('environment', 'mshost') + mgt.user = config.get('environment', 'mshost.user') + mgt.passwd = config.get('environment', 'mshost.passwd') + zs.mgtSvr.append(mgt) + + '''Add a database''' + db = dbServer() + db.dbSvr = config.get('environment', 'mysql.host') + db.user = config.get('environment', 'mysql.cloud.user') + db.passwd = config.get('environment', 'mysql.cloud.passwd') + zs.dbSvr = db + + '''Add some configuration''' + [zs.globalConfig.append(cfg) for cfg in getGlobalSettings(config)] + + ''''add loggers''' + testClientLogger = logger() + testClientLogger.name = 'TestClient' + testClientLogger.file = 'testclient.log' + + testCaseLogger = logger() + testCaseLogger.name = 'TestCase' + testCaseLogger.file = 'testcase.log' + + zs.logger.append(testClientLogger) + zs.logger.append(testCaseLogger) + return zs + + +if __name__ == '__main__': + parser = OptionParser() + parser.add_option('-i', '--input', action='store', default='setup.properties', \ + dest='input', help='file containing environment setup information') + parser.add_option('-o', '--output', action='store', default='./sandbox.cfg', \ + dest='output', help='path where environment json will be generated') + + + (opts, args) = parser.parse_args() + + cfg_parser = SafeConfigParser() + cfg_parser.read(opts.input) + + cfg = describeResources(cfg_parser) + generate_setup_config(cfg, opts.output) diff --git a/tools/marvin/marvin/sandbox/advancedsg/setup.properties b/tools/marvin/marvin/sandbox/advancedsg/setup.properties new file mode 100644 index 00000000000..ee07ce23938 --- /dev/null +++ b/tools/marvin/marvin/sandbox/advancedsg/setup.properties @@ -0,0 +1,61 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. + + +[globals] +#global settings in cloudstack +expunge.delay=60 +expunge.interval=60 +storage.cleanup.interval=300 +account.cleanup.interval=600 +expunge.workers=3 +workers=10 +vm.allocation.algorithm=random +vm.op.wait.interval=5 +guest.domain.suffix=sandbox.simulator +instance.name=QA +direct.agent.load.size=1000 +default.page.size=10000 +check.pod.cidrs=true +secstorage.allowed.internal.sites=10.147.28.0/24 +[environment] +dns=10.147.28.6 +mshost=localhost +mshost.user=root +mshost.passwd=password +mysql.host=localhost +mysql.cloud.user=cloud +mysql.cloud.passwd=cloud +[cloudstack] +#management network +private.gateway=10.147.29.1 +private.pod.startip=10.147.29.150 +private.pod.endip=10.147.29.159 +private.netmask=255.255.255.0 +#guest network +guest.gateway=10.147.31.1 +guest.vlan=31 +guest.vlan.startip=10.147.31.150 +guest.vlan.endip=10.147.31.159 +guest.netmask=255.255.255.0 +#hypervisor host information +hypervisor=Simulator +host=simulator0 +host.password=password +#storage pools +primary.pool=nfs://10.147.28.6:/export/home/sandbox/primary +secondary.pool=nfs://10.147.28.6:/export/home/sandbox/sstor diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py index 8dfd1b895d0..eeed3bfa8fd 100644 --- a/tools/marvin/setup.py +++ b/tools/marvin/setup.py @@ -45,7 +45,7 @@ setup(name="Marvin", url="https://builds.apache.org/job/cloudstack-marvin/", packages=["marvin", "marvin.cloudstackAPI", "marvin.integration", "marvin.integration.lib", "marvin.sandbox", - "marvin.sandbox.advanced", "marvin.sandbox.basic"], + "marvin.sandbox.advanced", "marvin.sandbox.advancedsg", "marvin.sandbox.basic"], license="LICENSE.txt", install_requires=[ "mysql-connector-python", From ea440f2593b82344c1b2cc6437a2f7fc9ad68e9b Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 15:50:19 -0700 Subject: [PATCH 076/412] Add API calls to update ACL item order on drag-and-drop --- ui/scripts/ui/widgets/multiEdit.js | 18 +++++++++++++++++- ui/scripts/vpc.js | 24 +++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 14f379fcc2b..936c20c48a0 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -34,6 +34,7 @@ $item.append($('').append($(''))); $tr = $('').appendTo($item.find('tbody')); + $item.data('json-obj', multiRule); if (itemData) { $tr.data('multi-edit-data', itemData); @@ -828,7 +829,22 @@ $.fn.multiEdit = function(args) { $(' + + CLOUDSTACK-2709 + Egress rules are are not supported on shared networks. + > + CLOUDSTACK-1747 diff --git a/docs/en-US/egress-firewall-rule.xml b/docs/en-US/egress-firewall-rule.xml index ef0e25efd03..ab16517b458 100644 --- a/docs/en-US/egress-firewall-rule.xml +++ b/docs/en-US/egress-firewall-rule.xml @@ -23,13 +23,15 @@ The egress firewall rules are supported only on virtual routers. - The egress traffic originates from a private network to a public network, such as the Internet. By default, the egress traffic is blocked, so no outgoing traffic is allowed from a guest network to the Internet. However, you can control the egress traffic in an Advanced zone by creating egress firewall rules. When an egress firewall rule is applied, the traffic specific to the rule is allowed and the remaining traffic is blocked. When all the firewall rules are removed the default policy, Block, is applied. + + The egress firewall rules are not supported on shared networks. + Consider the following scenarios to apply egress firewall rules: From 565d740e5d446ed05a8761afa074605863bee193 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 28 May 2013 15:00:07 +0530 Subject: [PATCH 150/412] fixing build error --- docs/en-US/Release_Notes.xml | 45 +++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index ab734cdf5d1..2ae87320e40 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -36,8 +36,8 @@ under the License. will find instruction in the &PRODUCT; API Developer's Guide - If you find any errors or problems in this guide, please see . We hope you enjoy - working with &PRODUCT;! + If you find any errors or problems in this guide, please see . + We hope you enjoy working with &PRODUCT;! Version 4.1.0 @@ -4399,9 +4399,10 @@ under the License. CLOUDSTACK-2709 - Egress rules are are not supported on shared networks. - > + + Egress rules are are not supported on shared networks. + CLOUDSTACK-1747 @@ -4654,9 +4655,12 @@ under the License. components.xml to be on the safe side. - After upgrading to 4.1, API clients are expected to send plain text passwords for login and user creation, instead of MD5 hash. Incase, api client changes are not acceptable, following changes are to be made for backward compatibility: - Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default authenticator (1st entry in the userAuthenticators adapter list is default) - + After upgrading to 4.1, API clients are expected to send plain text passwords for + login and user creation, instead of MD5 hash. Incase, api client changes are not + acceptable, following changes are to be made for backward compatibility: + Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default + authenticator (1st entry in the userAuthenticators adapter list is default) + <!-- Security adapters --> <bean id="userAuthenticators" class="com.cloud.utils.component.AdapterList"> <property name="Adapters"> @@ -4668,7 +4672,8 @@ under the License. </property> </bean> - PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to 4.1. + PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to + 4.1. If you are using Ubuntu, follow this procedure to upgrade your packages. If not, @@ -5134,9 +5139,12 @@ service cloudstack-agent start - After upgrading to 4.1, API clients are expected to send plain text passwords for login and user creation, instead of MD5 hash. Incase, api client changes are not acceptable, following changes are to be made for backward compatibility: - Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default authenticator (1st entry in the userAuthenticators adapter list is default) - + After upgrading to 4.1, API clients are expected to send plain text passwords for + login and user creation, instead of MD5 hash. Incase, api client changes are not + acceptable, following changes are to be made for backward compatibility: + Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default + authenticator (1st entry in the userAuthenticators adapter list is default) + <!-- Security adapters --> <bean id="userAuthenticators" class="com.cloud.utils.component.AdapterList"> <property name="Adapters"> @@ -5148,7 +5156,8 @@ service cloudstack-agent start </property> </bean> - PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to 4.1. + PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to + 4.1. Start the first Management Server. Do not start any other Management Server nodes @@ -5729,9 +5738,12 @@ service cloudstack-agent start - After upgrading to 4.1, API clients are expected to send plain text passwords for login and user creation, instead of MD5 hash. Incase, api client changes are not acceptable, following changes are to be made for backward compatibility: - Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default authenticator (1st entry in the userAuthenticators adapter list is default) - + After upgrading to 4.1, API clients are expected to send plain text passwords for + login and user creation, instead of MD5 hash. Incase, api client changes are not + acceptable, following changes are to be made for backward compatibility: + Modify componentsContext.xml, and make PlainTextUserAuthenticator as the default + authenticator (1st entry in the userAuthenticators adapter list is default) + <!-- Security adapters --> <bean id="userAuthenticators" class="com.cloud.utils.component.AdapterList"> <property name="Adapters"> @@ -5743,7 +5755,8 @@ service cloudstack-agent start </property> </bean> - PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to 4.1. + PlainTextUserAuthenticator works the same way MD5UserAuthenticator worked prior to + 4.1. If you have made changes to your existing copy of the From 75a8c1ca8816df7c29aca6689f51c449b4b073b4 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 28 May 2013 16:20:19 +0530 Subject: [PATCH 151/412] CLOUDSTACK-2712: updateHvCapability mapped to serviceOfferingResponse response object of updateHypervisorCapabilities is set to ServiceOfferingResponse Signed-off-by: Prasanna Santhanam --- .../admin/config/UpdateHypervisorCapabilitiesCmd.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java b/api/src/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java index e2fe8a7b1ea..8728f915dcb 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/config/UpdateHypervisorCapabilitiesCmd.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.admin.config; +import com.cloud.hypervisor.HypervisorCapabilities; +import com.cloud.user.Account; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -23,13 +25,9 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; -import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.log4j.Logger; -import com.cloud.hypervisor.HypervisorCapabilities; -import com.cloud.user.Account; - -@APICommand(name = "updateHypervisorCapabilities", description="Updates a hypervisor capabilities.", responseObject=ServiceOfferingResponse.class, since="3.0.0") +@APICommand(name = "updateHypervisorCapabilities", description="Updates a hypervisor capabilities.", responseObject=HypervisorCapabilitiesResponse.class, since="3.0.0") public class UpdateHypervisorCapabilitiesCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(UpdateHypervisorCapabilitiesCmd.class.getName()); private static final String s_name = "updatehypervisorcapabilitiesresponse"; From 4a563b64ee73fd280967ffb0d1efd9f2918c6573 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 28 May 2013 16:37:23 +0530 Subject: [PATCH 152/412] CLOUDSTACK-2713: StoragePoolResponse for a createTemplate createTemplate returns a templateresponse and not a storagepool response. Correcting the mapping to fix API docs. Signed-off-by: Prasanna Santhanam --- .../user/template/CreateTemplateCmd.java | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java index ba1f924fe02..6a482ac1ff2 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java @@ -16,24 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.template; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCreateCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.GuestOSResponse; -import org.apache.cloudstack.api.response.SnapshotResponse; -import org.apache.cloudstack.api.response.StoragePoolResponse; -import org.apache.cloudstack.api.response.TemplateResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.VolumeResponse; -import org.apache.log4j.Logger; - import com.cloud.async.AsyncJob; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; @@ -45,8 +27,24 @@ import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.UserContext; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.log4j.Logger; -@APICommand(name = "createTemplate", responseObject = StoragePoolResponse.class, description = "Creates a template of a virtual machine. " + "The virtual machine must be in a STOPPED state. " +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@APICommand(name = "createTemplate", responseObject = TemplateResponse.class, description = "Creates a template of a virtual machine. " + "The virtual machine must be in a STOPPED state. " + "A template created from this command is automatically designated as a private template visible to the account that created it.") public class CreateTemplateCmd extends BaseAsyncCreateCmd { public static final Logger s_logger = Logger.getLogger(CreateTemplateCmd.class.getName()); From cfd8056c142c2ed49252c989b00b00cdbe7850de Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 28 May 2013 20:54:01 +0900 Subject: [PATCH 153/412] remove a call path that cause NullPointerException QemuImg.convert expects the arguments not null. --- .../kvm/storage/LibvirtStorageAdaptor.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index fa2f6707923..89e22c8d05e 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -911,20 +911,20 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath); } else { destFile = new QemuImgFile(destPath, destFormat); + try { + qemu.convert(srcFile, destFile); + } catch (QemuImgException e) { + s_logger.error("Failed to convert " + srcFile.getFileName() + " to " + + destFile.getFileName() + " the error was: " + e.getMessage()); + newDisk = null; + } } } catch (QemuImgException e) { s_logger.error("Failed to fetch the information of file " + srcFile.getFileName() + " the error was: " + e.getMessage()); + newDisk = null; } } - - try { - qemu.convert(srcFile, destFile); - } catch (QemuImgException e) { - s_logger.error("Failed to convert " + srcFile.getFileName() + " to " - + destFile.getFileName() + " the error was: " + e.getMessage()); - } - } else if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() == StoragePoolType.RBD)) { /** * Qemu doesn't support writing to RBD format 2 directly, so we have to write to a temporary RAW file first From b0a9adeffee823eca0bf39427ddee71bd4a5fe24 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 28 May 2013 10:51:12 -0700 Subject: [PATCH 154/412] Advance SG enabled zone. Moved following db upgrade change from 40-41 to 41-42 db upgrade path: Adnvace Zone with SG enabled: DB upgrade for Advance SG enabled zone - setup name/displayText for Guest Shared SG enabled network (it used to be system network, and name was never set for it in 2.2.x) --- setup/db/db/schema-40to410.sql | 4 ---- setup/db/db/schema-410to420.sql | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index b4c30aebe88..b7b1c7a91dd 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -1639,7 +1639,3 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Usage', 'DEFAULT', 'manageme INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (163, UUID(), 10, 'Ubuntu 12.04 (32-bit)'); INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (164, UUID(), 10, 'Ubuntu 12.04 (64-bit)'); - -#update shared sg enabled network with not null name in Advance Security Group enabled network -UPDATE `cloud`.`networks` set name='Shared SG enabled network', display_text='Shared SG enabled network' WHERE name IS null AND traffic_type='Guest' AND data_center_id IN (select id from data_center where networktype='Advanced' and is_security_group_enabled=1) AND acl_type='Domain'; - diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 334aae76f33..f25588add1d 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1605,3 +1605,8 @@ CREATE TABLE `cloud`.`nic_ip_alias` ( alter table `cloud`.`vpc_gateways` add column network_acl_id bigint unsigned default 1 NOT NULL; update `cloud`.`vpc_gateways` set network_acl_id = 2; + + +#update shared sg enabled network with not null name in Advance Security Group enabled network +UPDATE `cloud`.`networks` set name='Shared SG enabled network', display_text='Shared SG enabled network' WHERE name IS null AND traffic_type='Guest' AND data_center_id IN (select id from data_center where networktype='Advanced' and is_security_group_enabled=1) AND acl_type='Domain'; + From 7ee71dfd87e0405bf41adb18c20c5825dffc8f71 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 28 May 2013 11:50:39 -0700 Subject: [PATCH 155/412] CLOUDSTACK-1974: Dedicate Guest VLAN Range - UI - Infrastructure menu - physical network - Guest - Dedicated Guest VLAN Range tab - Dedicate VLAN Range dialog - change VLAN Range field from textbox to dropdown that lists all existing Guest VLAN Ranges under the physical network. --- ui/scripts/system.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 9ac3b3083db..8b9a81fe7c7 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -2153,7 +2153,20 @@ createForm: { title: 'Dedicate VLAN Range', fields: { - vlanrange: { label: 'VLAN Range(s)', validation: { required: true } }, + vlanrange: { + label: 'VLAN Range', + select: function(args) { + var items = []; + if(args.context.physicalNetworks[0].vlan != null && args.context.physicalNetworks[0].vlan.length > 0) { + var vlanranges = args.context.physicalNetworks[0].vlan.split(";"); + for(var i = 0; i < vlanranges.length ; i++) { + items.push({id: vlanranges[i], description: vlanranges[i]}); + } + } + args.response.success({data: items}); + }, + validation: { required: true } + }, account: { label: 'label.account', validation: { required: true } }, domainid: { label: 'label.domain', From 78e50c3bc8f250f0ba72207e08aaeb553eafb4b0 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 28 May 2013 14:17:47 -0700 Subject: [PATCH 156/412] CLOUDSTACK-2678: portable IP ranges - regions detailView - Portable IP Ranges - implement Add Portable IP Range action. --- ui/scripts/regions.js | 84 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/ui/scripts/regions.js b/ui/scripts/regions.js index e86fd283aad..5ca416781ae 100644 --- a/ui/scripts/regions.js +++ b/ui/scripts/regions.js @@ -463,9 +463,11 @@ id: 'portableIpRanges', label: 'Portable IP Ranges', fields: { - name: { label: 'label.name' }, - gslbdomainname: { label: 'GSLB Domain Name' }, - gslblbmethod: { label: 'Algorithm' } + startip: { label: 'label.start.IP' }, + endip: { label: 'label.end.IP' }, + gateway: { label: 'label.gateway' }, + netmask: { label: 'label.netmask' }, + vlan: { label: 'label.vlan' } }, dataProvider: function(args) { $.ajax({ @@ -474,7 +476,7 @@ regionid: args.context.regions[0].id }, success: function(json) { - var items = json.listportableipresponse.portableip; + var items = json.listportableipresponse.portableiprange; args.response.success({ data: items }); @@ -483,7 +485,79 @@ args.response.error(parseXMLHttpResponse(json)); } }); - } + }, + actions: { + add: { + label: 'Add Portable IP Range', + + messages: { + notification: function(args) { + return 'Add Portable IP Range'; + } + }, + + createForm: { + title: 'Add Portable IP Range', + fields: { + startip: { + label: 'label.start.IP', + validation: { required: true } + }, + endip: { + label: 'label.end.IP', + validation: { required: true } + }, + gateway: { + label: 'label.gateway', + validation: { required: true } + }, + netmask: { + label: 'label.netmask', + validation: { required: true } + }, + vlan: { + label: 'label.vlan', + validation: { required: false } + } + } + }, + action: function(args) { + var data = { + regionid: args.context.regions[0].id, + startip: args.data.startip, + endip: args.data.endip, + gateway: args.data.gateway, + netmask: args.data.netmask + }; + if(args.data.vlan != null && args.data.vlan.length > 0) { + $.extend(data, { + vlan: args.data.vlan + }) + } + $.ajax({ + url: createURL('createPortableIpRange'), + data: data, + success: function(json) { + var jid = json.createportableiprangeresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.portableiprange; + } + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + } } }, From ecc30ddc0dfcd8237c7e9822160b3be207017520 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 28 May 2013 15:18:36 -0700 Subject: [PATCH 157/412] CLOUDSTACK-2678: portable IP ranges - regions menu - Portable IP Ranges - implement Delete Portable IP Range action. --- ui/scripts/regions.js | 94 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/ui/scripts/regions.js b/ui/scripts/regions.js index 5ca416781ae..b9b6daeac20 100644 --- a/ui/scripts/regions.js +++ b/ui/scripts/regions.js @@ -489,13 +489,11 @@ actions: { add: { label: 'Add Portable IP Range', - messages: { notification: function(args) { return 'Add Portable IP Range'; } }, - createForm: { title: 'Add Portable IP Range', fields: { @@ -557,7 +555,97 @@ poll: pollAsyncJobResult } } - } + }, + + detailView: { + name: 'Portable IP Range details', + actions: { + remove: { + label: 'Delete Portable IP Range', + messages: { + confirm: function(args) { + return 'Please confirm you want to delete Portable IP Range'; + }, + notification: function(args) { + return 'Delete Portable IP Range'; + } + }, + action: function(args) { + var data = { + id: args.context.portableIpRanges[0].id + }; + $.ajax({ + url: createURL('deletePortableIpRange'), + data: data, + async: true, + success: function(json) { + var jid = json.deleteportablepublicipresponse.jobid; + args.response.success({ + _custom: { + jobId: jid + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + tabs: { + details: { + title: 'label.details', + fields: [ + { + id: { label: 'label.id' } + }, + { + startip: { label: 'label.start.IP' }, + endip: { label: 'label.end.IP' }, + gateway: { label: 'label.gateway' }, + netmask: { label: 'label.netmask' }, + vlan: { label: 'label.vlan' }, + portableipaddress: { + label: 'Portable IPs', + converter: function(args) { + var text1 = ''; + if(args != null) { + for(var i = 0; i < args.length; i++) { + if(i > 0) { + text1 += ', '; + } + text1 += args[i].ipaddress; + } + } + return text1; + } + } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listPortableIpRanges'), + data: { + id: args.context.portableIpRanges[0].id + }, + success: function(json) { + var item = json.listportableipresponse.portableiprange[0]; + args.response.success({ + data: item + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } + } + } } }, From f2c468d00c2afcd2ccccc50d7f02de2c61b11997 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 28 May 2013 15:29:27 -0700 Subject: [PATCH 158/412] Removed unused imports and obsolete methods from ConfigurationManager/ConfigurationServer, NetworkManager/NetworkService --- .../ConfigurationManagerImpl.java | 130 +------- .../src/com/cloud/network/NetworkManager.java | 20 +- .../com/cloud/network/NetworkManagerImpl.java | 308 +++++++++++------- .../com/cloud/network/NetworkServiceImpl.java | 10 +- .../cloud/server/ConfigurationServerImpl.java | 29 +- .../cloud/network/MockNetworkManagerImpl.java | 22 -- .../com/cloud/vpc/MockNetworkManagerImpl.java | 54 +-- 7 files changed, 242 insertions(+), 331 deletions(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 8baf1fb134b..79375f9a86e 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -39,11 +39,6 @@ import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; -import com.cloud.dc.*; -import com.cloud.dc.dao.*; -import com.cloud.user.*; -import com.cloud.event.UsageEventUtils; -import com.cloud.utils.db.*; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.ApiConstants.LDAPParams; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; @@ -71,7 +66,13 @@ import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd; -import org.apache.cloudstack.region.*; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpDao; +import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.PortableIpRangeDao; +import org.apache.cloudstack.region.PortableIpRangeVO; +import org.apache.cloudstack.region.PortableIpVO; +import org.apache.cloudstack.region.Region; import org.apache.cloudstack.region.dao.RegionDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; @@ -149,8 +150,8 @@ import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; import com.cloud.network.dao.PhysicalNetworkVO; -import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; @@ -192,6 +193,11 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.StringUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.NicIpAlias; @@ -200,59 +206,8 @@ import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.NicIpAliasVO; import com.cloud.vm.dao.NicSecondaryIpDao; -import edu.emory.mathcs.backport.java.util.Arrays; -import org.apache.cloudstack.acl.SecurityChecker; -import org.apache.cloudstack.api.ApiConstants.LDAPParams; -import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; -import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; -import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; -import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; -import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.CreateServiceOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.DeleteDiskOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.DeleteServiceOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd; -import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; -import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; -import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; -import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; -import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; -import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; -import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; -import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; -import java.net.URI; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import edu.emory.mathcs.backport.java.util.Arrays; @Component @Local(value = { ConfigurationManager.class, ConfigurationService.class }) @@ -938,10 +893,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati */ } - Grouping.AllocationState allocationState = null; if (allocationStateStr != null && !allocationStateStr.isEmpty()) { try { - allocationState = Grouping.AllocationState.valueOf(allocationStateStr); + Grouping.AllocationState.valueOf(allocationStateStr); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("Unable to resolve Allocation State '" + allocationStateStr + "' to a supported state"); } @@ -1363,10 +1317,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Please enter a valid IPv6 address for IP6 DNS2"); } - Grouping.AllocationState allocationState = null; if (allocationStateStr != null && !allocationStateStr.isEmpty()) { try { - allocationState = Grouping.AllocationState.valueOf(allocationStateStr); + Grouping.AllocationState.valueOf(allocationStateStr); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("Unable to resolve Allocation State '" + allocationStateStr + "' to a supported state"); } @@ -2346,7 +2299,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String endIP = cmd.getEndIp(); String newVlanGateway = cmd.getGateway(); String newVlanNetmask = cmd.getNetmask(); - Long userId = UserContext.current().getCallerUserId(); String vlanId = cmd.getVlan(); Boolean forVirtualNetwork = cmd.isForVirtualNetwork(); Long networkId = cmd.getNetworkID(); @@ -3351,43 +3303,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Please ensure that your end IP is in the same subnet as your IP range's gateway, as per the IP range's netmask."); } } - - private void checkPrivateIpRangeErrors(Long podId, String startIP, String endIP) { - HostPodVO pod = _podDao.findById(podId); - if (pod == null) { - throw new InvalidParameterValueException("Please specify a valid pod."); - } - - // Check that the start and end IPs are valid - if (!NetUtils.isValidIp(startIP)) { - throw new InvalidParameterValueException("Please specify a valid start IP"); - } - - if (endIP != null && !NetUtils.isValidIp(endIP)) { - throw new InvalidParameterValueException("Please specify a valid end IP"); - } - - if (endIP != null && !NetUtils.validIpRange(startIP, endIP)) { - throw new InvalidParameterValueException("Please specify a valid IP range."); - } - - // Check that the IPs that are being added are compatible with the pod's - // CIDR - String cidrAddress = getCidrAddress(podId); - long cidrSize = getCidrSize(podId); - - if (endIP != null && !NetUtils.sameSubnetCIDR(startIP, endIP, cidrSize)) { - throw new InvalidParameterValueException("Please ensure that your start IP and end IP are in the same subnet, as per the pod's CIDR size."); - } - - if (!NetUtils.sameSubnetCIDR(startIP, cidrAddress, cidrSize)) { - throw new InvalidParameterValueException("Please ensure that your start IP is in the same subnet as the pod's CIDR address."); - } - - if (endIP != null && !NetUtils.sameSubnetCIDR(endIP, cidrAddress, cidrSize)) { - throw new InvalidParameterValueException("Please ensure that your end IP is in the same subnet as the pod's CIDR address."); - } - } + private String getCidrAddress(String cidr) { String[] cidrPair = cidr.split("\\/"); @@ -3399,15 +3315,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return Integer.parseInt(cidrPair[1]); } - private String getCidrAddress(long podId) { - HostPodVO pod = _podDao.findById(podId); - return pod.getCidrAddress(); - } - - private long getCidrSize(long podId) { - HostPodVO pod = _podDao.findById(podId); - return pod.getCidrSize(); - } @Override public void checkPodCidrSubnets(long dcId, Long podIdToBeSkipped, String cidr) { @@ -4359,7 +4266,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public boolean isOfferingForVpc(NetworkOffering offering) { boolean vpcProvider = _ntwkOffServiceMapDao.isProviderForNetworkOffering(offering.getId(), Provider.VPCVirtualRouter); - boolean internalLb = offering.getInternalLb(); return vpcProvider; } @@ -4516,6 +4422,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // Note: This method will be used for entity name validations in the coming // releases (place holder for now) + @SuppressWarnings("unused") private void validateEntityName(String str) { String forbidden = "~!@#$%^&*()+="; char[] searchChars = forbidden.toCharArray(); @@ -4741,7 +4648,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String endIP = cmd.getEndIp(); String gateway = cmd.getGateway(); String netmask = cmd.getNetmask(); - Long userId = UserContext.current().getCallerUserId(); String vlanId = cmd.getVlan(); Region region = _regionDao.findById(regionId); diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 19f396abe6e..8627251e717 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -19,7 +19,6 @@ package com.cloud.network; import java.util.List; import java.util.Map; -import com.cloud.network.element.DhcpServiceProvider; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import com.cloud.dc.DataCenter; @@ -39,6 +38,7 @@ import com.cloud.network.Network.Service; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.LoadBalancingServiceProvider; import com.cloud.network.element.StaticNatServiceProvider; import com.cloud.network.element.UserDataServiceProvider; @@ -59,7 +59,6 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; -import org.apache.cloudstack.region.PortableIp; /** * NetworkManager manages the network for the different end users. @@ -319,9 +318,6 @@ public interface NetworkManager { InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; - PublicIp assignVpnGatewayIpAddress(long dcId, Account owner, long vpcId) throws InsufficientAddressCapacityException, ConcurrentOperationException; - - /** * @param addr */ @@ -346,10 +342,7 @@ public interface NetworkManager { * @return */ int getNetworkLockTimeout(); - - - boolean cleanupIpResources(long addrId, long userId, Account caller); - + boolean restartNetwork(Long networkId, Account callerAccount, User callerUser, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; @@ -370,26 +363,23 @@ public interface NetworkManager { Map finalizeServicesAndProvidersForNetwork(NetworkOffering offering, Long physicalNetworkId); - List getProvidersForServiceInNetwork(Network network, Service service); StaticNatServiceProvider getStaticNatProviderForNetwork(Network network); + boolean isNetworkInlineMode(Network network); int getRuleCountForIp(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state); LoadBalancingServiceProvider getLoadBalancingProviderForNetwork(Network network, Scheme lbScheme); - boolean isSecondaryIpSetForNic(long nicId); - public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) - throws InsufficientAddressCapacityException; - + public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) throws InsufficientAddressCapacityException; List listVmNics(Long vmId, Long nicId); + String allocatePublicIpForGuestNic(Long networkId, DataCenter dc, Pod pod, Account caller, String requestedIp) throws InsufficientAddressCapacityException; - boolean removeVmSecondaryIpsOfNic(long nicId); NicVO savePlaceholderNic(Network network, String ip4Address, Type vmType); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index b443dca6abc..fda0773147c 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -16,9 +16,46 @@ // under the License. package com.cloud.network; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpDao; +import org.apache.cloudstack.region.PortableIpVO; +import org.apache.cloudstack.region.Region; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; -import com.cloud.agent.api.*; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckNetworkAnswer; +import com.cloud.agent.api.CheckNetworkCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -26,9 +63,16 @@ import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.dc.*; +import com.cloud.dc.AccountVlanMapVO; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DataCenterVnetVO; +import com.cloud.dc.Pod; +import com.cloud.dc.PodVlanMapVO; +import com.cloud.dc.Vlan; import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterVnetDao; @@ -43,25 +87,61 @@ import com.cloud.event.ActionEventUtils; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; import com.cloud.event.dao.UsageEventDao; -import com.cloud.exception.*; +import com.cloud.exception.AccountLimitException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ConnectionException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.UnsupportedServiceException; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; -import com.cloud.server.ConfigurationServer; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.IpAddress.State; -import com.cloud.network.Network.*; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Event; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.addr.PublicIp; -import com.cloud.network.dao.*; -import com.cloud.network.element.*; +import com.cloud.network.dao.AccountGuestVlanMapDao; +import com.cloud.network.dao.AccountGuestVlanMapVO; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkDomainDao; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.dao.NetworkServiceMapVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; +import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.UserIpv6AddressDao; +import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.IpDeployingRequester; +import com.cloud.network.element.LoadBalancingServiceProvider; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.StaticNatServiceProvider; +import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.lb.LoadBalancingRulesManager; -import com.cloud.network.rules.*; +import com.cloud.network.rules.FirewallManager; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.LoadBalancerContainer.Scheme; @@ -83,7 +163,13 @@ import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingDetailsDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.org.Grouping; -import com.cloud.user.*; +import com.cloud.server.ConfigurationServer; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.ResourceLimitService; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.Journal; @@ -92,35 +178,35 @@ import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.*; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.JoinBuilder.JoinType; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; -import com.cloud.vm.*; +import com.cloud.vm.Nic; import com.cloud.vm.Nic.ReservationStrategy; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.ReservationContextImpl; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; -import com.cloud.vm.dao.*; -import org.apache.cloudstack.acl.ControlledEntity.ACLType; -import org.apache.cloudstack.acl.SecurityChecker.AccessType; -import org.apache.cloudstack.region.PortableIp; -import org.apache.cloudstack.region.PortableIpDao; -import org.apache.cloudstack.region.PortableIpVO; -import org.apache.cloudstack.region.Region; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import java.net.URI; -import java.util.*; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicSecondaryIpDao; +import com.cloud.vm.dao.NicSecondaryIpVO; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; /** * NetworkManagerImpl implements NetworkManager. @@ -260,9 +346,6 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L PortableIpDao _portableIpDao; protected StateMachine2 _stateMachine; - private final HashMap _systemNetworks = new HashMap(5); - private static Long _privateOfferingId = null; - ScheduledExecutorService _executor; SearchBuilder AssignIpAddressSearch; @@ -488,11 +571,6 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return ipToReturn; } - @Override - public PublicIp assignVpnGatewayIpAddress(long dcId, Account owner, long vpcId) throws InsufficientAddressCapacityException, ConcurrentOperationException { - return assignDedicateIpAddress(owner, null, vpcId, dcId, false); - } - @DB @Override @@ -512,6 +590,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L // this ownerId comes from owner or type Account. See the class "AccountVO" and the annotations in that class // to get the table name and field name that is queried to fill this ownerid. ConcurrentOperationException ex = new ConcurrentOperationException("Unable to lock account"); + throw ex; } if (s_logger.isDebugEnabled()) { s_logger.debug("lock account " + ownerId + " is acquired"); @@ -2173,11 +2252,13 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } } + @Override public void removeNic(VirtualMachineProfile vm, Nic nic) { removeNic(vm, _nicDao.findById(nic.getId())); } + protected void removeNic(VirtualMachineProfile vm, NicVO nic) { nic.setState(Nic.State.Deallocating); _nicDao.update(nic.getId(), nic); @@ -3239,8 +3320,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return false; } - @Override - public boolean cleanupIpResources(long ipId, long userId, Account caller) { + + protected boolean cleanupIpResources(long ipId, long userId, Account caller) { boolean success = true; // Revoke all firewall rules for the ip @@ -3348,9 +3429,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L throws InsufficientAddressCapacityException { String ipaddr = null; Account caller = UserContext.current().getCaller(); - long callerUserId = UserContext.current().getCallerUserId(); // check permissions - DataCenter zone = _configMgr.getZone(zoneId); Network network = _networksDao.findById(networkId); _accountMgr.checkAccess(caller, null, false, network); @@ -3955,7 +4034,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L String requestedIpv4, String requestedIpv6) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException { //This method allocates direct ip for the Shared network in Advance zones - boolean ipv4 = false, ipv6 = false; + boolean ipv4 = false; Transaction txn = Transaction.currentTxn(); txn.start(); @@ -3998,7 +4077,6 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L //FIXME - get ipv6 address from the placeholder if it's stored there if (network.getIp6Gateway() != null) { if (nic.getIp6Address() == null) { - ipv6 = true; UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6); Vlan vlan = _vlanDao.findById(ip.getVlanId()); nic.setIp6Address(ip.getAddress().toString()); @@ -4057,37 +4135,39 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { - - VirtualMachine vm = vmProfile.getVirtualMachine(); - DataCenter dc = _configMgr.getZone(network.getDataCenterId()); - Host host = _hostDao.findById(vm.getHostId()); - DeployDestination dest = new DeployDestination(dc, null, null, host); - - NicProfile nic = getNicProfileForVm(network, requested, vm); - - //1) allocate nic (if needed) Always allocate if it is a user vm - if (nic == null || (vmProfile.getType() == VirtualMachine.Type.User)) { - int deviceId = _nicDao.countNics(vm.getId()); - - nic = allocateNic(requested, network, false, - deviceId, vmProfile).first(); - - if (nic == null) { - throw new CloudRuntimeException("Failed to allocate nic for vm " + vm + " in network " + network); - } - - s_logger.debug("Nic is allocated successfully for vm " + vm + " in network " + network); - } - - //2) prepare nic - if (prepare) { - Pair implemented = implementNetwork(nic.getNetworkId(), dest, context); - nic = prepareNic(vmProfile, dest, context, nic.getId(), implemented.second()); - s_logger.debug("Nic is prepared successfully for vm " + vm + " in network " + network); - } - - return nic; + + VirtualMachine vm = vmProfile.getVirtualMachine(); + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + Host host = _hostDao.findById(vm.getHostId()); + DeployDestination dest = new DeployDestination(dc, null, null, host); + + NicProfile nic = getNicProfileForVm(network, requested, vm); + + //1) allocate nic (if needed) Always allocate if it is a user vm + if (nic == null || (vmProfile.getType() == VirtualMachine.Type.User)) { + int deviceId = _nicDao.countNics(vm.getId()); + + nic = allocateNic(requested, network, false, + deviceId, vmProfile).first(); + + if (nic == null) { + throw new CloudRuntimeException("Failed to allocate nic for vm " + vm + " in network " + network); } + + s_logger.debug("Nic is allocated successfully for vm " + vm + " in network " + network); + } + + //2) prepare nic + if (prepare) { + Pair implemented = implementNetwork(nic.getNetworkId(), dest, context); + nic = prepareNic(vmProfile, dest, context, nic.getId(), implemented.second()); + s_logger.debug("Nic is prepared successfully for vm " + vm + " in network " + network); + } + + return nic; + } + + @Override public List getNicProfiles(VirtualMachine vm) { List nics = _nicDao.listByVmId(vm.getId()); @@ -4137,6 +4217,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return map; } + @Override public List getProvidersForServiceInNetwork(Network network, Service service) { Map> service2ProviderMap = getServiceProvidersMap(network.getId()); @@ -4147,6 +4228,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return null; } + protected List getElementForServiceInNetwork(Network network, Service service) { List elements = new ArrayList(); List providers = getProvidersForServiceInNetwork(network, service); @@ -4168,7 +4250,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } return elements; } - + + @Override public StaticNatServiceProvider getStaticNatProviderForNetwork(Network network) { //only one provider per Static nat service is supoprted @@ -4177,6 +4260,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return (StaticNatServiceProvider)element; } + @Override public LoadBalancingServiceProvider getLoadBalancingProviderForNetwork(Network network, Scheme lbScheme) { List lbElements = getElementForServiceInNetwork(network, Service.Lb); @@ -4202,7 +4286,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L assert lbElement instanceof LoadBalancingServiceProvider; return (LoadBalancingServiceProvider)lbElement; } - + + @Override public boolean isNetworkInlineMode(Network network) { NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId()); @@ -4218,47 +4303,50 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return rules.size(); } - @Override + + @Override public boolean isSecondaryIpSetForNic(long nicId) { NicVO nic = _nicDao.findById(nicId); return nic.getSecondaryIp(); } - @Override - public boolean removeVmSecondaryIpsOfNic(long nicId) { - Transaction txn = Transaction.currentTxn(); - txn.start(); - List ipList = _nicSecondaryIpDao.listByNicId(nicId); - if (ipList != null) { - for (NicSecondaryIpVO ip: ipList) { - _nicSecondaryIpDao.remove(ip.getId()); - } - s_logger.debug("Revoving nic secondary ip entry ..."); - } - txn.commit(); - return true; - } - @Override - public String allocatePublicIpForGuestNic(Long networkId, DataCenter dc, Pod pod,Account owner, - String requestedIp) throws InsufficientAddressCapacityException { - PublicIp ip = assignPublicIpAddress(dc.getId(), null, owner, VlanType.DirectAttached, networkId, requestedIp, false); - if (ip == null) { - s_logger.debug("There is no free public ip address"); - return null; - } - Ip ipAddr = ip.getAddress(); - return ipAddr.addr(); - } - - @Override - public NicVO savePlaceholderNic(Network network, String ip4Address, Type vmType) { - NicVO nic = new NicVO(null, null, network.getId(), null); - nic.setIp4Address(ip4Address); - nic.setReservationStrategy(ReservationStrategy.PlaceHolder); - nic.setState(Nic.State.Reserved); - nic.setVmType(vmType); - return _nicDao.persist(nic); + private boolean removeVmSecondaryIpsOfNic(long nicId) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + List ipList = _nicSecondaryIpDao.listByNicId(nicId); + if (ipList != null) { + for (NicSecondaryIpVO ip: ipList) { + _nicSecondaryIpDao.remove(ip.getId()); + } + s_logger.debug("Revoving nic secondary ip entry ..."); + } + txn.commit(); + return true; + } + + + @Override + public String allocatePublicIpForGuestNic(Long networkId, DataCenter dc, Pod pod,Account owner, + String requestedIp) throws InsufficientAddressCapacityException { + PublicIp ip = assignPublicIpAddress(dc.getId(), null, owner, VlanType.DirectAttached, networkId, requestedIp, false); + if (ip == null) { + s_logger.debug("There is no free public ip address"); + return null; } + Ip ipAddr = ip.getAddress(); + return ipAddr.addr(); + } + + + @Override + public NicVO savePlaceholderNic(Network network, String ip4Address, Type vmType) { + NicVO nic = new NicVO(null, null, network.getId(), null); + nic.setIp4Address(ip4Address); + nic.setReservationStrategy(ReservationStrategy.PlaceHolder); + nic.setState(Nic.State.Reserved); + nic.setVmType(vmType); + return _nicDao.persist(nic); + } } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index f880bccb046..4eed735f255 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -2549,7 +2549,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (removeVlan != null){ List tokens = processVlanRange(network,removeVlan); boolean result = removeVlanRange(network, tokens.get(0), tokens.get(1)); - } if (tags != null && tags.size() > 1) { @@ -2795,9 +2794,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { return true; } - private boolean physicalNetworkHasAllocatedVnets(long zoneId, long physicalNetworkId) { - return !_dcDao.listAllocatedVnets(physicalNetworkId).isEmpty(); - } @Override @ActionEvent(eventType = EventTypes.EVENT_PHYSICAL_NETWORK_DELETE, eventDescription = "deleting physical network", async = true) @@ -3758,14 +3754,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } } - - + private boolean getAllowSubdomainAccessGlobal() { return _allowSubdomainNetworkAccess; } - - + @Override public List> listTrafficTypeImplementor(ListTrafficTypeImplementorsCmd cmd) { String type = cmd.getTrafficType(); diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index bc52e9a881c..35f977b5921 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -36,18 +36,12 @@ import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.regex.Pattern; -import java.util.StringTokenizer; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.configuration.*; -import com.cloud.dc.*; -import com.cloud.dc.dao.DcDetailsDao; -import com.cloud.user.*; -import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; @@ -56,13 +50,25 @@ import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationVO; +import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceOwnerType; import com.cloud.configuration.Resource.ResourceType; +import com.cloud.configuration.ResourceCountVO; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterDetailsVO; +import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DcDetailVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.VlanVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DcDetailsDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; import com.cloud.domain.DomainVO; @@ -95,6 +101,11 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.test.IPRangeConfig; +import com.cloud.user.Account; +import com.cloud.user.AccountDetailVO; +import com.cloud.user.AccountDetailsDao; +import com.cloud.user.AccountVO; +import com.cloud.user.User; import com.cloud.user.dao.AccountDao; import com.cloud.utils.PasswordGenerator; import com.cloud.utils.PropertiesUtil; @@ -106,7 +117,6 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.Script; -import com.cloud.uuididentity.dao.IdentityDao; @Component @@ -124,12 +134,10 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio @Inject private DataCenterDao _dataCenterDao; @Inject private NetworkDao _networkDao; @Inject private VlanDao _vlanDao; - private String _domainSuffix; @Inject private DomainDao _domainDao; @Inject private AccountDao _accountDao; @Inject private ResourceCountDao _resourceCountDao; @Inject private NetworkOfferingServiceMapDao _ntwkOfferingServiceMapDao; - @Inject private IdentityDao _identityDao; @Inject private DcDetailsDao _dcDetailsDao; @Inject private ClusterDetailsDao _clusterDetailsDao; @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; @@ -162,9 +170,6 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio // Get init String init = _configDao.getValue("init"); - // Get domain suffix - needed for network creation - _domainSuffix = _configDao.getValue("guest.domain.suffix"); - if (init == null || init.equals("false")) { s_logger.debug("ConfigurationServer is saving default values to the database."); diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index 53c0fa82672..7ab322b3871 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -518,14 +518,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage return false; } - /* (non-Javadoc) - * @see com.cloud.network.NetworkManager#cleanupIpResources(long, long, com.cloud.user.Account) - */ - @Override - public boolean cleanupIpResources(long addrId, long userId, Account caller) { - // TODO Auto-generated method stub - return false; - } /* (non-Javadoc) * @see com.cloud.network.NetworkManager#restartNetwork(java.lang.Long, com.cloud.user.Account, com.cloud.user.User, boolean) @@ -795,15 +787,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage return null; } - /* (non-Javadoc) - * @see com.cloud.network.NetworkManager#assignVpnGatewayIpAddress(long, com.cloud.user.Account, long) - */ - @Override - public PublicIp assignVpnGatewayIpAddress(long dcId, Account owner, long vpcId) - throws InsufficientAddressCapacityException, ConcurrentOperationException { - // TODO Auto-generated method stub - return null; - } /* (non-Javadoc) * @see com.cloud.network.NetworkManager#markPublicIpAsAllocated(com.cloud.network.IPAddressVO) @@ -939,11 +922,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage return null; } - @Override - public boolean removeVmSecondaryIpsOfNic(long nicId) { - // TODO Auto-generated method stub - return false; - } @Override public NicVO savePlaceholderNic(Network network, String ip4Address, Type vmType) { diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index a1ece441863..d46be7c9e22 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -1142,10 +1142,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage return false; } - - - - + /* (non-Javadoc) * @see com.cloud.network.NetworkManager#releaseNic(com.cloud.vm.VirtualMachineProfile, com.cloud.vm.Nic) */ @@ -1156,10 +1153,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage } - - - - /* (non-Javadoc) * @see com.cloud.network.NetworkManager#createNicForVm(com.cloud.network.Network, com.cloud.vm.NicProfile, com.cloud.vm.ReservationContext, com.cloud.vm.VirtualMachineProfileImpl, boolean, boolean) */ @@ -1172,24 +1165,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage return null; } - - - - - /* (non-Javadoc) - * @see com.cloud.network.NetworkManager#assignVpnGatewayIpAddress(long, com.cloud.user.Account, long) - */ - @Override - public PublicIp assignVpnGatewayIpAddress(long dcId, Account owner, long vpcId) - throws InsufficientAddressCapacityException, ConcurrentOperationException { - // TODO Auto-generated method stub - return null; - } - - - - - + /* (non-Javadoc) * @see com.cloud.network.NetworkManager#markPublicIpAsAllocated(com.cloud.network.IPAddressVO) */ @@ -1200,9 +1176,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage } - - - /* (non-Javadoc) * @see com.cloud.network.NetworkManager#assignDedicateIpAddress(com.cloud.user.Account, java.lang.Long, java.lang.Long, long, boolean) */ @@ -1240,22 +1213,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage } - - - - /* (non-Javadoc) - * @see com.cloud.network.NetworkManager#cleanupIpResources(long, long, com.cloud.user.Account) - */ - @Override - public boolean cleanupIpResources(long addrId, long userId, Account caller) { - // TODO Auto-generated method stub - return false; - } - - - - - /* (non-Javadoc) * @see com.cloud.network.NetworkManager#restartNetwork(java.lang.Long, com.cloud.user.Account, com.cloud.user.User, boolean) */ @@ -1435,13 +1392,6 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage } - @Override - public boolean removeVmSecondaryIpsOfNic(long nicId) { - // TODO Auto-generated method stub - return false; - } - - @Override public NicVO savePlaceholderNic(Network network, String ip4Address, Type vmType) { // TODO Auto-generated method stub From 560fa52fdcef75df3c8baed69c8610659c1fd1a5 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 28 May 2013 15:57:14 -0700 Subject: [PATCH 159/412] CLOUDSTACK-2678: portable IP ranges - Associate IP Address - pop up a dialog that have cross zone dropdown (yes/no) and send selected option to API call. --- ui/scripts/network.js | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 0ffaa1dce2f..d66ec839dd6 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -1755,18 +1755,36 @@ } }, messages: { + /* confirm: function(args) { if(args.context.vpc) return 'message.acquire.new.ip.vpc'; else return 'message.acquire.new.ip'; }, + */ notification: function(args) { return 'label.acquire.new.ip'; } - }, + }, + createForm: { + title: 'label.acquire.new.ip', + fields: { + isportable: { + label: 'label.cross.zones', + select: function(args) { + var items = []; + items.push({ id: "false", description: _l('label.no') }); + items.push({ id: "true", description: _l('label.yes') }); + args.response.success({data: items}); + } + } + } + }, action: function(args) { - var dataObj = {}; + var dataObj = { + isportable: args.data.isportable + }; if('vpc' in args.context) { //from VPC section $.extend(dataObj, { vpcid: args.context.vpc[0].id @@ -2299,6 +2317,12 @@ ipaddress: { label: 'label.ip' } }, { + isportable: { + label: 'label.cross.zones', + converter: function(data) { + return data ? _l('label.yes') : _l('label.no'); + } + }, id: { label: 'label.id' }, associatednetworkid: { label: 'label.associated.network.id' }, networkname: { label: 'label.associated.network' }, From 6e55776795d2bf3c71f02ee8a38ad29e5d303f9d Mon Sep 17 00:00:00 2001 From: Min Chen Date: Tue, 28 May 2013 15:41:02 -0700 Subject: [PATCH 160/412] CLOUDSTACK-2331: Failed to display exception object information in case of CloudRuntimeException. --- .../org/apache/cloudstack/api/BaseCmd.java | 2 +- .../cloudstack/api/ServerApiException.java | 3 +- .../user/account/AddAccountToProjectCmd.java | 2 +- .../snapshot/CreateSnapshotPolicyCmd.java | 2 +- .../user/template/CreateTemplateCmd.java | 2 +- .../api/response/ExceptionResponse.java | 12 ++- server/src/com/cloud/api/ApiServer.java | 14 +++- .../com/cloud/api/query/QueryManagerImpl.java | 2 +- .../api/response/ApiResponseSerializer.java | 23 ++++-- .../com/cloud/network/NetworkManagerImpl.java | 22 ++--- .../com/cloud/network/NetworkModelImpl.java | 19 ++++- .../com/cloud/network/NetworkServiceImpl.java | 82 +++++++++---------- .../lb/LoadBalancingRulesManagerImpl.java | 27 ++++-- .../cloud/network/rules/RulesManagerImpl.java | 12 +-- .../com/cloud/network/vpc/VpcManagerImpl.java | 16 ++-- .../cloud/projects/ProjectManagerImpl.java | 34 +++++--- .../cloud/resource/ResourceManagerImpl.java | 26 +++--- .../cloud/server/ManagementServerImpl.java | 65 ++++++++------- .../src/com/cloud/user/DomainManagerImpl.java | 10 +-- .../src/com/cloud/vm/UserVmManagerImpl.java | 34 ++++---- .../ApplicationLoadBalancerManagerImpl.java | 2 +- .../exception/CloudRuntimeException.java | 24 +++--- .../utils/exception/ExceptionProxyObject.java | 50 +++++++++++ 23 files changed, 299 insertions(+), 186 deletions(-) create mode 100644 utils/src/com/cloud/utils/exception/ExceptionProxyObject.java diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index 9ac110cfb1b..219e7c79e6f 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -521,7 +521,7 @@ public abstract class BaseCmd { return project.getProjectAccountId(); } else { PermissionDeniedException ex = new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() + " as it's no longer active"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); throw ex; } } else { diff --git a/api/src/org/apache/cloudstack/api/ServerApiException.java b/api/src/org/apache/cloudstack/api/ServerApiException.java index 4b0fae58548..1a740d56c90 100644 --- a/api/src/org/apache/cloudstack/api/ServerApiException.java +++ b/api/src/org/apache/cloudstack/api/ServerApiException.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import com.cloud.exception.CloudException; import com.cloud.utils.exception.CSExceptionErrorCode; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExceptionProxyObject; @SuppressWarnings("serial") public class ServerApiException extends CloudRuntimeException { @@ -45,7 +46,7 @@ public class ServerApiException extends CloudRuntimeException { _description = description; if (cause instanceof CloudRuntimeException) { CloudRuntimeException rt = (CloudRuntimeException) cause; - ArrayList idList = rt.getIdProxyList(); + ArrayList idList = rt.getIdProxyList(); if (idList != null) { for (int i = 0; i < idList.size(); i++) { addProxyObject(idList.get(i)); diff --git a/api/src/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java b/api/src/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java index ebc22723585..58735f281e9 100644 --- a/api/src/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java @@ -101,7 +101,7 @@ public class AddAccountToProjectCmd extends BaseAsyncCmd { //verify input parameters if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id"); - ex.addProxyObject(project, getProjectId(), "projectId"); + ex.addProxyObject(getProjectId().toString(), "projectId"); throw ex; } diff --git a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java index 5a9ea2a073d..14c2ee11c80 100644 --- a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java @@ -112,7 +112,7 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { Project project = _projectService.findByProjectAccountId(volume.getAccountId()); if (project.getState() != Project.State.Active) { PermissionDeniedException ex = new PermissionDeniedException("Can't add resources to the specified project id in state=" + project.getState() + " as it's no longer active"); - ex.addProxyObject(project, project.getId(), "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); throw ex; } } else if (account.getState() == Account.State.disabled) { diff --git a/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java index 6a482ac1ff2..6aa60aca1fc 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java @@ -207,7 +207,7 @@ import java.util.Map; Project project = _projectService.findByProjectAccountId(accountId); if (project.getState() != Project.State.Active) { PermissionDeniedException ex = new PermissionDeniedException("Can't add resources to the specified project id in state=" + project.getState() + " as it's no longer active"); - ex.addProxyObject(project, project.getId(), "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); } } else if (account.getState() == Account.State.disabled) { throw new PermissionDeniedException("The owner of template is disabled: " + account); diff --git a/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java b/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java index 3afd516e075..830cf007cd0 100644 --- a/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java @@ -17,16 +17,18 @@ package org.apache.cloudstack.api.response; import java.util.ArrayList; +import java.util.List; import org.apache.cloudstack.api.BaseResponse; import com.cloud.serializer.Param; +import com.cloud.utils.exception.ExceptionProxyObject; import com.google.gson.annotations.SerializedName; public class ExceptionResponse extends BaseResponse { @SerializedName("uuidList") @Param(description="List of uuids associated with this error") - private ArrayList idList; + private List idList; @SerializedName("errorcode") @Param(description="numeric code associated with this error") private Integer errorCode; @@ -37,6 +39,10 @@ public class ExceptionResponse extends BaseResponse { @SerializedName("errortext") @Param(description="the text associated with this error") private String errorText = "Command failed due to Internal Server Error"; + public ExceptionResponse(){ + idList = new ArrayList(); + } + public Integer getErrorCode() { return errorCode; } @@ -53,12 +59,12 @@ public class ExceptionResponse extends BaseResponse { this.errorText = errorText; } - public void addProxyObject(String id) { + public void addProxyObject(ExceptionProxyObject id) { idList.add(id); return; } - public ArrayList getIdProxyList() { + public List getIdProxyList() { return idList; } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 497be50a766..e748a35a747 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -148,6 +148,7 @@ import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExceptionProxyObject; @Component public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiServerService { @@ -387,9 +388,16 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex); } catch (PermissionDeniedException ex){ - ArrayList idList = ex.getIdProxyList(); + ArrayList idList = ex.getIdProxyList(); if (idList != null) { - s_logger.info("PermissionDenied: " + ex.getMessage() + " on uuids: [" + StringUtils.listToCsvTags(idList) + "]"); + StringBuffer buf = new StringBuffer(); + for (ExceptionProxyObject obj : idList){ + buf.append(obj.getDescription()); + buf.append(":"); + buf.append(obj.getUuid()); + buf.append(" "); + } + s_logger.info("PermissionDenied: " + ex.getMessage() + " on objs: [" + buf.toString() + "]"); } else { s_logger.info("PermissionDenied: " + ex.getMessage()); } @@ -1067,7 +1075,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer apiResponse.setErrorCode(ex.getErrorCode().getHttpCode()); apiResponse.setErrorText(ex.getDescription()); apiResponse.setResponseName(responseName); - ArrayList idList = ex.getIdProxyList(); + ArrayList idList = ex.getIdProxyList(); if (idList != null) { for (int i=0; i < idList.size(); i++) { apiResponse.addProxyObject(idList.get(i)); diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index c586a7b19f2..5a25732bca0 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -2176,7 +2176,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { UserVmVO vmInstance = _userVmDao.findById(vmId); if ((vmInstance == null) || (vmInstance.getRemoved() != null)) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a virtual machine with specified id"); - ex.addProxyObject(vmInstance, vmId, "vmId"); + ex.addProxyObject(vmId.toString(), "vmId"); throw ex; } diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index 965660a52cc..d2e5130931d 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -21,8 +21,10 @@ import com.cloud.api.ApiResponseGsonHelper; import com.cloud.api.ApiServer; import com.cloud.utils.encoding.URLEncoder; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExceptionProxyObject; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; + import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ResponseObject; @@ -215,22 +217,27 @@ public class ApiResponseSerializer { subObj.setObjectName(serializedName.value()); } serializeResponseObjXML(sb, subObj); - } else { - // Only exception reponses carry a list of uuid - // strings. + } else if (value instanceof ExceptionProxyObject) { + // Only exception reponses carry a list of + // ExceptionProxyObject objects. + ExceptionProxyObject idProxy = (ExceptionProxyObject) value; // If this is the first IdentityProxy field // encountered, put in a uuidList tag. if (!usedUuidList) { - sb.append("<").append(serializedName.value()).append(">"); + sb.append("<" + serializedName.value() + ">"); usedUuidList = true; } - sb.append("").append(value).append(""); - // We have removed uuid property field due to removal of IdentityProxy class. + sb.append("<" + "uuid" + ">" + idProxy.getUuid() + ""); + // Append the new descriptive property also. + String idFieldName = idProxy.getDescription(); + if (idFieldName != null) { + sb.append("<" + "uuidProperty" + ">" + idFieldName + ""); + } } } if (usedUuidList) { - // close the uuidList. - sb.append(""); + // close the uuidList. + sb.append(""); } } else if (fieldValue instanceof Date) { sb.append("<").append(serializedName.value()).append(">").append(BaseCmd.getDateString((Date) fieldValue)). diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index fda0773147c..cc149a542fd 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -737,7 +737,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L // zone is of type DataCenter. See DataCenterVO.java. PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation, " + "Zone is currently disabled"); - ex.addProxyObject("data_center", zone.getId(), "zoneId"); + ex.addProxyObject(zone.getUuid(), "zoneId"); throw ex; } @@ -1483,8 +1483,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L if (errorIfAlreadySetup) { InvalidParameterValueException ex = new InvalidParameterValueException("Found existing network configuration (with specified id) for offering (with specified id)"); - ex.addProxyObject(offering, offering.getId(), "offeringId"); - ex.addProxyObject(configs.get(0), configs.get(0).getId(), "networkConfigId"); + ex.addProxyObject(offering.getUuid(), "offeringId"); + ex.addProxyObject(configs.get(0).getUuid(), "networkConfigId"); throw ex; } else { return configs; @@ -1500,8 +1500,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L if (errorIfAlreadySetup) { InvalidParameterValueException ex = new InvalidParameterValueException("Found existing network configuration (with specified id) for offering (with specified id)"); - ex.addProxyObject(offering, offering.getId(), "offeringId"); - ex.addProxyObject(configs.get(0), configs.get(0).getId(), "networkConfigId"); + ex.addProxyObject(offering.getUuid(), "offeringId"); + ex.addProxyObject(configs.get(0).getUuid(), "networkConfigId"); throw ex; } else { return configs; @@ -1553,7 +1553,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L if (networks.size() < 1) { // see networkOfferingVO.java CloudRuntimeException ex = new CloudRuntimeException("Unable to convert network offering with specified id to network profile"); - ex.addProxyObject(offering, offering.getId(), "offeringId"); + ex.addProxyObject(offering.getUuid(), "offeringId"); throw ex; } @@ -1932,7 +1932,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L if (!element.implement(network, offering, dest, context)) { CloudRuntimeException ex = new CloudRuntimeException("Failed to implement provider " + element.getProvider().getName() + " for network with specified id"); - ex.addProxyObject(network, network.getId(), "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } } @@ -2330,7 +2330,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L if (ntwkOff.getState() != NetworkOffering.State.Enabled) { // see NetworkOfferingVO InvalidParameterValueException ex = new InvalidParameterValueException("Can't use specified network offering id as its stat is not " + NetworkOffering.State.Enabled); - ex.addProxyObject(ntwkOff, ntwkOff.getId(), "networkOfferingId"); + ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId"); throw ex; } @@ -2339,7 +2339,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L // see PhysicalNetworkVO.java InvalidParameterValueException ex = new InvalidParameterValueException("Specified physical network id is" + " in incorrect state:" + pNtwk.getState()); - ex.addProxyObject("physical_network", pNtwk.getId(), "physicalNetworkId"); + ex.addProxyObject(pNtwk.getUuid(), "physicalNetworkId"); throw ex; } @@ -2992,7 +2992,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L NetworkVO network = _networksDao.findById(networkId); if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Network with specified id doesn't exist"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(String.valueOf(networkId), "networkId"); throw ex; } @@ -3241,7 +3241,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L " network provision due to ", ex); CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id)" + " elements and resources as a part of network provision for persistent network"); - e.addProxyObject(guestNetwork, guestNetwork.getId(), "networkId"); + e.addProxyObject(guestNetwork.getUuid(), "networkId"); throw e; } } diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index 010cb9d5c54..f6bd646a91d 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -37,9 +37,11 @@ import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.api.ApiDBUtils; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; import com.cloud.dc.Vlan.VlanType; @@ -283,7 +285,12 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { } else { CloudRuntimeException ex = new CloudRuntimeException("Multiple generic soure NAT IPs provided for network"); // see the IPAddressVO.java class. - ex.addProxyObject("user_ip_address", ip.getAssociatedWithNetworkId(), "networkId"); + IPAddressVO ipAddr = ApiDBUtils.findIpAddressById(ip.getAssociatedWithNetworkId()); + String ipAddrUuid = ip.getAssociatedWithNetworkId().toString(); + if ( ipAddr != null){ + ipAddrUuid = ipAddr.getUuid(); + } + ex.addProxyObject(ipAddrUuid, "networkId"); throw ex; } } @@ -1129,17 +1136,21 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { public PhysicalNetwork getDefaultPhysicalNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) { List networkList = _physicalNetworkDao.listByZoneAndTrafficType(zoneId, trafficType); + DataCenter dc = ApiDBUtils.findZoneById(zoneId); + String dcUuid = String.valueOf(zoneId); + if ( dc != null ){ + dcUuid = dc.getUuid(); + } if (networkList.isEmpty()) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find the default physical network with traffic=" + trafficType + " in the specified zone id"); - // Since we don't have a DataCenterVO object at our disposal, we just set the table name that the zoneId's corresponding uuid is looked up from, manually. - ex.addProxyObject("data_center", zoneId, "zoneId"); + ex.addProxyObject(dcUuid, "zoneId"); throw ex; } if (networkList.size() > 1) { InvalidParameterValueException ex = new InvalidParameterValueException("More than one physical networks exist in zone id=" + zoneId + " with traffic type=" + trafficType); - ex.addProxyObject("data_center", zoneId, "zoneId"); + ex.addProxyObject(dcUuid, "zoneId"); throw ex; } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 4eed735f255..98992846c4e 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -65,6 +65,7 @@ import org.springframework.stereotype.Component; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.bouncycastle.util.IPAddress; +import com.cloud.api.ApiDBUtils; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; @@ -317,7 +318,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } else { CloudRuntimeException ex = new CloudRuntimeException("Multiple generic soure NAT IPs provided for network"); // see the IPAddressVO.java class. - ex.addProxyObject("user_ip_address", ip.getAssociatedWithNetworkId(), "networkId"); + IPAddressVO ipAddr = ApiDBUtils.findIpAddressById(ip.getAssociatedWithNetworkId()); + String ipAddrUuid = ip.getAssociatedWithNetworkId().toString(); + if ( ipAddr != null ){ + ipAddrUuid = ipAddr.getUuid(); + } + ex.addProxyObject(ipAddrUuid, "networkId"); throw ex; } } @@ -877,7 +883,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // don't allow releasing system ip address if (ipVO.getSystem()) { InvalidParameterValueException ex = new InvalidParameterValueException("Can't release system IP address with specified id"); - ex.addProxyObject(ipVO, ipVO.getId(), "systemIpAddrId"); + ex.addProxyObject(ipVO.getUuid(), "systemIpAddrId"); throw ex; } @@ -994,12 +1000,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (ntwkOff == null || ntwkOff.isSystemOnly()) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering by specified id"); if (ntwkOff != null) { - ex.addProxyObject(ntwkOff, networkOfferingId, "networkOfferingId"); - // Get the VO object's table name. - String tablename = AnnotationHelper.getTableName(ntwkOff); - if (tablename != null) { - ex.addProxyObject(tablename, networkOfferingId, "networkOfferingId"); - } + ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId"); } throw ex; } @@ -1033,7 +1034,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // See DataCenterVO.java PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled"); - ex.addProxyObject(zone, zoneId, "zoneId"); + ex.addProxyObject(zone.getUuid(), "zoneId"); throw ex; } @@ -1255,13 +1256,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // Can add vlan range only to the network which allows it if (createVlan && !ntwkOff.getSpecifyIpRanges()) { InvalidParameterValueException ex = new InvalidParameterValueException("Network offering with specified id doesn't support adding multiple ip ranges"); - ex.addProxyObject(ntwkOff, ntwkOff.getId(), "networkOfferingId"); - String tablename = AnnotationHelper.getTableName(ntwkOff); - if (tablename != null) { - ex.addProxyObject(tablename, ntwkOff.getId(), "networkOfferingId"); - } else { - s_logger.info("\nCould not retrieve table name (annotation) from " + tablename + " VO proxy object\n"); - } + ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId"); throw ex; } @@ -1346,7 +1341,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } catch (ResourceUnavailableException ex) { s_logger.warn("Failed to implement persistent guest network " + network + "due to ", ex); CloudRuntimeException e = new CloudRuntimeException("Failed to implement persistent guest network"); - e.addProxyObject(network, network.getId(), "networkId"); + e.addProxyObject(network.getUuid(), "networkId"); throw e; } } @@ -1432,7 +1427,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) { // getProject() returns type ProjectVO. InvalidParameterValueException ex = new InvalidParameterValueException("Account " + caller + " cannot access specified project id"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); throw ex; } permittedAccounts.add(project.getProjectAccountId()); @@ -1757,14 +1752,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // see NetworkVO.java InvalidParameterValueException ex = new InvalidParameterValueException("unable to find network with specified id"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(String.valueOf(networkId), "networkId"); throw ex; } // don't allow to delete system network if (isNetworkSystem(network)) { InvalidParameterValueException ex = new InvalidParameterValueException("Network with specified id is system and can't be removed"); - ex.addProxyObject(network, network.getId(), "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } @@ -1793,7 +1788,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { NetworkVO network = _networksDao.findById(networkId); if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Network with specified id doesn't exist"); - ex.addProxyObject("networks", networkId, "networkId"); + ex.addProxyObject(networkId.toString(), "networkId"); throw ex; } @@ -1936,7 +1931,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (network == null) { // see NetworkVO.java InvalidParameterValueException ex = new InvalidParameterValueException("Specified network id doesn't exist in the system"); - ex.addProxyObject("networks", networkId, "networkId"); + ex.addProxyObject(String.valueOf(networkId), "networkId"); throw ex; } @@ -1992,14 +1987,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (networkOfferingId != null) { if (networkOffering == null || networkOffering.isSystemOnly()) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering with specified id"); - ex.addProxyObject(networkOffering, networkOfferingId, "networkOfferingId"); + ex.addProxyObject(networkOfferingId.toString(), "networkOfferingId"); throw ex; } // network offering should be in Enabled state if (networkOffering.getState() != NetworkOffering.State.Enabled) { InvalidParameterValueException ex = new InvalidParameterValueException("Network offering with specified id is not in " + NetworkOffering.State.Enabled + " state, can't upgrade to it"); - ex.addProxyObject(networkOffering, networkOfferingId, "networkOfferingId"); + ex.addProxyObject(networkOffering.getUuid(), "networkOfferingId"); throw ex; } //can't update from vpc to non-vpc network offering @@ -2021,7 +2016,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (changeCidr) { if (!checkForNonStoppedVmInNetwork(network.getId())) { InvalidParameterValueException ex = new InvalidParameterValueException("All user vm of network of specified id should be stopped before changing CIDR!"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } } @@ -2154,7 +2149,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (!_networkMgr.shutdownNetworkElementsAndResources(context, true, network)) { s_logger.warn("Failed to shutdown the network elements and resources as a part of network restart: " + network); CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network elements and resources as a part of update to network of specified id"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } } else { @@ -2173,13 +2168,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (!_networkMgr.shutdownNetwork(network.getId(), context, true)) { s_logger.warn("Failed to shutdown the network as a part of update to network with specified id"); CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network as a part of update of specified network id"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } } } else { CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network elements and resources as a part of update to network with specified id; network is in wrong state: " + network.getState()); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } } @@ -2190,7 +2185,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { boolean validStateToImplement = (networkState == Network.State.Implemented || networkState == Network.State.Setup || networkState == Network.State.Allocated); if (restartNetwork && !validStateToImplement) { CloudRuntimeException ex = new CloudRuntimeException("Failed to implement the network elements and resources as a part of update to network with specified id; network is in wrong state: " + networkState); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } @@ -2241,7 +2236,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } catch (Exception ex) { s_logger.warn("Failed to implement network " + network + " elements and resources as a part of network update due to ", ex); CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id) elements and resources as a part of network update"); - e.addProxyObject(network, networkId, "networkId"); + e.addProxyObject(network.getUuid(), "networkId"); throw e; } } @@ -2259,7 +2254,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { "f network update due to ", ex); CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified" + " id) elements and resources as a part of network update"); - e.addProxyObject(network, networkId, "networkId"); + e.addProxyObject(network.getUuid(), "networkId"); throw e; } } @@ -2527,7 +2522,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { PhysicalNetworkVO network = _physicalNetworkDao.findById(id); if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Physical Network with specified id doesn't exist in the system"); - ex.addProxyObject(network, id, "physicalNetworkId"); + ex.addProxyObject(id.toString(), "physicalNetworkId"); throw ex; } @@ -2535,7 +2530,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { DataCenter zone = _dcDao.findById(network.getDataCenterId()); if (zone == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Zone with id=" + network.getDataCenterId() + " doesn't exist in the system"); - ex.addProxyObject(zone, network.getDataCenterId(), "dataCenterId"); + ex.addProxyObject(String.valueOf(network.getDataCenterId()), "dataCenterId"); throw ex; } if (newVnetRangeString != null) { @@ -2804,7 +2799,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { PhysicalNetworkVO pNetwork = _physicalNetworkDao.findById(physicalNetworkId); if (pNetwork == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Physical Network with specified id doesn't exist in the system"); - ex.addProxyObject(pNetwork, physicalNetworkId, "physicalNetworkId"); + ex.addProxyObject(physicalNetworkId.toString(), "physicalNetworkId"); throw ex; } @@ -3100,7 +3095,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { Account account = _accountDao.findActiveAccount(accountName, domainId); if (account == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account " + accountName); - ex.addProxyObject("domain", domainId, "domainId"); + DomainVO domain = ApiDBUtils.findDomainById(domainId); + String domainUuid = domainId.toString(); + if (domain != null ){ + domainUuid = domain.getUuid(); + } + ex.addProxyObject(domainUuid, "domainId"); throw ex; } else { accountId = account.getId(); @@ -3112,7 +3112,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { Project project = _projectMgr.getProject(projectId); if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project by id " + projectId); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(projectId.toString(), "projectId"); throw ex; } accountId = project.getProjectAccountId(); @@ -3210,7 +3210,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { PhysicalNetworkVO network = _physicalNetworkDao.findById(physicalNetworkId); if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Physical Network with specified id doesn't exist in the system"); - ex.addProxyObject(network, physicalNetworkId, "physicalNetworkId"); + ex.addProxyObject(physicalNetworkId.toString(), "physicalNetworkId"); throw ex; } @@ -3219,7 +3219,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { PhysicalNetworkVO destNetwork = _physicalNetworkDao.findById(destinationPhysicalNetworkId); if (destNetwork == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Destination Physical Network with specified id doesn't exist in the system"); - ex.addProxyObject(destNetwork, destinationPhysicalNetworkId, "destinationPhysicalNetworkId"); + ex.addProxyObject(destinationPhysicalNetworkId.toString(), "destinationPhysicalNetworkId"); throw ex; } } @@ -3646,7 +3646,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { PhysicalNetworkVO network = _physicalNetworkDao.findById(physicalNetworkId); if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Physical Network with specified id doesn't exist in the system"); - ex.addProxyObject(network, physicalNetworkId, "physicalNetworkId"); + ex.addProxyObject(physicalNetworkId.toString(), "physicalNetworkId"); throw ex; } @@ -3818,7 +3818,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (pNtwk == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a physical network" + " having the given id"); - ex.addProxyObject("physical_network", physicalNetworkId, "physicalNetworkId"); + ex.addProxyObject(String.valueOf(physicalNetworkId), "physicalNetworkId"); throw ex; } @@ -3912,7 +3912,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (userVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Virtual mahine id does not exist"); - ex.addProxyObject(userVm, vmId, "vmId"); + ex.addProxyObject(vmId.toString(), "vmId"); throw ex; } _accountMgr.checkAccess(caller, null, true, userVm); diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 1d7b3121158..633357e4bdd 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -947,7 +947,11 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements UserVm vm = _vmDao.findById(instanceId); if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { InvalidParameterValueException ex = new InvalidParameterValueException("Invalid instance id specified"); - ex.addProxyObject(vm, instanceId, "instanceId"); + if (vm == null) { + ex.addProxyObject(instanceId.toString(), "instanceId"); + } else { + ex.addProxyObject(vm.getUuid(), "instanceId"); + } throw ex; } @@ -971,7 +975,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements if (nicInSameNetwork == null) { InvalidParameterValueException ex = new InvalidParameterValueException("VM " + instanceId + " cannot be added because it doesn't belong in the same network."); - ex.addProxyObject(vm, instanceId, "instanceId"); + ex.addProxyObject(vm.getUuid(), "instanceId"); throw ex; } @@ -1024,7 +1028,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements if (!success) { CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms " + instanceIds); - ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); // TBD: Also pack in the instanceIds in the exception using the // right VO object or table name. throw ex; @@ -1075,7 +1079,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds); CloudRuntimeException ex = new CloudRuntimeException( "Failed to remove specified load balancer rule id for vms " + instanceIds); - ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); throw ex; } success = true; @@ -1098,7 +1102,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements if (!success) { CloudRuntimeException ex = new CloudRuntimeException( "Failed to remove specified load balancer rule id for vms " + instanceIds); - ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); throw ex; } return success; @@ -1368,12 +1372,17 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements if (ipAddr == null || !ipAddr.readyToUse()) { InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to create load balancer rule, invalid IP address id specified"); - ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); + if (ipAddr == null){ + ex.addProxyObject(String.valueOf(sourceIpId), "sourceIpId"); + } + else{ + ex.addProxyObject(ipAddr.getUuid(), "sourceIpId"); + } throw ex; } else if (ipAddr.isOneToOneNat()) { InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to create load balancer rule; specified sourceip id has static nat enabled"); - ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); + ex.addProxyObject(ipAddr.getUuid(), "sourceIpId"); throw ex; } @@ -1384,7 +1393,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements if (networkId == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to create load balancer rule ; specified sourceip id is not associated with any network"); - ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); + ex.addProxyObject(ipAddr.getUuid(), "sourceIpId"); throw ex; } @@ -2083,7 +2092,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Lb)) { InvalidParameterValueException ex = new InvalidParameterValueException( "LB service is not supported in specified network id"); - ex.addProxyObject(network, network.getId(), "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 3c95cef440f..dd5f99ba574 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -1257,14 +1257,14 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules if (ipAddress.getSystem()) { InvalidParameterValueException ex = new InvalidParameterValueException("Can't disable static nat for system IP address with specified id"); - ex.addProxyObject(ipAddress, ipId, "ipId"); + ex.addProxyObject(ipAddress.getUuid(), "ipId"); throw ex; } Long vmId = ipAddress.getAssociatedWithVmId(); if (vmId == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Specified IP address id is not associated with any vm Id"); - ex.addProxyObject(ipAddress, ipId, "ipId"); + ex.addProxyObject(ipAddress.getUuid(), "ipId"); throw ex; } @@ -1292,7 +1292,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules if (!ipAddress.isOneToOneNat()) { InvalidParameterValueException ex = new InvalidParameterValueException("One to one nat is not enabled for the specified ip id"); - ex.addProxyObject(ipAddress, ipId, "ipId"); + ex.addProxyObject(ipAddress.getUuid(), "ipId"); throw ex; } @@ -1353,14 +1353,14 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules if (ip == null || !ip.isOneToOneNat() || ip.getAssociatedWithVmId() == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Source ip address of the specified firewall rule id is not static nat enabled"); - ex.addProxyObject(ruleVO, rule.getId(), "ruleId"); + ex.addProxyObject(ruleVO.getUuid(), "ruleId"); throw ex; } String dstIp = ip.getVmIp(); if (dstIp == null) { InvalidParameterValueException ex = new InvalidParameterValueException("VM ip address of the specified public ip is not set "); - ex.addProxyObject(ruleVO, rule.getId(), "ruleId"); + ex.addProxyObject(ruleVO.getUuid(), "ruleId"); throw ex; } @@ -1432,7 +1432,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules Network network = _networkModel.getNetwork(networkId); if (network == null) { CloudRuntimeException ex = new CloudRuntimeException("Unable to find an ip address to map to specified vm id"); - ex.addProxyObject(vm, vm.getId(), "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 15e1539ae7d..380a95e1882 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -587,7 +587,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (vpcOff == null || vpcOff.getState() != State.Enabled) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find vpc offering in " + State.Enabled + " state by specified id"); - ex.addProxyObject("vpc_offerings", vpcOffId, "vpcOfferingId"); + if (vpcOff == null) { + ex.addProxyObject(String.valueOf(vpcOffId), "vpcOfferingId"); + } else { + ex.addProxyObject(vpcOff.getUuid(), "vpcOfferingId"); + } throw ex; } @@ -596,7 +600,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // See DataCenterVO.java PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled"); - ex.addProxyObject("data_center", zone.getId(), "zoneId"); + ex.addProxyObject(zone.getUuid(), "zoneId"); throw ex; } @@ -943,7 +947,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified"); - ex.addProxyObject("vpc", vpcId, "VPC"); + ex.addProxyObject(String.valueOf(vpcId), "VPC"); throw ex; } @@ -1275,7 +1279,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified"); - ex.addProxyObject("vpc", vpcId, "VPC"); + ex.addProxyObject(String.valueOf(vpcId), "VPC"); throw ex; } @@ -1354,7 +1358,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified"); - ex.addProxyObject("vpc", vpcId, "VPC"); + ex.addProxyObject(String.valueOf(vpcId), "VPC"); throw ex; } @@ -2016,7 +2020,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC "); - ex.addProxyObject("vpc", vpcId, "VPC"); + ex.addProxyObject(String.valueOf(vpcId), "VPC"); throw ex; } _accountMgr.checkAccess(caller, null, false, vpc); diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java index de4f3ccd11b..7ab385f2de4 100755 --- a/server/src/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/com/cloud/projects/ProjectManagerImpl.java @@ -43,6 +43,7 @@ import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.api.ApiDBUtils; import com.cloud.api.query.dao.ProjectAccountJoinDao; import com.cloud.api.query.dao.ProjectInvitationJoinDao; import com.cloud.api.query.dao.ProjectJoinDao; @@ -50,6 +51,7 @@ import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; @@ -508,14 +510,14 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(String.valueOf(projectId), "projectId"); throw ex; } //User can be added to Active project only if (project.getState() != Project.State.Active) { InvalidParameterValueException ex = new InvalidParameterValueException("Can't add account to the specified project id in state=" + project.getState() + " as it's no longer active"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(project.getUuid(),"projectId"); throw ex; } @@ -525,8 +527,12 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); if (account == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account name=" + accountName + " in specified domain id"); - // We don't have a DomainVO object with us, so just pass the tablename "domain" manually. - ex.addProxyObject("domain", project.getDomainId(), "domainId"); + DomainVO domain = ApiDBUtils.findDomainById(project.getDomainId()); + String domainUuid = String.valueOf(project.getDomainId()); + if ( domain != null ){ + domainUuid = domain.getUuid(); + } + ex.addProxyObject(domainUuid, "domainId"); throw ex; } @@ -590,7 +596,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(String.valueOf(projectId), "projectId"); throw ex; } @@ -598,8 +604,12 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); if (account == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId()); - // Since we don't have a domainVO object, pass the table name manually. - ex.addProxyObject("domain", project.getDomainId(), "domainId"); + DomainVO domain = ApiDBUtils.findDomainById(project.getDomainId()); + String domainUuid = String.valueOf(project.getDomainId()); + if ( domain != null ){ + domainUuid = domain.getUuid(); + } + ex.addProxyObject(domainUuid, "domainId"); } //verify permissions @@ -610,14 +620,14 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { if (projectAccount == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Account " + accountName + " is not assigned to the project with specified id"); // Use the projectVO object and not the projectAccount object to inject the projectId. - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); throw ex; } //can't remove the owner of the project if (projectAccount.getAccountRole() == Role.Admin) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to delete account " + accountName + " from the project with specified id as the account is the owner of the project"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); throw ex; } @@ -791,7 +801,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(String.valueOf(projectId), "projectId"); throw ex; } @@ -833,7 +843,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { //verify input parameters if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project with specified id"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(String.valueOf(projectId), "projectId"); throw ex; } @@ -844,7 +854,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { return _projectDao.findById(projectId); } else { CloudRuntimeException ex = new CloudRuntimeException("Failed to suspend project with specified id"); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(project.getUuid(), "projectId"); throw ex; } diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 13780d2756b..f767b6810a4 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -391,7 +391,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, if (zone == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Can't find zone by the id specified"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(String.valueOf(dcId), "dcId"); throw ex; } @@ -400,7 +400,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, && !_accountMgr.isRootAdmin(account.getType())) { PermissionDeniedException ex = new PermissionDeniedException( "Cannot perform this operation, Zone with specified id is currently disabled"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(zone.getUuid(), "dcId"); throw ex; } @@ -419,8 +419,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, if (!Long.valueOf(pod.getDataCenterId()).equals(dcId)) { InvalidParameterValueException ex = new InvalidParameterValueException( "Pod with specified id doesn't belong to the zone " + dcId); - ex.addProxyObject(pod, podId, "podId"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(pod.getUuid(), "podId"); + ex.addProxyObject(zone.getUuid(), "dcId"); throw ex; } @@ -504,8 +504,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, "Unable to create cluster " + clusterName + " in pod and data center with specified ids", e); // Get the pod VO object's table name. - ex.addProxyObject(pod, podId, "podId"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(pod.getUuid(), "podId"); + ex.addProxyObject(zone.getUuid(), "dcId"); throw ex; } clusterId = cluster.getId(); @@ -639,7 +639,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, if (cluster == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "can not find cluster for specified clusterId"); - ex.addProxyObject(cluster, clusterId, "clusterId"); + ex.addProxyObject(clusterId.toString(), "clusterId"); throw ex; } else { if (cluster.getGuid() == null) { @@ -647,7 +647,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, if (!hosts.isEmpty()) { CloudRuntimeException ex = new CloudRuntimeException( "Guid is not updated for cluster with specified cluster id; need to wait for hosts in this cluster to come up"); - ex.addProxyObject(cluster, clusterId, "clusterId"); + ex.addProxyObject(cluster.getUuid(), "clusterId"); throw ex; } } @@ -703,7 +703,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, && !_accountMgr.isRootAdmin(account.getType())) { PermissionDeniedException ex = new PermissionDeniedException( "Cannot perform this operation, Zone with specified id is currently disabled"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(zone.getUuid(), "dcId"); throw ex; } @@ -721,8 +721,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, + podId + " doesn't belong to the zone with specified zoneId" + dcId); - ex.addProxyObject(pod, podId, "podId"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(pod.getUuid(), "podId"); + ex.addProxyObject(zone.getUuid(), "dcId"); throw ex; } } @@ -792,8 +792,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, + clusterName + " in pod with specified podId and data center with specified dcID", e); - ex.addProxyObject(pod, podId, "podId"); - ex.addProxyObject(zone, dcId, "dcId"); + ex.addProxyObject(pod.getUuid(), "podId"); + ex.addProxyObject(zone.getUuid(), "dcId"); throw ex; } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 8323af84981..d5d95f8eadb 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1153,7 +1153,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Running, cannot " + "migrate the vm with specified id"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -1174,8 +1174,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to find the host (with specified id) of VM with specified id"); - ex.addProxyObject(srcHost, srcHostId, "hostId"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(String.valueOf(srcHostId), "hostId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -1331,7 +1331,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (volume == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find volume with" + " specified id."); - ex.addProxyObject(volume, volumeId, "volumeId"); + ex.addProxyObject(volumeId.toString(), "volumeId"); throw ex; } @@ -1585,8 +1585,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe + " in specified domain"); // Since we don't have a DomainVO object here, we directly set // tablename to "domain". - String tablename = "domain"; - ex.addProxyObject(tablename, domainId, "domainId"); + DomainVO domain = ApiDBUtils.findDomainById(domainId); + String domainUuid = domainId.toString(); + if ( domain != null ){ + domainUuid = domain.getUuid(); + } + ex.addProxyObject(domainUuid, "domainId"); throw ex; } else { accountId = account.getId(); @@ -1606,7 +1610,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe Project project = _projectMgr.getProject(projectId); if (project == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find project by id " + projectId); - ex.addProxyObject(project, projectId, "projectId"); + ex.addProxyObject(projectId.toString(), "projectId"); throw ex; } accountId = project.getProjectAccountId(); @@ -1840,14 +1844,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (isIso && template.getFormat() != ImageFormat.ISO) { s_logger.error("Template Id " + templateId + " is not an ISO"); InvalidParameterValueException ex = new InvalidParameterValueException("Specified Template Id is not an ISO"); - ex.addProxyObject(template, templateId, "templateId"); + ex.addProxyObject(template.getUuid(), "templateId"); throw ex; }// If ISO not requested then it shouldn't be an ISO. if (!isIso && template.getFormat() == ImageFormat.ISO) { s_logger.error("Incorrect format of the template id " + templateId); InvalidParameterValueException ex = new InvalidParameterValueException("Incorrect format " + template.getFormat() + " of the specified template id"); - ex.addProxyObject(template, templateId, "templateId"); + ex.addProxyObject(template.getUuid(), "templateId"); throw ex; } } @@ -1951,14 +1955,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe VMTemplateVO template = _templateDao.findById(id); if (template == null || template.getRemoved() != null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find template/iso with specified id"); - ex.addProxyObject(template, id, "templateId"); + ex.addProxyObject(id.toString(), "templateId"); throw ex; } // Don't allow to modify system template if (id.equals(Long.valueOf(1))) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to update template/iso of specified id"); - ex.addProxyObject(template, id, "templateId"); + ex.addProxyObject(template.getUuid(), "templateId"); throw ex; } @@ -2306,7 +2310,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe DomainVO domain = _domainDao.findById(domainId); if (domain == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find domain with specified domain id"); - ex.addProxyObject(domain, domainId, "domainId"); + ex.addProxyObject(domainId.toString(), "domainId"); throw ex; } else if (domain.getParent() == null && domainName != null) { // check if domain is ROOT domain - and deny to edit it with the new @@ -2330,7 +2334,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (!domains.isEmpty() && !sameDomain) { InvalidParameterValueException ex = new InvalidParameterValueException("Failed to update specified domain id with name '" + domainName + "' since it already exists in the system"); - ex.addProxyObject(domain, domainId, "domainId"); + ex.addProxyObject(domain.getUuid(), "domainId"); throw ex; } } @@ -3236,7 +3240,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe VMInstanceVO systemVm = _vmInstanceDao.findByIdTypes(instanceId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm); if (systemVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a system vm of specified instanceId"); - ex.addProxyObject(systemVm, instanceId, "instanceId"); + ex.addProxyObject(String.valueOf(instanceId), "instanceId"); throw ex; } return systemVm.getType(); @@ -3248,7 +3252,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe VMInstanceVO systemVm = _vmInstanceDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm); if (systemVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a system vm with specified vmId"); - ex.addProxyObject(systemVm, vmId, "vmId"); + ex.addProxyObject(String.valueOf(vmId), "vmId"); throw ex; } @@ -3258,7 +3262,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return startSecondaryStorageVm(vmId); } else { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a system vm with specified vmId"); - ex.addProxyObject(systemVm, vmId, "vmId"); + ex.addProxyObject(systemVm.getUuid(), "vmId"); throw ex; } } @@ -3271,7 +3275,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe VMInstanceVO systemVm = _vmInstanceDao.findByIdTypes(id, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm); if (systemVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a system vm with specified vmId"); - ex.addProxyObject(systemVm, id, "vmId"); + ex.addProxyObject(id.toString(), "vmId"); throw ex; } @@ -3293,7 +3297,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (systemVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a system vm with specified vmId"); - ex.addProxyObject(systemVm, cmd.getId(), "vmId"); + ex.addProxyObject(cmd.getId().toString(), "vmId"); throw ex; } @@ -3310,7 +3314,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (systemVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a system vm with specified vmId"); - ex.addProxyObject(systemVm, cmd.getId(), "vmId"); + ex.addProxyObject(cmd.getId().toString(), "vmId"); throw ex; } @@ -3348,7 +3352,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe User user = _accountMgr.getUserIncludingRemoved(userId); if ((user == null) || (user.getRemoved() != null)) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find active user of specified id"); - ex.addProxyObject(user, userId, "userId"); + ex.addProxyObject(String.valueOf(userId), "userId"); throw ex; } @@ -3444,7 +3448,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe VolumeVO volume = _volumeDao.findById(volumeId); if (volume == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find volume with specified volumeId"); - ex.addProxyObject(volume, volumeId, "volumeId"); + ex.addProxyObject(volumeId.toString(), "volumeId"); throw ex; } @@ -3464,7 +3468,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe + ". It should be either detached or the VM should be in stopped state."); PermissionDeniedException ex = new PermissionDeniedException( "Invalid state of the volume with specified ID. It should be either detached or the VM should be in stopped state."); - ex.addProxyObject(volume, volumeId, "volumeId"); + ex.addProxyObject(volume.getUuid(), "volumeId"); throw ex; } @@ -3481,7 +3485,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (!isExtractable && account != null && account.getType() != Account.ACCOUNT_TYPE_ADMIN) { // Global // admins are always allowed to extract PermissionDeniedException ex = new PermissionDeniedException("The volume with specified volumeId is not allowed to be extracted"); - ex.addProxyObject(volume, volumeId, "volumeId"); + ex.addProxyObject(volume.getUuid(), "volumeId"); throw ex; } } @@ -3639,7 +3643,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe InstanceGroupVO group = _vmGroupDao.findById(groupId.longValue()); if (group == null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a vm group with specified groupId"); - ex.addProxyObject(group, groupId, "groupId"); + ex.addProxyObject(groupId.toString(), "groupId"); throw ex; } @@ -3845,7 +3849,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (s == null) { InvalidParameterValueException ex = new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + owner.getAccountName() + " in specified domain id"); - ex.addProxyObject(owner, owner.getDomainId(), "domainId"); + DomainVO domain = ApiDBUtils.findDomainById(owner.getDomainId()); + String domainUuid = String.valueOf(owner.getDomainId()); + if (domain != null){ + domainUuid = domain.getUuid(); + } + ex.addProxyObject(domainUuid, "domainId"); throw ex; } @@ -3932,7 +3941,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe UserVmVO vm = _userVmDao.findById(cmd.getId()); if (vm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("No VM with specified id found."); - ex.addProxyObject(vm, cmd.getId(), "vmId"); + ex.addProxyObject(cmd.getId().toString(), "vmId"); throw ex; } @@ -3943,7 +3952,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe String password = vm.getDetail("Encrypted.Password"); if (password == null || password.equals("")) { InvalidParameterValueException ex = new InvalidParameterValueException("No password for VM with specified id found."); - ex.addProxyObject(vm, cmd.getId(), "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -4053,7 +4062,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (hpvCapabilities == null) { InvalidParameterValueException ex = new InvalidParameterValueException("unable to find the hypervisor capabilities for specified id"); - ex.addProxyObject(hpvCapabilities, id, "Id"); + ex.addProxyObject(id.toString(), "Id"); throw ex; } diff --git a/server/src/com/cloud/user/DomainManagerImpl.java b/server/src/com/cloud/user/DomainManagerImpl.java index c451041d951..00a779e2ff9 100644 --- a/server/src/com/cloud/user/DomainManagerImpl.java +++ b/server/src/com/cloud/user/DomainManagerImpl.java @@ -226,7 +226,7 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom if ((cleanup != null) && cleanup.booleanValue()) { if (!cleanupDomain(domain.getId(), ownerId)) { CloudRuntimeException e = new CloudRuntimeException("Failed to clean up domain resources and sub domains, delete failed on domain " + domain.getName() + " (id: " + domain.getId() + ")."); - e.addProxyObject(domain, domain.getId(), "domainId"); + e.addProxyObject(domain.getUuid(), "domainId"); throw e; } } else { @@ -235,13 +235,13 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom if (!_domainDao.remove(domain.getId())) { rollBackState = true; CloudRuntimeException e = new CloudRuntimeException("Delete failed on domain " + domain.getName() + " (id: " + domain.getId() + "); Please make sure all users and sub domains have been removed from the domain before deleting"); - e.addProxyObject(domain, domain.getId(), "domainId"); + e.addProxyObject(domain.getUuid(), "domainId"); throw e; } } else { rollBackState = true; CloudRuntimeException e = new CloudRuntimeException("Can't delete the domain yet because it has " + accountsForCleanup.size() + "accounts that need a cleanup"); - e.addProxyObject(domain, domain.getId(), "domainId"); + e.addProxyObject(domain.getUuid(), "domainId"); throw e; } } @@ -480,7 +480,7 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom DomainVO domain = _domainDao.findById(domainId); if (domain == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find domain with specified domain id"); - ex.addProxyObject(domain, domainId, "domainId"); + ex.addProxyObject(domainId.toString(), "domainId"); throw ex; } else if (domain.getParent() == null && domainName != null) { // check if domain is ROOT domain - and deny to edit it with the new name @@ -501,7 +501,7 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom if (!domains.isEmpty() && !sameDomain) { InvalidParameterValueException ex = new InvalidParameterValueException("Failed to update specified domain id with name '" + domainName + "' since it already exists in the system"); - ex.addProxyObject(domain, domainId, "domainId"); + ex.addProxyObject(domain.getUuid(), "domainId"); throw ex; } } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 96b95293b47..56578776b98 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3288,7 +3288,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } catch (CloudException e) { CloudRuntimeException ex = new CloudRuntimeException( "Unable to destroy with specified vmId", e); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -3315,7 +3315,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } else { CloudRuntimeException ex = new CloudRuntimeException( "Failed to destroy vm with specified vmId"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } } @@ -3508,7 +3508,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (userVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "unable to find a virtual machine with specified id"); - ex.addProxyObject(userVm, vmId, "vmId"); + ex.addProxyObject(String.valueOf(vmId), "vmId"); throw ex; } @@ -3550,7 +3550,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (vm.getState() != State.Stopped) { InvalidParameterValueException ex = new InvalidParameterValueException( "VM is not Stopped, unable to migrate the vm having the specified id"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -3627,7 +3627,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } InvalidParameterValueException ex = new InvalidParameterValueException( "VM is not Running, unable to migrate the vm with specified id"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } if (!vm.getHypervisorType().equals(HypervisorType.XenServer) @@ -3719,7 +3719,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } CloudRuntimeException ex = new CloudRuntimeException("VM is not Running, unable to migrate the vm with" + " specified id"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -3849,7 +3849,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } InvalidParameterValueException ex = new InvalidParameterValueException( "VM is Running, unable to move the vm with specified vmId"); - ex.addProxyObject(vm, cmd.getVmId(), "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -3863,7 +3863,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { InvalidParameterValueException ex = new InvalidParameterValueException( "Specified Vm id belongs to the project and can't be moved"); - ex.addProxyObject(vm, cmd.getVmId(), "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } Account newAccount = _accountService.getActiveAccountByName( @@ -4116,7 +4116,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (network == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Unable to find specified network id"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(networkId.toString(), "networkId"); throw ex; } @@ -4129,7 +4129,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (networkOffering.isSystemOnly()) { InvalidParameterValueException ex = new InvalidParameterValueException( "Specified Network id is system only and can't be used for vm deployment"); - ex.addProxyObject(network, networkId, "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } applicableNetworks.add(network); @@ -4180,7 +4180,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use " resources as a part of network provision for persistent network due to ", ex); CloudRuntimeException e = new CloudRuntimeException("Failed to implement network" + " (with specified id) elements and resources as a part of network provision"); - e.addProxyObject(newNetwork, newNetwork.getId(), "networkId"); + e.addProxyObject(newNetwork.getUuid(), "networkId"); throw e; } } @@ -4241,7 +4241,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Cannot find VM with ID " + vmId); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(String.valueOf(vmId), "vmId"); throw ex; } @@ -4287,7 +4287,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (rootVols.isEmpty()) { InvalidParameterValueException ex = new InvalidParameterValueException( "Can not find root volume for VM " + vm.getUuid()); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } @@ -4322,8 +4322,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use if (template == null) { InvalidParameterValueException ex = new InvalidParameterValueException( "Cannot find template/ISO for specified volumeid and vmId"); - ex.addProxyObject(vm, vmId, "vmId"); - ex.addProxyObject(root, root.getId(), "volumeId"); + ex.addProxyObject(vm.getUuid(), "vmId"); + ex.addProxyObject(root.getUuid(), "volumeId"); throw ex; } } @@ -4335,7 +4335,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use s_logger.debug("Stop vm " + vm.getUuid() + " failed", e); CloudRuntimeException ex = new CloudRuntimeException( "Stop vm failed for specified vmId"); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } } @@ -4396,7 +4396,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use s_logger.debug("Unable to start VM " + vm.getUuid(), e); CloudRuntimeException ex = new CloudRuntimeException( "Unable to start VM with specified id" + e.getMessage()); - ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } } diff --git a/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java b/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java index ec0be8c9d96..ac3b8f56e61 100644 --- a/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java +++ b/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java @@ -201,7 +201,7 @@ public class ApplicationLoadBalancerManagerImpl extends ManagerBase implements A if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Lb)) { InvalidParameterValueException ex = new InvalidParameterValueException( "LB service is not supported in specified network id"); - ex.addProxyObject(network, network.getId(), "networkId"); + ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } diff --git a/utils/src/com/cloud/utils/exception/CloudRuntimeException.java b/utils/src/com/cloud/utils/exception/CloudRuntimeException.java index 00141f24122..d3b4c197e14 100755 --- a/utils/src/com/cloud/utils/exception/CloudRuntimeException.java +++ b/utils/src/com/cloud/utils/exception/CloudRuntimeException.java @@ -18,7 +18,6 @@ package com.cloud.utils.exception; import java.util.ArrayList; -import com.cloud.utils.AnnotationHelper; import com.cloud.utils.SerialVersionUID; /** @@ -28,8 +27,8 @@ public class CloudRuntimeException extends RuntimeException { private static final long serialVersionUID = SerialVersionUID.CloudRuntimeException; - // This holds a list of uuids and their names. Add uuid:fieldname pairs - protected ArrayList idList = new ArrayList(); + // This holds a list of uuids and their descriptive names. + protected ArrayList idList = new ArrayList(); protected int csErrorCode; @@ -49,21 +48,20 @@ public class CloudRuntimeException extends RuntimeException { setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); } + public void addProxyObject(ExceptionProxyObject obj){ + idList.add(obj); + } + public void addProxyObject(String uuid) { - idList.add(uuid); - return; + idList.add(new ExceptionProxyObject(uuid, null)); } - public void addProxyObject(Object voObj, Long id, String idFieldName) { - // Get the VO object's table name. - String tablename = AnnotationHelper.getTableName(voObj); - if (tablename != null) { - addProxyObject(tablename, id, idFieldName); - } - return; + public void addProxyObject(String voObjUuid, String description) { + ExceptionProxyObject proxy = new ExceptionProxyObject(voObjUuid, description); + idList.add(proxy); } - public ArrayList getIdProxyList() { + public ArrayList getIdProxyList() { return idList; } diff --git a/utils/src/com/cloud/utils/exception/ExceptionProxyObject.java b/utils/src/com/cloud/utils/exception/ExceptionProxyObject.java new file mode 100644 index 00000000000..01cbd632c14 --- /dev/null +++ b/utils/src/com/cloud/utils/exception/ExceptionProxyObject.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// 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. +package com.cloud.utils.exception; + + +public class ExceptionProxyObject { + private String uuid; + private String description; + + public ExceptionProxyObject(){ + + } + + public ExceptionProxyObject(String uuid, String desc){ + this.uuid = uuid; + this.description = desc; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + +} From ab2e3b9e07c0da623011859ef73388b5ce0c11ce Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 28 May 2013 16:16:34 -0700 Subject: [PATCH 161/412] CLOUDSTACK-2678: portable IP ranges - regions menu - region detailView - show "View Portable IP Ranges" for root-admin, hide it for regular-user/domain-admin. --- ui/scripts/regions.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ui/scripts/regions.js b/ui/scripts/regions.js index b9b6daeac20..4be600f8b69 100644 --- a/ui/scripts/regions.js +++ b/ui/scripts/regions.js @@ -117,8 +117,20 @@ detailView: { name: 'Region details', viewAll: [ - { path: 'regions.GSLB', label: 'GSLB' }, - { path: 'regions.portableIpRanges', label: 'Portable IP' } + { + path: 'regions.GSLB', + label: 'GSLB' + }, + { + path: 'regions.portableIpRanges', + label: 'Portable IP', + preFilter: function(args) { + if (isAdmin()) + return true; + + return false; + } + } ], actions: { edit: { From ccac84c469607768665cd2239abfdf8bf0561f21 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 28 May 2013 17:05:51 -0700 Subject: [PATCH 162/412] Workaround for broken bvt/ simulator tests Actually this check makes no sense since it breaks basic zone for all hypervisors except KVM, XS Will revisit later --- server/src/com/cloud/resource/ResourceManagerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index f767b6810a4..1e28d92896f 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -445,7 +445,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, } if (zone.isSecurityGroupEnabled()) { - if( hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer ) { + if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer + && hypervisorType != HypervisorType.Simulator) { throw new InvalidParameterValueException("Don't support hypervisor type " + hypervisorType + " in advanced security enabled zone"); } } From be5639f7e278aca722757475139a61b69df0163c Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Wed, 29 May 2013 09:48:25 +0530 Subject: [PATCH 163/412] Adding environment attributes to tests - test_internal_lb _ test_privategw_acl Signed-off-by: Prasanna Santhanam --- test/integration/smoke/test_internal_lb.py | 2 ++ test/integration/smoke/test_privategw_acl.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/test/integration/smoke/test_internal_lb.py b/test/integration/smoke/test_internal_lb.py index ae64297bf1c..6c039004ab5 100644 --- a/test/integration/smoke/test_internal_lb.py +++ b/test/integration/smoke/test_internal_lb.py @@ -22,6 +22,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * +from nose.plugins.attrib import attr class TestInternalLb(cloudstackTestCase): @@ -97,6 +98,7 @@ class TestInternalLb(cloudstackTestCase): + @attr(tags=["simulator", "advanced"]) def test_internallb(self): #1) Create and enable network offering with Internal Lb vm service diff --git a/test/integration/smoke/test_privategw_acl.py b/test/integration/smoke/test_privategw_acl.py index 5daf6ca0a59..af5c28b5b2b 100644 --- a/test/integration/smoke/test_privategw_acl.py +++ b/test/integration/smoke/test_privategw_acl.py @@ -22,6 +22,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * +from nose.plugins.attrib import attr class TestPrivateGwACL(cloudstackTestCase): @@ -37,6 +38,8 @@ class TestPrivateGwACL(cloudstackTestCase): self.templateId = 5 self.privateGwId = None + + @attr(tags=["simulator", "advanced"]) def test_privategw_acl(self): # 1) Create VPC From 7fb6eaa0ca5f0f58b23ab6af812db6366743717a Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Wed, 29 May 2013 10:01:20 +0530 Subject: [PATCH 164/412] Attributes for tests Removing simulator attribute as the NetworkACL related support is to be extended on the simulator. Signed-off-by: Prasanna Santhanam --- test/integration/smoke/test_internal_lb.py | 2 +- test/integration/smoke/test_network_acl.py | 3 ++- test/integration/smoke/test_privategw_acl.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integration/smoke/test_internal_lb.py b/test/integration/smoke/test_internal_lb.py index 6c039004ab5..07a539592ca 100644 --- a/test/integration/smoke/test_internal_lb.py +++ b/test/integration/smoke/test_internal_lb.py @@ -98,7 +98,7 @@ class TestInternalLb(cloudstackTestCase): - @attr(tags=["simulator", "advanced"]) + @attr(tags=["advanced"]) def test_internallb(self): #1) Create and enable network offering with Internal Lb vm service diff --git a/test/integration/smoke/test_network_acl.py b/test/integration/smoke/test_network_acl.py index 66f0a6fc1c5..3ed45be9cd0 100644 --- a/test/integration/smoke/test_network_acl.py +++ b/test/integration/smoke/test_network_acl.py @@ -22,7 +22,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * - +from nose.plugins.attrib import attr class TestNetworkACL(cloudstackTestCase): networkOfferingId = 11 @@ -40,6 +40,7 @@ class TestNetworkACL(cloudstackTestCase): + @attr(tags=["advanced"]) def test_networkAcl(self): # 1) Create VPC diff --git a/test/integration/smoke/test_privategw_acl.py b/test/integration/smoke/test_privategw_acl.py index af5c28b5b2b..9c37e5e7fc0 100644 --- a/test/integration/smoke/test_privategw_acl.py +++ b/test/integration/smoke/test_privategw_acl.py @@ -39,7 +39,7 @@ class TestPrivateGwACL(cloudstackTestCase): self.privateGwId = None - @attr(tags=["simulator", "advanced"]) + @attr(tags=["advanced"]) def test_privategw_acl(self): # 1) Create VPC From 84d904abf2e6e0616a47b338e588276530094d4e Mon Sep 17 00:00:00 2001 From: Koushik Das Date: Wed, 29 May 2013 14:20:21 +0530 Subject: [PATCH 165/412] CLOUDSTACK-2719: Additional public IP is getting acquired during Cisco VNMc provider Guest Network restart (cleanup=true) An extra public ip is acquired while implementing the vnmc element as there is a limitation where in the source nat cannot be used as asa outside ip. As a result of this when the network gets re-implemented an additional ip is acquired every time. The fix involves checking for existing public ips in the network and reuse it in case it is not a source nat ip for assigning to asa outside interface. --- .../network/element/CiscoVnmcElement.java | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java b/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java index b335edb9f63..9118bad6afe 100644 --- a/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java +++ b/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java @@ -98,6 +98,8 @@ import com.cloud.network.cisco.NetworkAsa1000vMapVO; import com.cloud.network.dao.CiscoAsa1000vDao; import com.cloud.network.dao.CiscoNexusVSMDeviceDao; import com.cloud.network.dao.CiscoVnmcDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkAsa1000vMapDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.PhysicalNetworkDao; @@ -148,7 +150,9 @@ public class CiscoVnmcElement extends AdapterBase implements SourceNatServicePro PhysicalNetworkDao _physicalNetworkDao; @Inject PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; - @Inject + @Inject + IPAddressDao _ipAddressDao; + @Inject HostDetailsDao _hostDetailsDao; @Inject HostDao _hostDao; @@ -342,22 +346,33 @@ public class CiscoVnmcElement extends AdapterBase implements SourceNatServicePro } // due to VNMC limitation of not allowing source NAT ip as the outside ip of firewall, - // an additional public ip needs to acquired for assigning as firewall outside ip + // an additional public ip needs to acquired for assigning as firewall outside ip. + // In case there are already additional ip addresses available (network restart) use one + // of them such that it is not the source NAT ip IpAddress outsideIp = null; - try { - Account caller = UserContext.current().getCaller(); - long callerUserId = UserContext.current().getCallerUserId(); - outsideIp = _networkMgr.allocateIp(owner, false, caller, callerUserId, zone); - } catch (ResourceAllocationException e) { - s_logger.error("Unable to allocate additional public Ip address. Exception details " + e); - return false; + List publicIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null); + for (IPAddressVO ip : publicIps) { + if (!ip.isSourceNat()) { + outsideIp = ip; + break; + } } + if (outsideIp == null) { // none available, acquire one + try { + Account caller = UserContext.current().getCaller(); + long callerUserId = UserContext.current().getCallerUserId(); + outsideIp = _networkMgr.allocateIp(owner, false, caller, callerUserId, zone); + } catch (ResourceAllocationException e) { + s_logger.error("Unable to allocate additional public Ip address. Exception details " + e); + return false; + } - try { - outsideIp = _networkMgr.associateIPToGuestNetwork(outsideIp.getId(), network.getId(), true); - } catch (ResourceAllocationException e) { - s_logger.error("Unable to assign allocated additional public Ip " + outsideIp.getAddress().addr() + " to network with vlan " + vlanId + ". Exception details " + e); - return false; + try { + outsideIp = _networkMgr.associateIPToGuestNetwork(outsideIp.getId(), network.getId(), true); + } catch (ResourceAllocationException e) { + s_logger.error("Unable to assign allocated additional public Ip " + outsideIp.getAddress().addr() + " to network with vlan " + vlanId + ". Exception details " + e); + return false; + } } // create logical edge firewall in VNMC From e3a104aa58082521dac2a92a9cc1c5b23494f29b Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Wed, 29 May 2013 17:14:36 +0530 Subject: [PATCH 166/412] fix networkAclServiceTest failure --- server/test/com/cloud/vpc/NetworkACLServiceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/test/com/cloud/vpc/NetworkACLServiceTest.java b/server/test/com/cloud/vpc/NetworkACLServiceTest.java index 9a368b94ae4..e71fabfef2d 100644 --- a/server/test/com/cloud/vpc/NetworkACLServiceTest.java +++ b/server/test/com/cloud/vpc/NetworkACLServiceTest.java @@ -138,6 +138,7 @@ public class NetworkACLServiceTest extends TestCase{ Mockito.when(_networkAclMgr.getNetworkACL(Mockito.anyLong())).thenReturn(acl); Mockito.when(_networkAclMgr.createNetworkACLItem(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyString(), Mockito.anyList(), Mockito.anyInt(), Mockito.anyInt(), Mockito.any(NetworkACLItem.TrafficType.class), Mockito.anyLong(), Mockito.anyString(), Mockito.anyInt())).thenReturn(new NetworkACLItemVO()); + Mockito.when(_networkACLItemDao.findByAclAndNumber(Mockito.anyLong(), Mockito.anyInt())).thenReturn(null); assertNotNull(_aclService.createNetworkACLItem(createACLItemCmd)); } From 2b9396982323e17116515806836cd2f615968557 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Wed, 29 May 2013 15:48:13 +0530 Subject: [PATCH 167/412] add upgrade path to "cloud.dns.name" global config value. fail creating GSLB rule if DNS name is not confiugred --- server/src/com/cloud/configuration/Config.java | 2 +- .../region/gslb/GlobalLoadBalancingRulesServiceImpl.java | 3 ++- setup/db/db/schema-410to420.sql | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index e1d3751f290..7c88aae5044 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -408,7 +408,7 @@ public enum Config { VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null), VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "1800", "In second, timeout for create vm snapshot", null), - CloudDnsName("Advanced", ManagementServer.class, String.class, "cloud.dns.name", "default", " DNS name of the cloud", null), + CloudDnsName("Advanced", ManagementServer.class, String.class, "cloud.dns.name", null, "DNS name of the cloud for the GSLB service", null), BlacklistedRoutes("Advanced", VpcManager.class, String.class, "blacklisted.routes", null, "Routes that are blacklisted, can not be used for Static Routes creation for the VPC Private Gateway", "routes", ConfigurationParameterScope.zone.toString()), diff --git a/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java b/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java index a1865c64af7..a803f9fb16b 100644 --- a/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java +++ b/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java @@ -126,7 +126,8 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR throw new InvalidParameterValueException("Invalid region ID: " + regionId); } - if (!region.checkIfServiceEnabled(Region.Service.Gslb)) { + String providerDnsName = _globalConfigDao.getValue(Config.CloudDnsName.key()); + if (!region.checkIfServiceEnabled(Region.Service.Gslb) || (providerDnsName == null)) { throw new CloudRuntimeException("GSLB service is not enabled in region : " + region.getName()); } diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index cf4e98dc8a0..ec79237de5e 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -332,6 +332,8 @@ CREATE TABLE `cloud`.`global_load_balancer_lb_rule_map` ( CONSTRAINT `fk_lb_rule_id` FOREIGN KEY(`lb_rule_id`) REFERENCES `load_balancing_rules`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'cloud.dns.name', null, 'DNS name of the cloud for the GSLB service'); + INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.cpus', '40', 'The default maximum number of cpu cores that can be used for an account'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.memory', '40960', 'The default maximum memory (in MiB) that can be used for an account'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.primary.storage', '200', 'The default maximum primary storage space (in GiB) that can be used for an account'); From c8a0c40a2f50fdabab085a0d6896edcdb8226ef4 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Wed, 29 May 2013 17:01:54 +0530 Subject: [PATCH 168/412] CLOUDSTACK-2477: [GSLB] CloudStack currently allows admin to map LB rule of one account to GSLB rule of a different account ensuring account id of GSLB rule and LB rule are same --- .../region/gslb/GlobalLoadBalancingRulesServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java b/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java index a803f9fb16b..96ac76e3eae 100644 --- a/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java +++ b/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java @@ -204,6 +204,10 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR _accountMgr.checkAccess(caller, null, true, loadBalancer); + if (gslbRule.getAccountId() != loadBalancer.getAccountId()) { + throw new InvalidParameterValueException("GSLB rule and load balancer rule does not belong to same account"); + } + if (loadBalancer.getState() == LoadBalancer.State.Revoke) { throw new InvalidParameterValueException("Load balancer ID " + loadBalancer.getUuid() + " is in revoke state"); } From ee317287b3981200ec1a54ee096600fd24a16475 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Wed, 29 May 2013 18:50:06 +0530 Subject: [PATCH 169/412] CLOUDSTACK-2285: [GSLB] addNetscalerLoadBalancer with GSLB functionality shouldn't be exposed in basic zone imposing restriction that NetScaler can only be added when EIP/ELB service are available in basic zone. --- .../ExternalLoadBalancerDeviceManagerImpl.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java index f93bf7ae9b5..04f96825abb 100644 --- a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java @@ -138,6 +138,8 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase @Inject NetworkManager _networkMgr; @Inject + NetworkDao _networksDao = null; + @Inject InlineLoadBalancerNicMapDao _inlineLoadBalancerNicMapDao; @Inject NicDao _nicDao; @@ -208,6 +210,17 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase } zoneId = pNetwork.getDataCenterId(); + DataCenter dc = _dcDao.findById(zoneId); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + List guestNetworks = _networksDao.listByZoneAndTrafficType(dc.getId(), TrafficType.Guest); + com.cloud.network.dao.NetworkVO basicZoneNetwork = guestNetworks.get(0); + NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(basicZoneNetwork.getNetworkOfferingId()); + if (!ntwkOff.getElasticIp() && !ntwkOff.getElasticLb()) { + throw new InvalidParameterValueException("Could not add external load balancer device in to basic zone " + + " with no Elastic IP and Elastic LB services."); + } + } + PhysicalNetworkServiceProviderVO ntwkSvcProvider = _physicalNetworkServiceProviderDao.findByServiceProvider(pNetwork.getId(), ntwkDevice.getNetworkServiceProvder()); if (gslbProvider) { From cea9e3919a613c71da5d95acf631355ab2b6d018 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Wed, 29 May 2013 18:52:12 +0530 Subject: [PATCH 170/412] fixing unit tests to comply with bug "CLOUDSTACK-2477: [GSLB] CloudStack currently allows admin to map LB rule of one account to GSLB rule of a different account" --- .../GlobalLoadBalancingRulesServiceImplTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/test/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImplTest.java b/server/test/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImplTest.java index 1c281a08bed..ab545342cfa 100644 --- a/server/test/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImplTest.java +++ b/server/test/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImplTest.java @@ -577,8 +577,14 @@ public class GlobalLoadBalancingRulesServiceImplTest extends TestCase { LoadBalancerVO lbRule1 = new LoadBalancerVO(); lbRule1.setState(FirewallRule.State.Active); Field networkIdField1 = LoadBalancerVO.class.getSuperclass().getDeclaredField("networkId"); + Field accountIdField1 = LoadBalancerVO.class.getSuperclass().getDeclaredField("accountId"); + Field domainIdField1 = LoadBalancerVO.class.getSuperclass().getDeclaredField("domainId"); networkIdField1.setAccessible(true); + accountIdField1.setAccessible(true); + domainIdField1.setAccessible(true); networkIdField1.set(lbRule1, new Long(1)); + accountIdField1.set(lbRule1, new Long(3)); + domainIdField1.set(lbRule1, new Long(1)); Field idField1 = LoadBalancerVO.class.getSuperclass().getDeclaredField("id"); idField1.setAccessible(true); idField1.set(lbRule1, new Long(1)); @@ -586,8 +592,14 @@ public class GlobalLoadBalancingRulesServiceImplTest extends TestCase { LoadBalancerVO lbRule2 = new LoadBalancerVO(); lbRule2.setState(FirewallRule.State.Active); Field networkIdField2 = LoadBalancerVO.class.getSuperclass().getDeclaredField("networkId"); + Field accountIdField2 = LoadBalancerVO.class.getSuperclass().getDeclaredField("accountId"); + Field domainIdField2 = LoadBalancerVO.class.getSuperclass().getDeclaredField("domainId"); networkIdField2.setAccessible(true); + accountIdField2.setAccessible(true); + domainIdField2.setAccessible(true); networkIdField2.set(lbRule2, new Long(1)); + accountIdField2.set(lbRule2, new Long(3)); + domainIdField2.set(lbRule2, new Long(1)); Field idField2 = LoadBalancerVO.class.getSuperclass().getDeclaredField("id"); idField2.setAccessible(true); idField2.set(lbRule2, new Long(2)); @@ -611,6 +623,7 @@ public class GlobalLoadBalancingRulesServiceImplTest extends TestCase { try { gslbServiceImpl.assignToGlobalLoadBalancerRule(assignCmd); } catch (InvalidParameterValueException e) { + s_logger.info(e.getMessage()); Assert.assertTrue(e.getMessage().contains("Load balancer rule specified should be in unique zone")); } } From 8d1189c2ae87216bc1c4a1443f75e9a8629abdc2 Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Wed, 29 May 2013 19:01:23 +0530 Subject: [PATCH 171/412] CLOUDSTACK-2060 Global config to turn off dynamically scale vm functionality --- api/src/com/cloud/agent/api/to/VirtualMachineTO.java | 9 +++++++++ .../hypervisor/xen/resource/XenServer56FP1Resource.java | 2 +- server/src/com/cloud/configuration/Config.java | 3 ++- server/src/com/cloud/hypervisor/HypervisorGuruBase.java | 9 ++++++++- server/src/com/cloud/vm/UserVmManagerImpl.java | 8 ++++++++ setup/db/db/schema-410to420.sql | 4 ++++ 6 files changed, 32 insertions(+), 3 deletions(-) diff --git a/api/src/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/com/cloud/agent/api/to/VirtualMachineTO.java index b84d20a9239..46ee01bc8a3 100644 --- a/api/src/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/com/cloud/agent/api/to/VirtualMachineTO.java @@ -52,6 +52,7 @@ public class VirtualMachineTO { boolean rebootOnCrash; boolean enableHA; boolean limitCpuUse; + boolean enableDynamicallyScaleVm; String vncPassword; String vncAddr; Map params; @@ -102,6 +103,14 @@ public class VirtualMachineTO { this.id = id; } + public boolean isEnableDynamicallyScaleVm() { + return enableDynamicallyScaleVm; + } + + public void setEnableDynamicallyScaleVm(boolean enableDynamicallyScaleVm) { + this.enableDynamicallyScaleVm = enableDynamicallyScaleVm; + } + public String getName() { return name; } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java index 54aeef5a022..8e378093f3f 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java @@ -137,7 +137,7 @@ public class XenServer56FP1Resource extends XenServer56Resource { vmr.actionsAfterCrash = Types.OnCrashBehaviour.DESTROY; vmr.actionsAfterShutdown = Types.OnNormalExit.DESTROY; - if (isDmcEnabled(conn, host)) { + if (isDmcEnabled(conn, host) && vmSpec.isEnableDynamicallyScaleVm()) { //scaling is allowed vmr.memoryStaticMin = mem_128m; //128MB //TODO: Remove hardcoded 8GB and assign proportionate to ServiceOffering and mem overcommit ratio diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 7c88aae5044..929d56bf10c 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -180,7 +180,8 @@ public enum Config { RouterTemplateLXC("Advanced", NetworkManager.class, String.class, "router.template.lxc", "SystemVM Template (LXC)", "Name of the default router template on LXC.", null, ConfigurationParameterScope.zone.toString()), RouterExtraPublicNics("Advanced", NetworkManager.class, Integer.class, "router.extra.public.nics", "2", "specify extra public nics used for virtual router(up to 5)", "0-5"), StartRetry("Advanced", AgentManager.class, Integer.class, "start.retry", "10", "Number of times to retry create and start commands", null), - ScaleRetry("Advanced", AgentManager.class, Integer.class, "scale.retry", "2", "Number of times to retry scaling up the vm", null), + EnableDynamicallyScaleVm("Advanced", ManagementServer.class, Boolean.class, "enable.dynamic.scale.vm", "false", "Enables/Diables dynamically scaling a vm", null, ConfigurationParameterScope.zone.toString()), + ScaleRetry("Advanced", ManagementServer.class, Integer.class, "scale.retry", "2", "Number of times to retry scaling up the vm", null), StopRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "stop.retry.interval", "600", "Time in seconds between retries to stop or destroy a vm" , null), StorageCleanupInterval("Advanced", StorageManager.class, Integer.class, "storage.cleanup.interval", "86400", "The interval (in seconds) to wait before running the storage cleanup thread.", null), StorageCleanupEnabled("Advanced", StorageManager.class, Boolean.class, "storage.cleanup.enabled", "true", "Enables/disables the storage cleanup thread.", null), diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index ea4fcc1e5bf..d8945af9b8c 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -25,7 +25,9 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.configuration.Config; import com.cloud.offering.ServiceOffering; +import com.cloud.server.ConfigurationServer; import com.cloud.storage.dao.VMTemplateDetailsDao; import com.cloud.utils.component.AdapterBase; import com.cloud.vm.NicProfile; @@ -43,6 +45,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis @Inject NicDao _nicDao; @Inject VMInstanceDao _virtualMachineDao; @Inject NicSecondaryIpDao _nicSecIpDao; + @Inject ConfigurationServer _configServer; + protected HypervisorGuruBase() { super(); @@ -121,7 +125,10 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis // Workaround to make sure the TO has the UUID we need for Niciri integration VMInstanceVO vmInstance = _virtualMachineDao.findById(to.getId()); to.setUuid(vmInstance.getUuid()); - + + // + to.setEnableDynamicallyScaleVm(Boolean.parseBoolean(_configServer.getConfigValue(Config.EnableDynamicallyScaleVm.key(), Config.ConfigurationParameterScope.zone.toString(), vm.getDataCenterId()))); + return to; } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 56578776b98..a734d44c800 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -32,6 +32,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.server.ConfigurationServer; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroupVO; @@ -400,6 +401,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject AffinityGroupDao _affinityGroupDao; + @Inject + ConfigurationServer _configServer; protected ScheduledExecutorService _executor = null; protected int _expungeInterval; @@ -1141,6 +1144,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use boolean success = false; if(vmInstance.getState().equals(State.Running)){ int retry = _scaleRetry; + boolean enableDynamicallyScaleVm = Boolean.parseBoolean(_configServer.getConfigValue(Config.EnableDynamicallyScaleVm.key(), Config.ConfigurationParameterScope.zone.toString(), vmInstance.getDataCenterId())); + if(!enableDynamicallyScaleVm){ + throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin"); + } + // Increment CPU and Memory count accordingly. if (newCpu > currentCpu) { _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long (newCpu - currentCpu)); diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index ec79237de5e..e081a252386 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1712,6 +1712,10 @@ update `cloud`.`vpc_gateways` set network_acl_id = 2; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'VpcManager', 'blacklisted.routes', NULL, 'Routes that are blacklisted, can not be used for Static Routes creation for the VPC Private Gateway'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'enable.dynamic.scale.vm', 'false', 'Enables/Diables dynamically scaling a vm'); + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'scale.retry', '2', 'Number of times to retry scaling up the vm'); + UPDATE `cloud`.`snapshots` set swift_id=null where swift_id=0; From 9905f656a5307c9d9b990ed988c2ea82b432231a Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 11:32:15 -0700 Subject: [PATCH 172/412] only check hypervisor type for security enabled advanced zone --- server/src/com/cloud/resource/ResourceManagerImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 1e28d92896f..74bd6d04a3e 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -161,6 +161,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.dc.DataCenter.NetworkType; @Component @Local({ ResourceManager.class, ResourceService.class }) @@ -444,7 +445,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, + cmd.getHypervisor() + " to a supported "); } - if (zone.isSecurityGroupEnabled()) { + if (zone.isSecurityGroupEnabled() && zone.getNetworkType().equals(NetworkType.Advanced)) { if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer && hypervisorType != HypervisorType.Simulator) { throw new InvalidParameterValueException("Don't support hypervisor type " + hypervisorType + " in advanced security enabled zone"); From 6a7cc2021e627f68218ebb1c594a06dbcb148c51 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 13:45:07 -0700 Subject: [PATCH 173/412] when VR is rebooted, CloudStack will reapply all ips/rules to VR, this patch will reduce time the VR reboot takes - remove 1 s sleep in vmware - reduce the time arping takes --- patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh | 6 ++++-- utils/src/com/cloud/utils/ssh/SshHelper.java | 4 ---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh b/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh index f326fac9e54..d23ec00de5e 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/ipassoc.sh @@ -227,7 +227,8 @@ add_first_ip() { if [ $if_keep_state -ne 1 -o $old_state -ne 0 ] then sudo ip link set $ethDev up - sudo arping -c 3 -I $ethDev -A -U -s $ipNoMask $ipNoMask; + sudo arping -c 1 -I $ethDev -A -U -s $ipNoMask $ipNoMask; + sudo arping -c 1 -I $ethDev -A -U -s $ipNoMask $ipNoMask; fi add_routing $1 @@ -273,7 +274,8 @@ add_an_ip () { if [ $if_keep_state -ne 1 -o $old_state -ne 0 ] then sudo ip link set $ethDev up - sudo arping -c 3 -I $ethDev -A -U -s $ipNoMask $ipNoMask; + sudo arping -c 1 -I $ethDev -A -U -s $ipNoMask $ipNoMask; + sudo arping -c 1 -I $ethDev -A -U -s $ipNoMask $ipNoMask; fi add_routing $1 return $? diff --git a/utils/src/com/cloud/utils/ssh/SshHelper.java b/utils/src/com/cloud/utils/ssh/SshHelper.java index 6ee87ad722c..fb81e506ee5 100755 --- a/utils/src/com/cloud/utils/ssh/SshHelper.java +++ b/utils/src/com/cloud/utils/ssh/SshHelper.java @@ -146,10 +146,6 @@ public class SshHelper { } sess = conn.openSession(); - // There is a bug in Trilead library, wait a second before - // starting a shell and executing commands, from http://spci.st.ewi.tudelft.nl/chiron/xref/nl/tudelft/swerl/util/SSHConnection.html - Thread.sleep(1000); - sess.execCommand(command); InputStream stdout = sess.getStdout(); From 0b728f2e77c7ae69c8dcd4ddaba18559180454ae Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 13:51:22 -0700 Subject: [PATCH 174/412] remove unused file --- scripts/storage/qcow2/modifyvlan.sh | 269 ---------------------------- 1 file changed, 269 deletions(-) delete mode 100755 scripts/storage/qcow2/modifyvlan.sh diff --git a/scripts/storage/qcow2/modifyvlan.sh b/scripts/storage/qcow2/modifyvlan.sh deleted file mode 100755 index 5e26af0ba02..00000000000 --- a/scripts/storage/qcow2/modifyvlan.sh +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with 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. - - -# $Id: modifyvlan.sh 9132 2010-06-04 20:17:43Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/scripts/storage/qcow2/modifyvlan.sh $ -# modifyvlan.sh -- adds and deletes VLANs from a Routing Server -# set -x - -usage() { - printf "Usage: %s: -o -v -g \n" -} - -addVlan() { - local vlanId=$1 - - ifconfig bond1.$vlanId > /dev/null - - if [ $? -gt 0 ] - then - vconfig add bond1 $vlanId - - if [ $? -gt 0 ] - then - return 1 - fi - fi - - # Make ifcfg-bond1.$vlanId - rm /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - touch /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - echo "DEVICE=bond1.$vlanId" >> /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - echo "VLAN=yes" >> /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - echo "BRIDGE=xenbr1.$vlanId" >> /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - - # Try to add xenbr1.$vlanId over bond1.$vlanId, if it does not already exist - - ifconfig xenbr1.$vlanId > /dev/null - - if [ $? -gt 0 ] - then - brctl addbr xenbr1.$vlanId - - if [ $? -gt 0 ] - then - return 1 - fi - - brctl addif xenbr1.$vlanId bond1.$vlanId - - if [ $? -gt 0 ] - then - return 1 - fi - - fi - - ifconfig xenbr1.$vlanId up - - if [ $? -gt 0 ] - then - return 1 - fi - - # Make ifcfg-xenbr1.$vlanId - rm /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - touch /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - echo "TYPE=bridge" >> /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - echo "DEVICE=xenbr1.$vlanId" >> /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - - return 0 -} - -deleteVlan() { - local vlanId=$1 - - # Try to remove xenbr1.$vlanId - ifconfig xenbr1.$vlanId down - - if [ $? -gt 0 ] - then - return 1 - fi - - brctl delbr xenbr1.$vlanId - - if [ $? -gt 0 ] - then - return 1 - fi - - # Remove ifcfg-xenbr1.$vlanId - rm /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId - - # Try to remove bond1.$vlanId - - vconfig rem bond1.$vlanId - - if [ $? -gt 0 ] - then - return 1 - fi - - # Remove ifcfg-bond1.$vlanId - rm /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId - - return 0 - -} - -checkIfVlanExists() { - local vlanId=$1 - - if [ "$vlanId" == "untagged" ] - then - # This VLAN should always exist, since the bridge is xenbr1, which is created during vsetup - return 0 - fi - - ifconfig bond1.$vlanId > /dev/null - - if [ $? -gt 0 ] - then - return 1 - fi - - ifconfig xenbr1.$vlanId > /dev/null - - if [ $? -gt 0 ] - then - return 1 - fi - - if [ ! -f /etc/sysconfig/network-scripts/ifcfg-xenbr1.$vlanId ] - then - return 1 - fi - - if [ ! -f /etc/sysconfig/network-scripts/ifcfg-bond1.$vlanId ] - then - return 1 - fi - - return 0 -} - -arpingVlan() { - local vlanId=$1 - local vlanGateway=$2 - - # Change!!! - return 0 - - success=1 - for i in $(seq 1 3) - do - arping -I xenbr1.$vlanId $vlanGateway > /dev/null - - if [ $? -gt 0 ] - then - success=0 - break - fi - done - - return $success -} - -op= -vlanId= -vlanGateway= -option=$@ - -while getopts 'o:v:g:' OPTION -do - case $OPTION in - o) oflag=1 - op="$OPTARG" - ;; - v) vflag=1 - vlanId="$OPTARG" - ;; - g) gflag=1 - vlanGateway="$OPTARG" - ;; - ?) usage - exit 2 - ;; - esac -done - -# Check that all arguments were passed in -if [ "$oflag$vflag$gflag" != "111" ] -then - usage - exit 2 -fi - -if [ "$op" == "add" ] -then - # Check if the vlan already exists, and exit with success if it does - checkIfVlanExists $vlanId - - if [ $? -eq 0 ] - then - exit 0 - fi - - # Add the vlan - addVlan $vlanId - - # If the add fails then return failure - if [ $? -gt 0 ] - then - exit 1 - fi - - # Ping the vlan - arpingVlan $vlanId $vlanGateway - - # If the ping fails then delete the vlan and return failure. Else, return success. - if [ $? -gt 0 ] - then - deleteVlan $vlanId - exit 1 - else - exit 0 - fi -else - if [ "$op" == "delete" ] - then - # Delete the vlan - deleteVlan $vlanId - - # Always exit with success - exit 0 - fi -fi - - - - - - - - - - - - - From a91f04e759be7132ceda11ec5b3e1ff19d83c4e3 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 14:11:57 -0700 Subject: [PATCH 175/412] migrate volume in Vmware leaves a copy of this volume untracked in secondary storage, this patch removes the volume in secondary storag after volume migration --- .../manager/VmwareStorageManagerImpl.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index 9f1351e96f3..faadbacb0ef 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -491,6 +491,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { hyperHost, volumeId, new DatastoreMO(context, morDatastore), secondaryStorageURL, volumePath); + deleteVolumeDirOnSecondaryStorage(volumeId, secondaryStorageURL); } return new CopyVolumeAnswer(cmd, true, null, result.first(), result.second()); } catch (Throwable e) { @@ -1438,4 +1439,22 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { workingVM = hyperHost.findVmOnHyperHost(uniqueName); return workingVM; } + + + + private String deleteVolumeDirOnSecondaryStorage(long volumeId, String secStorageUrl) throws Exception { + String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl); + String volumeMountRoot = secondaryMountPoint + "/" + getVolumeRelativeDirInSecStroage(volumeId); + + return deleteDir(volumeMountRoot); + } + + private String deleteDir(String dir) { + synchronized(dir.intern()) { + Script command = new Script(false, "rm", _timeout, s_logger); + command.add("-rf"); + command.add(dir); + return command.execute(); + } + } } From 83c13fcf27a8635a743ba97c736c0fae1f66c9b6 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Wed, 29 May 2013 14:24:01 -0700 Subject: [PATCH 176/412] CLOUDSTACK-2614: Fix the permission of patchviasocket.pl It's non-executable now, which cause trouble on deb package. --- scripts/vm/hypervisor/kvm/patchviasocket.pl | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/vm/hypervisor/kvm/patchviasocket.pl diff --git a/scripts/vm/hypervisor/kvm/patchviasocket.pl b/scripts/vm/hypervisor/kvm/patchviasocket.pl old mode 100644 new mode 100755 From 3ae00f4b2fbbb305b783cff880ac92c3caf74871 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 14:44:47 -0700 Subject: [PATCH 177/412] update volume API needs to respect the account and domain --- server/src/com/cloud/storage/VolumeManagerImpl.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 4b654eb2253..aedb68e1316 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -358,6 +358,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { throws ResourceAllocationException { Account caller = UserContext.current().getCaller(); long ownerId = cmd.getEntityOwnerId(); + Account owner = _accountDao.findById(ownerId); Long zoneId = cmd.getZoneId(); String volumeName = cmd.getVolumeName(); String url = cmd.getUrl(); @@ -367,7 +368,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { validateVolume(caller, ownerId, zoneId, volumeName, url, format); - VolumeVO volume = persistVolume(caller, ownerId, zoneId, volumeName, + VolumeVO volume = persistVolume(owner, zoneId, volumeName, url, cmd.getFormat()); VolumeInfo vol = this.volFactory.getVolume(volume.getId()); @@ -700,7 +701,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { return UUID.randomUUID().toString(); } - private VolumeVO persistVolume(Account caller, long ownerId, Long zoneId, + private VolumeVO persistVolume(Account owner, Long zoneId, String volumeName, String url, String format) { Transaction txn = Transaction.currentTxn(); @@ -708,20 +709,17 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1, new Long(-1), null, null, 0, Volume.Type.DATADISK); - Account owner = (caller.getId() == ownerId) ? caller : _accountMgr - .getActiveAccountById(ownerId); volume.setPoolId(null); volume.setDataCenterId(zoneId); volume.setPodId(null); - volume.setAccountId(ownerId); + volume.setAccountId(owner.getAccountId()); + volume.setDomainId(owner.getDomainId()); long diskOfferingId = _diskOfferingDao.findByUniqueName( "Cloud.com-Custom").getId(); volume.setDiskOfferingId(diskOfferingId); // volume.setSize(size); volume.setInstanceId(null); volume.setUpdated(new Date()); - volume.setDomainId((owner == null) ? Domain.ROOT_DOMAIN : owner - .getDomainId()); volume = _volsDao.persist(volume); try { From a4d428434daa64221771597985cefa5f70741b96 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 15:10:49 -0700 Subject: [PATCH 178/412] Don't use volume name as snapshot name because volume name may incude space, \ and other special character, which cannot be part of path name in vmware after this commit, snapshot name is volumeUuid_timestring. --- .../com/cloud/storage/snapshot/SnapshotManagerImpl.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 26aae48ba38..92d80ee6cc8 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1033,12 +1033,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, // Snapshot Name: VMInstancename + volumeName + timeString String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT); - VMInstanceVO vmInstance = _vmDao.findById(volume.getInstanceId()); - String vmDisplayName = "detached"; - if (vmInstance != null) { - vmDisplayName = vmInstance.getHostName(); - } - String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString; + String snapshotName = volume.getUuid() + "_" + timeString; // Create the Snapshot object and save it so we can return it to the // user From 56535a7a83f9871f97023e84f87bc9d65aa0f868 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 15:18:20 -0700 Subject: [PATCH 179/412] zero size of template/volume being registered causes infinite loop on SSVM, the fix is if the size is 0, return success without download --- .../com/cloud/storage/template/HttpTemplateDownloader.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/com/cloud/storage/template/HttpTemplateDownloader.java b/core/src/com/cloud/storage/template/HttpTemplateDownloader.java index 628ad64c0dc..c8aac27da8c 100644 --- a/core/src/com/cloud/storage/template/HttpTemplateDownloader.java +++ b/core/src/com/cloud/storage/template/HttpTemplateDownloader.java @@ -250,6 +250,13 @@ public class HttpTemplateDownloader implements TemplateDownloader { } } else { remoteSize2 = Long.parseLong(contentLengthHeader.getValue()); + if ( remoteSize2 == 0 ) { + status = TemplateDownloader.Status.DOWNLOAD_FINISHED; + String downloaded = "(download complete remote=" + remoteSize + "bytes)"; + errorString = "Downloaded " + totalBytes + " bytes " + downloaded; + downloadTime = 0; + return 0; + } } if (remoteSize == 0) { From 354d259d5513c3788e43ee4662984bd69e07b629 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 15:37:13 -0700 Subject: [PATCH 180/412] volume size is virtual disk size not physical size, so uploaded volume should use virtual size --- server/src/com/cloud/storage/download/DownloadMonitorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java index 220cbffd5a3..663ab4a7fc0 100755 --- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java +++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java @@ -552,7 +552,7 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor //Create usage event long size = -1; if(volumeHost!=null){ - size = volumeHost.getPhysicalSize(); + size = volumeHost.getSize(); volume.setSize(size); this._volumeDao.update(volume.getId(), volume); } From a8bb62c35cb4ed93fb8cdb257c00cea0576a08d0 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 15:48:23 -0700 Subject: [PATCH 181/412] lockRow doesn't work without transaction, start transaction before lockRow --- server/src/com/cloud/network/NetworkManagerImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index cc149a542fd..70cb14f1f07 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2614,7 +2614,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L public boolean shutdownNetwork(long networkId, ReservationContext context, boolean cleanupElements) { boolean result = false; Transaction txn = Transaction.currentTxn(); - + txn.start(); NetworkVO network = _networksDao.lockRow(networkId, true); if (network == null) { s_logger.debug("Unable to find network with id: " + networkId); @@ -2625,7 +2625,6 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return false; } - txn.start(); if (isSharedNetworkWithServices(network)) { network.setState(Network.State.Shutdown); _networksDao.update(network.getId(), network); From ddba994a97ac96fff674ec8248168e9b60351ab7 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 15:57:25 -0700 Subject: [PATCH 182/412] unitNumber is per adapter/controller not per VM in wmware, after this patch, you can plug in up to 10 NICs to vmware VM --- .../vmware/resource/VmwareResource.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 630d1b42e50..0a15d4b5b3d 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -2405,6 +2405,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[totalChangeDevices]; int i = 0; + int ideUnitNumber = 0; + int scsiUnitNumber =0; + int nicUnitNumber = 0; int ideControllerKey = vmMo.getIDEDeviceControllerKey(); int scsiControllerKey = vmMo.getScsiDeviceControllerKey(); int controllerKey; @@ -2429,7 +2432,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, String.format("[%s] systemvm/%s", secDsMo.getName(), mgr.getSystemVMIsoFileNameOnDatastore()), - secDsMo.getMor(), true, true, i, i + 1); + secDsMo.getMor(), true, true, ideUnitNumber++, i + 1); deviceConfigSpecArray[i].setDevice(isoInfo.first()); if (isoInfo.second()) { if(s_logger.isDebugEnabled()) @@ -2440,7 +2443,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); } - i++; } else { // we will always plugin a CDROM device if (volIso != null && volIso.getPath() != null && !volIso.getPath().isEmpty()) { @@ -2449,7 +2451,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa assert (isoDatastoreInfo.second() != null); deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, i, i + 1); + Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber++, i + 1); deviceConfigSpecArray[i].setDevice(isoInfo.first()); if (isoInfo.second()) { if(s_logger.isDebugEnabled()) @@ -2462,7 +2464,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } else { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, null, true, true, i, i + 1); + Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, null, true, true, ideUnitNumber++, i + 1); deviceConfigSpecArray[i].setDevice(isoInfo.first()); if (isoInfo.second()) { if(s_logger.isDebugEnabled()) @@ -2476,9 +2478,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); } } - i++; } - + i++; for (VolumeTO vol : sortVolumesByDeviceId(disks)) { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); @@ -2512,17 +2513,20 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String[] diskChain = _gson.fromJson(chainInfo, String[].class); if (diskChain == null || diskChain.length < 1) { s_logger.warn("Empty previously-saved chain info, fall back to the original"); - device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, new String[] { datastoreDiskPath }, volumeDsDetails.first(), i, i + 1); + device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, new String[] { datastoreDiskPath }, volumeDsDetails.first(), + (controllerKey==ideControllerKey)?ideUnitNumber++:scsiUnitNumber++, i + 1); } else { s_logger.info("Attach the disk with stored chain info: " + chainInfo); for (int j = 0; j < diskChain.length; j++) { diskChain[j] = String.format("[%s] %s", volumeDsDetails.second().getName(), diskChain[j]); } - device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, diskChain, volumeDsDetails.first(), i, i + 1); + device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, diskChain, volumeDsDetails.first(), + (controllerKey==ideControllerKey)?ideUnitNumber++:scsiUnitNumber++, i + 1); } } else { - device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, new String[] { datastoreDiskPath }, volumeDsDetails.first(), i, i + 1); + device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, new String[] { datastoreDiskPath }, volumeDsDetails.first(), + (controllerKey==ideControllerKey)?ideUnitNumber++:scsiUnitNumber++, i + 1); } deviceConfigSpecArray[i].setDevice(device); deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); @@ -2551,10 +2555,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa ManagedObjectReference dvsMor = dataCenterMo.getDvSwitchMor(networkInfo.first()); dvSwitchUuid = dataCenterMo.getDvSwitchUuid(dvsMor); s_logger.info("Preparing NIC device on dvSwitch : " + dvSwitchUuid); - nic = VmwareHelper.prepareDvNicDevice(vmMo, networkInfo.first(), nicDeviceType, networkInfo.second(), dvSwitchUuid, nicTo.getMac(), i, i + 1, true, true); + nic = VmwareHelper.prepareDvNicDevice(vmMo, networkInfo.first(), nicDeviceType, networkInfo.second(), dvSwitchUuid, nicTo.getMac(), nicUnitNumber++, i + 1, true, true); } else { s_logger.info("Preparing NIC device on network " + networkInfo.second()); - nic = VmwareHelper.prepareNicDevice(vmMo, networkInfo.first(), nicDeviceType, networkInfo.second(), nicTo.getMac(), i, i + 1, true, true); + nic = VmwareHelper.prepareNicDevice(vmMo, networkInfo.first(), nicDeviceType, networkInfo.second(), nicTo.getMac(), nicUnitNumber++, i + 1, true, true); } deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); From fdc9f10cc17019be8949800e6ecc4a15dfc84e7a Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 16:01:22 -0700 Subject: [PATCH 183/412] fix , Windows 2008 32bit instance can't get IP address, normally, in dhcp reply, the target ip is allocated ip for VM. but windows 2008 32bit has special field in dhcp reply, which makes dhcp reply use 255.255.255.255 as target ip, which is blocked by SG rule, --- scripts/vm/hypervisor/xenserver/vmops | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index d18eca836b8..650e95535e0 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -541,6 +541,7 @@ def default_ebtables_rules(): util.pread2(['ebtables', '-N', 'DEFAULT_EBTABLES']) util.pread2(['ebtables', '-A', 'FORWARD', '-j' 'DEFAULT_EBTABLES']) util.pread2(['ebtables', '-A', 'DEFAULT_EBTABLES', '-p', 'IPv4', '--ip-dst', '255.255.255.255', '--ip-proto', 'udp', '--ip-dport', '67', '-j', 'ACCEPT']) + util.pread2(['ebtables', '-A', 'DEFAULT_EBTABLES', '-p', 'IPv4', '--ip-dst', '255.255.255.255', '--ip-proto', 'udp', '--ip-dport', '68', '-j', 'ACCEPT']) util.pread2(['ebtables', '-A', 'DEFAULT_EBTABLES', '-p', 'ARP', '--arp-op', 'Request', '-j', 'ACCEPT']) util.pread2(['ebtables', '-A', 'DEFAULT_EBTABLES', '-p', 'ARP', '--arp-op', 'Reply', '-j', 'ACCEPT']) # deny mac broadcast and multicast From 271cf92ab7f58b19dbdae056ce35144d31f0604f Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 16:27:08 -0700 Subject: [PATCH 184/412] allow subdomain access parent domain's network --- server/src/com/cloud/network/NetworkManagerImpl.java | 3 +++ server/src/com/cloud/network/NetworkModelImpl.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 70cb14f1f07..b92ef4b7dfa 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1544,6 +1544,9 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L finalizeServicesAndProvidersForNetwork(offering, plan.getPhysicalNetworkId()))); if (domainId != null && aclType == ACLType.Domain) { + if (subdomainAccess == null ) { + subdomainAccess = true; + } _networksDao.addDomainToNetwork(id, domainId, subdomainAccess); } diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index f6bd646a91d..6b63eadd4b2 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -1673,7 +1673,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { if (networkDomainMap.subdomainAccess) { Set parentDomains = _domainMgr.getDomainParentIds(domainId); - if (parentDomains.contains(domainId)) { + if (parentDomains.contains(networkDomainId)) { return true; } } From 51930405d9998a352071f4ec8104c0e03300e389 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 16:51:42 -0700 Subject: [PATCH 185/412] Don't need to handle dhcp entry differently for different guest OS --- .../router/VirtualNetworkApplianceManagerImpl.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index b969be25fde..9e01aa350a6 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -3370,20 +3370,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); Nic defaultNic = findGatewayIp(vm.getId()); String gatewayIp = defaultNic.getGateway(); - boolean needGateway = true; if (gatewayIp != null && !gatewayIp.equals(nic.getGateway())) { - needGateway = false; - GuestOSVO guestOS = _guestOSDao.findById(vm.getGuestOSId()); - // Do set dhcp:router option for non-default nic on certain OS(including Windows), and leave other OS unset. - // Because some OS(e.g. CentOS) would set routing on wrong interface - for (String name : _guestOSNeedGatewayOnNonDefaultNetwork) { - if (guestOS.getDisplayName().startsWith(name)) { - needGateway = true; - break; - } - } - } - if (!needGateway) { gatewayIp = "0.0.0.0"; } dhcpCommand.setDefaultRouter(gatewayIp); From 7b8ae4d5110204dc41375219cdd3ce68ebec2f2a Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 17:05:03 -0700 Subject: [PATCH 186/412] virt-what does't report correct hypervisor on some new platform, use /proc/xen for XS --- patches/systemvm/debian/config/etc/init.d/cloud-early-config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patches/systemvm/debian/config/etc/init.d/cloud-early-config b/patches/systemvm/debian/config/etc/init.d/cloud-early-config index ca3b970210b..7be8663ff51 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -87,14 +87,13 @@ EOF hypervisor() { [ -d /proc/xen ] && mount -t xenfs none /proc/xen + [ -d /proc/xen ] && echo "xen-domU" && return 0 local try=$([ -x /usr/sbin/virt-what ] && virt-what | tail -1) [ "$try" != "" ] && echo $try && return 0 vmware-checkvm &> /dev/null && echo "vmware" && return 0 - [ -d /proc/xen ] && echo "xen-domU" && return 0 - grep -q QEMU /proc/cpuinfo && echo "kvm" && return 0 grep -q QEMU /var/log/messages && echo "kvm" && return 0 From 68275fbd6bbe8dd83e216cafed7bbb669b5f2836 Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 29 May 2013 17:47:51 -0700 Subject: [PATCH 187/412] only basic zone is pod based network --- .../network/router/VirtualNetworkApplianceManagerImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 9e01aa350a6..b8b88600a76 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1438,9 +1438,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V assert guestNetwork.getTrafficType() == TrafficType.Guest; // 1) Get deployment plan and find out the list of routers - boolean isPodBased = (dest.getDataCenter().getNetworkType() == NetworkType.Basic || - _networkModel.areServicesSupportedInNetwork(guestNetwork.getId(), Service.SecurityGroup)) - && guestNetwork.getTrafficType() == TrafficType.Guest; + boolean isPodBased = (dest.getDataCenter().getNetworkType() == NetworkType.Basic); // dest has pod=null, for Basic Zone findOrDeployVRs for all Pods List destinations = new ArrayList(); From 34943adfc69f406ba78b7cae17b92abcc1c147e9 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Wed, 29 May 2013 17:58:49 -0700 Subject: [PATCH 188/412] CLOUDSTACK-2366: Anti-Affinity - DeleteAffinityGroup API should allow for deletion of Afffinity group even when VMs are associated with it. Changes: - Allowing group to be deleted even when VMs are associated to it. --- .../affinity/AffinityGroupService.java | 5 +---- .../affinitygroup/DeleteAffinityGroupCmd.java | 18 ++++++------------ .../affinity/AffinityGroupServiceImpl.java | 13 ++++++++++--- .../affinity/AffinityApiUnitTest.java | 17 +++-------------- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java b/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java index 26c32c89c1f..7423c4864d5 100644 --- a/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java +++ b/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java @@ -18,7 +18,6 @@ package org.apache.cloudstack.affinity; import java.util.List; -import com.cloud.exception.ResourceInUseException; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; @@ -45,10 +44,8 @@ public interface AffinityGroupService { * @param account * @param domainId * @param affinityGroupName - * @throws ResourceInUseException */ - boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName) - throws ResourceInUseException; + boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName); /** Lists Affinity Groups in your account * @param account diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java index ea4a010ab93..f80e17626fd 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java @@ -30,7 +30,6 @@ import org.apache.log4j.Logger; import com.cloud.async.AsyncJob; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.ResourceInUseException; import com.cloud.user.Account; import com.cloud.user.UserContext; @@ -123,17 +122,12 @@ public class DeleteAffinityGroupCmd extends BaseAsyncCmd { @Override public void execute(){ - try{ - boolean result = _affinityGroupService.deleteAffinityGroup(id, accountName, domainId, name); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete affinity group"); - } - } catch (ResourceInUseException ex) { - s_logger.warn("Exception: ", ex); - throw new ServerApiException(ApiErrorCode.RESOURCE_IN_USE_ERROR, ex.getMessage()); + boolean result = _affinityGroupService.deleteAffinityGroup(id, accountName, domainId, name); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete affinity group"); } } diff --git a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java index efe18c3b375..52112792d14 100644 --- a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java +++ b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java @@ -125,8 +125,7 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro @DB @Override @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_DELETE, eventDescription = "Deleting affinity group") - public boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName) - throws ResourceInUseException { + public boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName) { Account caller = UserContext.current().getCaller(); Account owner = _accountMgr.finalizeOwner(caller, account, domainId, null); @@ -164,7 +163,15 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro List affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupId); if (!affinityGroupVmMap.isEmpty()) { - throw new ResourceInUseException("Cannot delete affinity group when it's in use by virtual machines"); + SearchBuilder listByAffinityGroup = _affinityGroupVMMapDao.createSearchBuilder(); + listByAffinityGroup.and("affinityGroupId", listByAffinityGroup.entity().getAffinityGroupId(), + SearchCriteria.Op.EQ); + listByAffinityGroup.done(); + SearchCriteria sc = listByAffinityGroup.create(); + sc.setParameters("affinityGroupId", affinityGroupId); + + _affinityGroupVMMapDao.lockRows(sc, null, true); + _affinityGroupVMMapDao.remove(sc); } _affinityGroupDao.expunge(affinityGroupId); diff --git a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java index 484b044e28e..24c5d3d4e10 100644 --- a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java +++ b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.affinity; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; @@ -64,6 +65,8 @@ import com.cloud.user.AccountVO; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.UserVmDao; @@ -172,20 +175,6 @@ public class AffinityApiUnitTest { _affinityService.deleteAffinityGroup(null, "user", domainId, null); } - @Test(expected = ResourceInUseException.class) - public void deleteAffinityGroupInUse() throws ResourceInUseException { - List affinityGroupVmMap = new ArrayList(); - AffinityGroupVMMapVO mapVO = new AffinityGroupVMMapVO(20L, 10L); - affinityGroupVmMap.add(mapVO); - when(_affinityGroupVMMapDao.listByAffinityGroup(20L)).thenReturn(affinityGroupVmMap); - - AffinityGroupVO groupVO = new AffinityGroupVO(); - when(_groupDao.findById(20L)).thenReturn(groupVO); - when(_groupDao.lockRow(20L, true)).thenReturn(groupVO); - - _affinityService.deleteAffinityGroup(20L, "user", domainId, null); - } - @Test(expected = InvalidParameterValueException.class) public void updateAffinityGroupVMRunning() throws ResourceInUseException { From 85062a75ae363ec37d00cd1beb92a29b408cd238 Mon Sep 17 00:00:00 2001 From: Ashutosh Date: Thu, 23 May 2013 09:23:14 +0530 Subject: [PATCH 189/412] Test cases for assignVirtualMachne feature. Signed-off-by: Prasanna Santhanam --- test/integration/component/test_assign_vm.py | 458 +++++++++++++++++++ tools/marvin/marvin/integration/lib/base.py | 13 + 2 files changed, 471 insertions(+) create mode 100644 test/integration/component/test_assign_vm.py diff --git a/test/integration/component/test_assign_vm.py b/test/integration/component/test_assign_vm.py new file mode 100644 index 00000000000..1dc93a81417 --- /dev/null +++ b/test/integration/component/test_assign_vm.py @@ -0,0 +1,458 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. + +""" +""" +#Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.integration.lib.base import (Account, + Domain, + User, + Project, + Volume, + Snapshot, + DiskOffering, + ServiceOffering, + VirtualMachine) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + cleanup_resources, + list_volumes, + update_resource_limit, + list_networks, + list_snapshots, + list_virtual_machines) + +def log_test_exceptions(func): + def _log_test_exceptions(self, *args, **kwargs): + try: + func(self, *args, **kwargs) + except Exception as e: + self.debug('Test %s Failed due to Exception=%s' % (func, e)) + raise e + return _log_test_exceptions + +class Services: + """Test service data for:Change the ownershop of + VM/network/datadisk/snapshot/template/ISO from one account to any other account. + """ + def __init__(self): + self.services = {"domain" : {"name": "Domain",}, + "account" : {"email" : "test@test.com", + "firstname" : "Test", + "lastname" : "User", + "username" : "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password" : "password",}, + "user" : {"email" : "user@test.com", + "firstname": "User", + "lastname" : "User", + "username" : "User", + # Random characters are appended for unique + # username + "password" : "fr3sca",}, + "project" : {"name" : "Project", + "displaytext" : "Test project",}, + "volume" : {"diskname" : "TestDiskServ", + "max" : 6,}, + "disk_offering" : {"displaytext" : "Small", + "name" : "Small", + "disksize" : 1}, + "virtual_machine" : {"displayname" : "testserver", + "username" : "root",# VM creds for SSH + "password" : "password", + "ssh_port" : 22, + "hypervisor" : 'XenServer', + "privateport" : 22, + "publicport" : 22, + "protocol" : 'TCP',}, + "service_offering" : {"name" : "Tiny Instance", + "displaytext" : "Tiny Instance", + "cpunumber" : 1, + "cpuspeed" : 100,# in MHz + "memory" : 128}, + #"storagetype" : "local"}, + "sleep" : 60, + "ostype" : 'CentOS 5.3 (64-bit)',# CentOS 5.3 (64-bit) + } + +class TestVMOwnership(cloudstackTestCase): + @classmethod + def setUpClass(cls): + cls._cleanup = [] + cls.api_client = super(TestVMOwnership, + cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone Domain and create Domains and sub Domains. + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + # Get and set template id for VM creation. + cls.template = get_template(cls.api_client, + cls.zone.id, + cls.services["ostype"]) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + def create_domain_account_user(parentDomain=None): + domain = Domain.create(cls.api_client, + cls.services["domain"], + parentdomainid=parentDomain.id if parentDomain else None) + cls._cleanup.append(domain) + # Create an Account associated with domain + account = Account.create(cls.api_client, + cls.services["account"], + domainid=domain.id) + cls._cleanup.append(account) + # Create an User, Project, Volume associated with account + user = User.create(cls.api_client, + cls.services["user"], + account=account.name, + domainid=account.domainid) + cls._cleanup.append(user) + project = Project.create(cls.api_client, + cls.services["project"], + account=account.name, + domainid=account.domainid) + cls._cleanup.append(project) + volume = Volume.create(cls.api_client, + cls.services["volume"], + zoneid=cls.zone.id, + account=account.name, + domainid=account.domainid, + diskofferingid=cls.disk_offering.id) + cls._cleanup.append(volume) + return {'domain':domain, 'account':account, 'user':user, 'project':project, 'volume':volume} + + # Create disk offerings. + try: + cls.disk_offering = DiskOffering.create(cls.api_client, + cls.services["disk_offering"]) + # Create service offerings. + cls.service_offering = ServiceOffering.create(cls.api_client, + cls.services["service_offering"]) + # Cleanup + cls._cleanup = [cls.service_offering] + # Create domain, account, user, project and volumes. + cls.domain_account_user1 = create_domain_account_user() + cls.domain_account_user2 = create_domain_account_user() + cls.sdomain_account_user1 = create_domain_account_user(cls.domain_account_user1['domain']) + cls.sdomain_account_user2 = create_domain_account_user(cls.domain_account_user2['domain']) + cls.ssdomain_account_user2 = create_domain_account_user(cls.sdomain_account_user2['domain']) + except Exception as e: + raise e + return + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.api_client, reversed(cls._cleanup)) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + self.apiclient = self.api_client#self.testClient.getApiClient() + #self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + self.snapshot = None + return + + def create_vm(self, + account, + domain, + isRunning=False, + project =None, + limit =None, + pfrule =False, + lbrule =None, + natrule =None, + volume =None, + snapshot =False): + #TODO: Implemnt pfrule/lbrule/natrule + self.debug("Deploying instance in the account: %s" % account.name) + self.virtual_machine = VirtualMachine.create(self.apiclient, + self.services["virtual_machine"], + accountid=account.name, + domainid=domain.id, + serviceofferingid=self.service_offering.id, + mode=self.zone.networktype if pfrule else 'basic', + projectid=project.id if project else None) + self.debug("Deployed instance in account: %s" % account.name) + list_virtual_machines(self.apiclient, + id=self.virtual_machine.id) + if snapshot: + volumes = list_volumes(self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True) + self.snapshot = Snapshot.create(self.apiclient, + volumes[0].id, + account=account.name, + domainid=account.domainid) + if volume: + self.virtual_machine.attach_volume(self.apiclient, + volume) + if not isRunning: + self.virtual_machine.stop(self.apiclient) + self.cleanup.append(self.virtual_machine) + + def check_vm_is_moved_in_account_domainid(self, account): + list_vm_response = list_virtual_machines(self.api_client, + id=self.virtual_machine.id, + account=account.name, + domainid=account.domainid) + self.debug('VM=%s moved to account=%s and domainid=%s' % (list_vm_response, account.name, account.domainid)) + self.assertNotEqual(len(list_vm_response), 0, 'Unable to move VM to account=%s domainid=%s' % (account.name, account.domainid)) + + def tearDown(self): + try: + self.debug("Cleaning up the resources") + cleanup_resources(self.apiclient, reversed(self.cleanup)) + self.debug("Cleanup complete!") + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_01_move_across_different_domains(self): + """Test as root, stop a VM from domain1 and attempt to move it to account in domain2 + """ + # Validate the following: + # 1. deploy VM in domain_1 + # 2. stop VM in domain_1 + # 3. assignVirtualMachine to domain_2 + self.create_vm(self.domain_account_user1['account'], self.domain_account_user1['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.domain_account_user2['account'].name ,self.domain_account_user2['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.domain_account_user2['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_02_move_across_subdomains(self): + """Test as root, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in subdomain_1 + # 2. stop VM in subdomain_1 + # 3. assignVirtualMachine to subdomain_2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.sdomain_account_user2['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_03_move_from_domain_to_subdomain(self): + """Test as root stop a VM from domain1 and attempt to move it to subdomain1 + """ + # Validate the following: + # 1. deploy VM in domain_1 + # 2. stop VM in domain_1 + # 3. assignVirtualMachine to subdomain_1 + self.create_vm(self.domain_account_user1['account'], self.domain_account_user1['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.sdomain_account_user1['account'].name ,self.sdomain_account_user1['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.sdomain_account_user1['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_04_move_from_domain_to_sub_of_subdomain(self): + """Test as root, stop a VM from domain1 and attempt to move it to sub-subdomain1 + """ + # Validate the following: + # 1. deploy VM in domain_2 + # 2. stop VM in domain_2 + # 3. assignVirtualMachine to sub subdomain_2 + self.create_vm(self.domain_account_user2['account'], self.domain_account_user2['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.ssdomain_account_user2['account'].name ,self.ssdomain_account_user2['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.ssdomain_account_user2['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_05_move_to_domain_from_sub_of_subdomain(self): + """Test as root, stop a VM from sub-subdomain1 and attempt to move it to domain1 + """ + # Validate the following: + # 1. deploy VM in sub subdomain2 + # 2. stop VM in sub subdomain2 + # 3. assignVirtualMachine to sub domain2 + self.create_vm(self.ssdomain_account_user2['account'], self.ssdomain_account_user2['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.domain_account_user2['account'].name ,self.domain_account_user2['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.domain_account_user2['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_06_move_to_domain_from_subdomain(self): + """Test as root, stop a Vm from subdomain1 and attempt to move it to domain1 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 + # 2. stop VM in sub subdomain1 + # 3. assignVirtualMachine to domain1 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.domain_account_user1['account'].name ,self.domain_account_user1['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.domain_account_user1['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_07_move_across_subdomain(self): + """Test as root, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 + # 2. stop VM in sub subdomain1 + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.sdomain_account_user2['account']) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_08_move_across_subdomain_network_create(self): + """Test as root, stop a VM from subdomain1 and attempt to move it to subdomain2, network should get craeted + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 + # 2. stop VM in sub subdomain1 + # 3. assignVirtualMachine to subdomain2 network should get created + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain']) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + self.check_vm_is_moved_in_account_domainid(self.sdomain_account_user2['account']) + networks = list_networks(self.apiclient, + account=self.sdomain_account_user2['account'].name, + domainid=self.sdomain_account_user2['domain'].id) + self.assertEqual(isinstance(networks, list), + True, + "Check for list networks response return valid data") + self.assertNotEqual(len(networks), + 0, + "Check list networks response") + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_09_move_across_subdomain(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 + # 2. stop VM in sub subdomain1 + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain']) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_10_move_across_subdomain_vm_running(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'],isRunning=True) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_11_move_across_subdomain_vm_pfrule(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 with PF rule set. + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'],pfrule=True) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_12_move_across_subdomain_vm_volumes(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 with volumes. + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'],volume=self.sdomain_account_user1['volume']) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + # Check all volumes attached to same VM + list_volume_response = list_volumes(self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True) + self.assertEqual(isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list") + + self.assertNotEqual(list_volume_response[0].domainid, self.sdomain_account_user2['domain'].id, "Volume ownership not changed.") + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_13_move_across_subdomain_vm_snapshot(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 with snapshot. + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'], snapshot=True) + self.virtual_machine.assign_virtual_machine(self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + snapshots = list_snapshots(self.apiclient, + id=self.snapshot.id) + self.assertEqual(snapshots, + None, + "Snapshots stil present for a vm in domain") + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_14_move_across_subdomain_vm_project(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 with snapshot. + # 3. assignVirtualMachine to subdomain2 + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'], project=self.sdomain_account_user1['project']) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_15_move_across_subdomain_account_limit(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 when limit reached + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 when account limit is reached. + # 3. assignVirtualMachine to subdomain2 + update_resource_limit(self.apiclient, + 0, # VM Instances + account=self.sdomain_account_user2['account'].name, + domainid=self.sdomain_account_user2['domain'].id, + max=0) + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'], snapshot=True) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) + + @attr(tags = ["advanced"]) + @log_test_exceptions + def test_16_move_across_subdomain_volume_and_account_limit(self): + """Test as domain admin, stop a VM from subdomain1 and attempt to move it to subdomain2 volumes are attached and limit reached + """ + # Validate the following: + # 1. deploy VM in sub subdomain1 when account limit is reached. + # 3. assignVirtualMachine to subdomain2 + update_resource_limit( + self.apiclient, + 0, # VM Instances + account=self.sdomain_account_user2['account'].name, + domainid=self.sdomain_account_user2['domain'].id, + max=0) + self.create_vm(self.sdomain_account_user1['account'], self.sdomain_account_user1['domain'], snapshot=True, volume=self.sdomain_account_user1['volume']) + self.assertRaises(Exception, self.virtual_machine.assign_virtual_machine, self.apiclient, self.sdomain_account_user2['account'].name ,self.sdomain_account_user2['domain'].id) diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index ec1c34e12c7..6b3be75e896 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -527,6 +527,19 @@ class VirtualMachine: if isinstance(response, list): return response[0].password + def assign_virtual_machine(self, apiclient, account, domainid): + """Move a user VM to another user under same domain.""" + + cmd = assignVirtualMachine.assignVirtualMachineCmd() + cmd.virtualmachineid = self.id + cmd.account = account + cmd.domainid = domainid + try: + response = apiclient.assignVirtualMachine(cmd) + return response + except Exception as e: + raise Exception("assignVirtualMachine failed - %s" %e) + class Volume: """Manage Volume Life cycle From 718bc97a7e424c1be5f9b04da283393c2ac772aa Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Thu, 30 May 2013 11:12:20 +0530 Subject: [PATCH 190/412] CLOUDSTACK-1824: Stop service displayed as cloud-management service cloudstack-management is being displayed as cloud-management Signed-off-by: Prasanna Santhanam --- packaging/centos63/cloud-management.rc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packaging/centos63/cloud-management.rc b/packaging/centos63/cloud-management.rc index 6fd435b555d..35f31b28538 100755 --- a/packaging/centos63/cloud-management.rc +++ b/packaging/centos63/cloud-management.rc @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -# cloud-management This shell script takes care of starting and stopping Tomcat +# cloudstack-management This shell script takes care of starting and stopping Tomcat # # chkconfig: - 80 20 # @@ -56,13 +56,13 @@ stop() { let count="${count}+1" done if [ "$(ps --pid $pid | grep -c $pid)" -eq "0" ]; then - log_success_msg "Stopping cloud-management:" + log_success_msg "Stopping ${NAME}:" else - log_failure_msg "Stopping cloud-management:" + log_failure_msg "Stopping ${NAME}:" fi else - echo "Cannot find PID file of Cloud-management" - log_failure_msg "Stopping cloud-management:" + echo "Cannot find PID file of ${NAME}" + log_failure_msg "Stopping ${NAME}:" fi } From 4b9b475336d8e97403f7b16ec79b1826e054185b Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 30 May 2013 11:23:12 +0530 Subject: [PATCH 191/412] Add method getVolumeRelativeDirInSecStroage to fix compile error. --- .../hypervisor/vmware/manager/VmwareStorageManagerImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index faadbacb0ef..00255216cf3 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -1457,4 +1457,8 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { return command.execute(); } } + + private static String getVolumeRelativeDirInSecStroage(long volumeId) { + return "volumes/" + volumeId; + } } From 62413eec1067ab5d92977fab93a2fc88d4247bb0 Mon Sep 17 00:00:00 2001 From: Saksham Srivastava Date: Wed, 29 May 2013 18:17:00 +0530 Subject: [PATCH 192/412] CLOUDSTACK-2130: Fix resource_name for old NIC ID Signed-off-by: Mice Xia --- server/src/com/cloud/vm/UserVmManagerImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index a734d44c800..a55c6f8fb81 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1026,6 +1026,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use Network oldDefaultNetwork = null; oldDefaultNetwork = _networkModel.getDefaultNetworkForVm(vmId); + String oldNicIdString = Long.toString(_networkModel.getDefaultNic(vmId).getId()); long oldNetworkOfferingId = -1L; if(oldDefaultNetwork!=null) { @@ -1065,13 +1066,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use String nicIdString = Long.toString(nic.getId()); long newNetworkOfferingId = network.getNetworkOfferingId(); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), - vmInstance.getId(), nicIdString, oldNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); + vmInstance.getId(), oldNicIdString, oldNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), nicIdString, newNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), nicIdString, newNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), - vmInstance.getId(), nicIdString, oldNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); + vmInstance.getId(), oldNicIdString, oldNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); return _vmDao.findById(vmInstance.getId()); } From 4989f73fdaab23cb8d78131163eeb9a0e1ad2856 Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Thu, 30 May 2013 15:26:39 +0800 Subject: [PATCH 193/412] fix CLOUDSTACK-2128, should save network offering id into usage event --- server/src/com/cloud/vm/VirtualMachineManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index c65514b1b31..568fe558247 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -2836,7 +2836,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac long isDefault = (nic.isDefaultNic()) ? 1 : 0; // insert nic's Id into DB as resource_name UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmVO.getAccountId(), - vmVO.getDataCenterId(), vmVO.getId(), Long.toString(nic.getId()), nic.getNetworkId(), + vmVO.getDataCenterId(), vmVO.getId(), Long.toString(nic.getId()), network.getNetworkOfferingId(), null, isDefault, VirtualMachine.class.getName(), vmVO.getUuid()); return nic; } else { From 17267794adb2bab923fb20515a7b943780d61921 Mon Sep 17 00:00:00 2001 From: Saksham Srivastava Date: Thu, 30 May 2013 11:00:03 +0530 Subject: [PATCH 194/412] CLOUDSTACK-681: Dedicated Resources - Explicit Dedication, Private zone, pod, cluster or host. This feature allows a user to deploy VMs only in the resources dedicated to his account or domain. 1. Resources(Zones, Pods, Clusters or hosts) can be dedicated to an account or domain. Implemented 12 new APIs to dedicate/list/release resources: - dedicateZone, listDedicatedZones, releaseDedicatedZone for a Zone. - dedicatePod, listDedicatedPods, releaseDedicatedPod for a Pod. - dedicateCluster, listDedicatedClusters, releaseDedicatedCluster for a Cluster - dedicateHost, listDedicatedHosts, releaseDedicatedHost for a Host. 2. Once a resource(eg. pod) is dedicated to an account, other resources(eg. clusters/hosts) inside that cannot be further dedicated. 3. Once a resource is dedicated to a domain, other resources inside that can be further dedicated to its sub-domain or account. 4. If any resource (eg.cluster) is dedicated to a account/domain, then resources(eg. Pod) above that cannot be dedicated to different accounts/domain (not belonging to the same domain) 5. To use Explicit dedication, user needs to create an Affinity Group of type 'ExplicitDedication' 6. A VM can be deployed with the above affinity group parameter as an input. 7. A new ExplicitDedicationProcessor has been added which will process the affinity group of type 'Explicit Dedication' for a deployment of a VM that demands dedicated resources. This processor implements the AffinityGroupProcessor adapter. This processor will update the avoid list. 8. A VM requesting dedication will be deployed on dedicatd resources if available with the user account. 9. A VM requesting dedication can also be deployed on the dedicated resources available with the parent domains iff no dedicated resources are available with the current user's account or domain. 10. A VM (without dedication) can be deployed on shared host but not on dedicated hosts. 11. To modify the dedication, the resource has to be released first. 12. Existing Private zone functionality has been redirected to Explicit dedication of zones. 13. Updated the db upgrade schema script. A new table "dedicated_resources" has been added. 14. Added the right permissions in commands.properties 15. Unit tests: For the new APIs and Service, added unit tests under : plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/DedicatedApiUnitTest.java 16. Marvin Test: To dedicate host, create affinity group, deploy-vm, check if vm is deployed on the dedicated host. --- api/src/com/cloud/dc/DedicatedResources.java | 33 + api/src/com/cloud/event/EventTypes.java | 4 + client/pom.xml | 10 + client/tomcatconf/applicationContext.xml.in | 14 +- client/tomcatconf/commands.properties.in | 13 + client/tomcatconf/componentContext.xml.in | 32 +- .../orchestration/CloudOrchestrator.java | 3 +- .../src/com/cloud/dc/dao/ClusterDao.java | 1 + .../src/com/cloud/dc/dao/ClusterDaoImpl.java | 14 +- .../com/cloud/domain/dao/DomainDaoImpl.java | 2 +- .../src/com/cloud/host/dao/HostDao.java | 6 + .../src/com/cloud/host/dao/HostDaoImpl.java | 37 +- .../explicit-dedication/pom.xml | 33 + .../affinity/ExplicitDedicationProcessor.java | 383 ++++++++ plugins/dedicated-resources/pom.xml | 29 + .../api/commands/DedicateClusterCmd.java | 115 +++ .../api/commands/DedicateHostCmd.java | 118 +++ .../api/commands/DedicatePodCmd.java | 120 +++ .../api/commands/DedicateZoneCmd.java | 120 +++ .../commands/ListDedicatedClustersCmd.java | 105 +++ .../api/commands/ListDedicatedHostsCmd.java | 105 +++ .../api/commands/ListDedicatedPodsCmd.java | 105 +++ .../api/commands/ListDedicatedZonesCmd.java | 105 +++ .../commands/ReleaseDedicatedClusterCmd.java | 91 ++ .../api/commands/ReleaseDedicatedHostCmd.java | 91 ++ .../api/commands/ReleaseDedicatedPodCmd.java | 91 ++ .../api/commands/ReleaseDedicatedZoneCmd.java | 91 ++ .../api/response/DedicateClusterResponse.java | 79 ++ .../api/response/DedicateHostResponse.java | 79 ++ .../api/response/DedicatePodResponse.java | 82 ++ .../api/response/DedicateZoneResponse.java | 83 ++ .../manager/DedicatedResourceManagerImpl.java | 816 ++++++++++++++++++ .../dedicated/services/DedicatedService.java | 63 ++ .../manager/DedicatedApiUnitTest.java | 324 +++++++ .../test/resource/dedicatedContext.xml | 45 + plugins/pom.xml | 4 +- .../com/cloud/api/query/QueryManagerImpl.java | 33 +- .../ConfigurationManagerImpl.java | 23 +- .../src/com/cloud/dc/DedicatedResourceVO.java | 136 +++ .../cloud/dc/dao/DedicatedResourceDao.java | 49 ++ .../dc/dao/DedicatedResourceDaoImpl.java | 304 +++++++ .../cloud/resource/ResourceManagerImpl.java | 22 +- .../com/cloud/user/AccountManagerImpl.java | 26 +- .../src/com/cloud/user/DomainManagerImpl.java | 26 + .../src/com/cloud/vm/UserVmManagerImpl.java | 50 +- .../ChildTestConfiguration.java | 11 +- setup/db/create-schema.sql | 1 - setup/db/db/schema-410to420.sql | 27 +- .../component/test_explicit_dedication.py | 231 +++++ tools/apidoc/gen_toc.py | 4 +- 50 files changed, 4348 insertions(+), 41 deletions(-) create mode 100755 api/src/com/cloud/dc/DedicatedResources.java create mode 100644 plugins/affinity-group-processors/explicit-dedication/pom.xml create mode 100644 plugins/affinity-group-processors/explicit-dedication/src/org/apache/cloudstack/affinity/ExplicitDedicationProcessor.java create mode 100644 plugins/dedicated-resources/pom.xml create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java create mode 100644 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java create mode 100755 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java create mode 100755 plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java create mode 100644 plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java create mode 100644 plugins/dedicated-resources/test/resource/dedicatedContext.xml create mode 100644 server/src/com/cloud/dc/DedicatedResourceVO.java create mode 100644 server/src/com/cloud/dc/dao/DedicatedResourceDao.java create mode 100644 server/src/com/cloud/dc/dao/DedicatedResourceDaoImpl.java create mode 100644 test/integration/component/test_explicit_dedication.py diff --git a/api/src/com/cloud/dc/DedicatedResources.java b/api/src/com/cloud/dc/DedicatedResources.java new file mode 100755 index 00000000000..e8e5ab3dffc --- /dev/null +++ b/api/src/com/cloud/dc/DedicatedResources.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package com.cloud.dc; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface DedicatedResources extends InfrastructureEntity, InternalIdentity, Identity{ + long getId(); + Long getDataCenterId(); + Long getPodId(); + Long getClusterId(); + Long getHostId(); + Long getDomainId(); + Long getAccountId(); + String getUuid(); + +} diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 6dfb1ab57b6..ed4ba1254f0 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -438,6 +438,10 @@ public class EventTypes { public static final String EVENT_PORTABLE_IP_RANGE_DELETE = "PORTABLE.IP.RANGE.DELETE"; public static final String EVENT_PORTABLE_IP_TRANSFER = "PORTABLE.IP.TRANSFER"; + // Dedicated Resources + public static final String EVENT_DEDICATE_RESOURCE = "DEDICATE.RESOURCE"; + public static final String EVENT_DEDICATE_RESOURCE_RELEASE = "DEDICATE.RESOURCE.RELEASE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking diff --git a/client/pom.xml b/client/pom.xml index 0c38ecb65d2..ab758eb2a67 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -29,6 +29,11 @@ org.apache.cloudstack cloud-plugin-acl-static-role-based ${project.version} + + + org.apache.cloudstack + cloud-plugin-dedicated-resources + ${project.version} org.apache.cloudstack @@ -136,6 +141,11 @@ cloud-plugin-planner-implicit-dedication ${project.version} + + org.apache.cloudstack + cloud-plugin-explicit-dedication + ${project.version} + org.apache.cloudstack cloud-plugin-host-allocator-random diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index edf83a94ae6..26b698af144 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -93,7 +93,7 @@ - + @@ -156,9 +156,17 @@ - + - + + + + + + + + + diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index fd5479f44b4..f0ae7bf3dd7 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -641,3 +641,16 @@ listInternalLoadBalancerVMs=1 ### Network Isolation methods listing listNetworkIsolationMethods=1 +#### Dedicated Resource commands +dedicateZone=1 +dedicatePod=1 +dedicateCluster=1 +dedicateHost=1 +releaseDedicatedZone=1 +releaseDedicatedPod=1 +releaseDedicatedCluster=1 +releaseDedicatedHost=1 +listDedicatedZones=1 +listDedicatedPods=1 +listDedicatedClusters=1 +listDedicatedHosts=1 diff --git a/client/tomcatconf/componentContext.xml.in b/client/tomcatconf/componentContext.xml.in index e946f448d90..fcbcc1bb50b 100644 --- a/client/tomcatconf/componentContext.xml.in +++ b/client/tomcatconf/componentContext.xml.in @@ -24,28 +24,27 @@ xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/tx + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - @@ -171,7 +170,7 @@ - + @@ -260,13 +259,22 @@ - + + - + + + + + + + diff --git a/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java index 963e4d7d967..ca299ea45bc 100755 --- a/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java @@ -89,10 +89,9 @@ public class CloudOrchestrator implements OrchestrationService { public CloudOrchestrator() { } - + public VirtualMachineEntity createFromScratch(String uuid, String iso, String os, String hypervisor, String hostName, int cpu, int speed, long memory, List networks, List computeTags, Map details, String owner) { - // TODO Auto-generated method stub return null; } diff --git a/engine/schema/src/com/cloud/dc/dao/ClusterDao.java b/engine/schema/src/com/cloud/dc/dao/ClusterDao.java index 3ce0798a8a2..673888bc2ab 100644 --- a/engine/schema/src/com/cloud/dc/dao/ClusterDao.java +++ b/engine/schema/src/com/cloud/dc/dao/ClusterDao.java @@ -34,4 +34,5 @@ public interface ClusterDao extends GenericDao { Map> getPodClusterIdMap(List clusterIds); List listDisabledClusters(long zoneId, Long podId); List listClustersWithDisabledPods(long zoneId); + List listClustersByDcId(long zoneId); } diff --git a/engine/schema/src/com/cloud/dc/dao/ClusterDaoImpl.java b/engine/schema/src/com/cloud/dc/dao/ClusterDaoImpl.java index 86dc65e05bd..ba2686a4004 100644 --- a/engine/schema/src/com/cloud/dc/dao/ClusterDaoImpl.java +++ b/engine/schema/src/com/cloud/dc/dao/ClusterDaoImpl.java @@ -52,6 +52,7 @@ public class ClusterDaoImpl extends GenericDaoBase implements C protected final SearchBuilder AvailHyperSearch; protected final SearchBuilder ZoneSearch; protected final SearchBuilder ZoneHyTypeSearch; + protected final SearchBuilder ZoneClusterSearch; private static final String GET_POD_CLUSTER_MAP_PREFIX = "SELECT pod_id, id FROM cloud.cluster WHERE cluster.id IN( "; private static final String GET_POD_CLUSTER_MAP_SUFFIX = " )"; @@ -85,12 +86,16 @@ public class ClusterDaoImpl extends GenericDaoBase implements C AvailHyperSearch.and("zoneId", AvailHyperSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); AvailHyperSearch.select(null, Func.DISTINCT, AvailHyperSearch.entity().getHypervisorType()); AvailHyperSearch.done(); + + ZoneClusterSearch = createSearchBuilder(); + ZoneClusterSearch.and("dataCenterId", ZoneClusterSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ZoneClusterSearch.done(); } @Override public List listByZoneId(long zoneId) { SearchCriteria sc = ZoneSearch.create(); - sc.setParameters("dataCenterId", zoneId); + sc.setParameters("dataCenterId", zoneId); return listBy(sc); } @@ -223,6 +228,13 @@ public class ClusterDaoImpl extends GenericDaoBase implements C return customSearch(sc, null); } + @Override + public List listClustersByDcId(long zoneId) { + SearchCriteria sc = ZoneClusterSearch.create(); + sc.setParameters("dataCenterId", zoneId); + return listBy(sc); + } + @Override public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); diff --git a/engine/schema/src/com/cloud/domain/dao/DomainDaoImpl.java b/engine/schema/src/com/cloud/domain/dao/DomainDaoImpl.java index 9460a73dc57..c84aa60897c 100644 --- a/engine/schema/src/com/cloud/domain/dao/DomainDaoImpl.java +++ b/engine/schema/src/com/cloud/domain/dao/DomainDaoImpl.java @@ -288,5 +288,5 @@ public class DomainDaoImpl extends GenericDaoBase implements Dom return parentDomains; } - + } diff --git a/engine/schema/src/com/cloud/host/dao/HostDao.java b/engine/schema/src/com/cloud/host/dao/HostDao.java index 98bdcb470e1..8ceb8f23132 100755 --- a/engine/schema/src/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/com/cloud/host/dao/HostDao.java @@ -80,4 +80,10 @@ public interface HostDao extends GenericDao, StateDao listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId, String haTag); + + List findByPodId(Long podId); + + List findByClusterId(Long clusterId); + + List listByDataCenterId(long id); } diff --git a/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java index 07a42322ce3..810b973e296 100755 --- a/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java @@ -37,6 +37,7 @@ import com.cloud.cluster.agentlb.HostTransferMapVO; import com.cloud.cluster.agentlb.dao.HostTransferMapDao; import com.cloud.cluster.agentlb.dao.HostTransferMapDaoImpl; import com.cloud.dc.ClusterVO; +import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterDaoImpl; import com.cloud.host.Host; @@ -86,6 +87,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder GuidSearch; protected SearchBuilder DcSearch; protected SearchBuilder PodSearch; + protected SearchBuilder ClusterSearch; protected SearchBuilder TypeSearch; protected SearchBuilder StatusSearch; protected SearchBuilder ResourceStateSearch; @@ -201,6 +203,9 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao DcSearch = createSearchBuilder(); DcSearch.and("dc", DcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DcSearch.and("type", DcSearch.entity().getType(), Op.EQ); + DcSearch.and("status", DcSearch.entity().getStatus(), Op.EQ); + DcSearch.and("resourceState", DcSearch.entity().getResourceState(), Op.EQ); DcSearch.done(); ClusterStatusSearch = createSearchBuilder(); @@ -215,9 +220,13 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao TypeNameZoneSearch.done(); PodSearch = createSearchBuilder(); - PodSearch.and("pod", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); + PodSearch.and("podId", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); PodSearch.done(); + ClusterSearch = createSearchBuilder(); + ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + ClusterSearch.done(); + TypeSearch = createSearchBuilder(); TypeSearch.and("type", TypeSearch.entity().getType(), SearchCriteria.Op.EQ); TypeSearch.done(); @@ -373,7 +382,17 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao List hosts = listBy(sc); return hosts.size(); } - + + @Override + public List listByDataCenterId(long id) { + SearchCriteria sc = DcSearch.create(); + sc.setParameters("dcId", id); + sc.setParameters("status", Status.Up); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } @Override public HostVO findByGuid(String guid) { @@ -906,6 +925,20 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return findOneBy(sc); } + @Override + public List findByPodId(Long podId) { + SearchCriteria sc = PodSearch.create(); + sc.setParameters("podId", podId); + return listBy(sc); + } + + @Override + public List findByClusterId(Long clusterId) { + SearchCriteria sc = ClusterSearch.create(); + sc.setParameters("clusterId", clusterId); + return listBy(sc); + } + @Override public List findHypervisorHostInCluster(long clusterId) { SearchCriteria sc = TypeClusterStatusSearch.create(); diff --git a/plugins/affinity-group-processors/explicit-dedication/pom.xml b/plugins/affinity-group-processors/explicit-dedication/pom.xml new file mode 100644 index 00000000000..bb3c595841a --- /dev/null +++ b/plugins/affinity-group-processors/explicit-dedication/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + cloud-plugin-explicit-dedication + Apache CloudStack Plugin - Explicit Dedication Processor + + org.apache.cloudstack + cloudstack-plugins + 4.2.0-SNAPSHOT + ../../pom.xml + + + install + src + + diff --git a/plugins/affinity-group-processors/explicit-dedication/src/org/apache/cloudstack/affinity/ExplicitDedicationProcessor.java b/plugins/affinity-group-processors/explicit-dedication/src/org/apache/cloudstack/affinity/ExplicitDedicationProcessor.java new file mode 100644 index 00000000000..a0eb56cbb8a --- /dev/null +++ b/plugins/affinity-group-processors/explicit-dedication/src/org/apache/cloudstack/affinity/ExplicitDedicationProcessor.java @@ -0,0 +1,383 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.affinity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.log4j.Logger; + +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.AffinityConflictException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = AffinityGroupProcessor.class) +public class ExplicitDedicationProcessor extends AffinityProcessorBase implements AffinityGroupProcessor { + + private static final Logger s_logger = Logger.getLogger(ExplicitDedicationProcessor.class); + @Inject + protected UserVmDao _vmDao; + @Inject + protected VMInstanceDao _vmInstanceDao; + @Inject + protected DataCenterDao _dcDao; + @Inject + protected DedicatedResourceDao _dedicatedDao; + @Inject + protected HostPodDao _podDao; + @Inject + protected ClusterDao _clusterDao; + @Inject + protected HostDao _hostDao; + @Inject + protected DomainDao _domainDao; + @Inject + protected AffinityGroupDao _affinityGroupDao; + @Inject + protected AffinityGroupVMMapDao _affinityGroupVMMapDao; + + /** + * This method will process the affinity group of type 'Explicit Dedication' for a deployment of a VM that demands dedicated resources. + * For ExplicitDedicationProcessor we need to add dedicated resources into the IncludeList based on the level we have dedicated resources available. + * For eg. if admin dedicates a pod to a domain, then all the user in that domain can use the resources of that pod. + * We need to take care of the situation when dedicated resources further have resources dedicated to sub-domain/account. + * This IncludeList is then used to update the avoid list for a given data center. + */ + @Override + public void process(VirtualMachineProfile vmProfile, DeploymentPlan plan, + ExcludeList avoid) throws AffinityConflictException { + VirtualMachine vm = vmProfile.getVirtualMachine(); + List vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType()); + DataCenter dc = _dcDao.findById(vm.getDataCenterId()); + long domainId = vm.getDomainId(); + long accountId = vm.getAccountId(); + + for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) { + if (vmGroupMapping != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Processing affinity group of type 'ExplicitDedication' for VM Id: " + vm.getId()); + } + + List dr = _dedicatedDao.listByAccountId(accountId); + List drOfDomain = searchInDomainResources(domainId); + List drOfParentDomain = searchInParentDomainResources(domainId); + List resourceList = new ArrayList(); + resourceList.addAll(dr); + resourceList.addAll(drOfDomain); + resourceList.addAll(drOfParentDomain); + boolean canUse = false; + + if (plan.getHostId() != null) { + HostVO host = _hostDao.findById(plan.getHostId()); + ClusterVO clusterofHost = _clusterDao.findById(host.getClusterId()); + HostPodVO podOfHost = _podDao.findById(host.getPodId()); + DataCenterVO zoneOfHost = _dcDao.findById(host.getDataCenterId()); + if (resourceList != null && resourceList.size() != 0) { + for(DedicatedResourceVO resource : resourceList){ + if ((resource.getHostId() != null && resource.getHostId() == plan.getHostId()) || + (resource.getClusterId() != null && resource.getClusterId() == clusterofHost.getId()) || + (resource.getPodId() != null && resource.getPodId() == podOfHost.getId()) || + (resource.getDataCenterId() != null && resource.getDataCenterId() == zoneOfHost.getId())){ + canUse = true; + } + } + } + if (!canUse) { + throw new CloudRuntimeException("Cannot use this host " + host.getName() + " for explicit dedication"); + } + } else if (plan.getClusterId() != null) { + ClusterVO cluster = _clusterDao.findById(plan.getClusterId()); + HostPodVO podOfCluster = _podDao.findById(cluster.getPodId()); + DataCenterVO zoneOfCluster = _dcDao.findById(cluster.getDataCenterId()); + List hostToUse = new ArrayList(); + // check whether this cluster or its pod is dedicated + if (resourceList != null && resourceList.size() != 0) { + for(DedicatedResourceVO resource : resourceList){ + if ((resource.getClusterId() != null && resource.getClusterId() == cluster.getId()) || + (resource.getPodId() != null && resource.getPodId() == podOfCluster.getId()) || + (resource.getDataCenterId() != null && resource.getDataCenterId() == zoneOfCluster.getId())){ + canUse = true; + } + + // check for all dedicated host; if it belongs to this cluster + if (!canUse){ + if (resource.getHostId() != null) { + HostVO dHost = _hostDao.findById(resource.getHostId()); + if (dHost.getClusterId() == cluster.getId()) { + hostToUse.add(dHost); + } + } + } + + } + } + + if (hostToUse.isEmpty() && !canUse) { + throw new CloudRuntimeException("Cannot use this cluster " + cluster.getName() + " for explicit dedication"); + } + + if (hostToUse != null && hostToUse.size() != 0) { + // add other non-dedicated hosts to avoid list + List hostList = _hostDao.findByClusterId(cluster.getId()); + for (HostVO host : hostList){ + if (!hostToUse.contains(host)) { + avoid.addHost(host.getId()); + } + } + } + + } else if (plan.getPodId() != null) { + HostPodVO pod = _podDao.findById(plan.getPodId()); + DataCenterVO zoneOfPod = _dcDao.findById(pod.getDataCenterId()); + List clustersToUse = new ArrayList(); + List hostsToUse = new ArrayList(); + // check whether this cluster or its pod is dedicated + if (resourceList != null && resourceList.size() != 0) { + for(DedicatedResourceVO resource : resourceList){ + if ((resource.getPodId() != null && resource.getPodId() == pod.getId()) || + (resource.getDataCenterId() != null && resource.getDataCenterId() == zoneOfPod.getId())){ + canUse = true; + } + + // check for all dedicated cluster/host; if it belongs to this pod + if (!canUse){ + if (resource.getClusterId() != null) { + ClusterVO dCluster = _clusterDao.findById(resource.getClusterId()); + if (dCluster.getPodId() == pod.getId()) { + clustersToUse.add(dCluster); + } + } + if (resource.getHostId() != null) { + HostVO dHost = _hostDao.findById(resource.getHostId()); + if (dHost.getPodId() == pod.getId()) { + hostsToUse.add(dHost); + } + } + } + + } + } + + if (hostsToUse.isEmpty() && clustersToUse.isEmpty() && !canUse) { + throw new CloudRuntimeException("Cannot use this pod " + pod.getName() + " for explicit dedication"); + } + + if (clustersToUse != null && clustersToUse.size() != 0) { + // add other non-dedicated clusters to avoid list + List clusterList = _clusterDao.listByPodId(pod.getId()); + for (ClusterVO cluster : clusterList){ + if (!clustersToUse.contains(cluster)) { + avoid.addCluster(cluster.getId()); + } + } + } + + if (hostsToUse != null && hostsToUse.size() != 0) { + // add other non-dedicated hosts to avoid list + List hostList = _hostDao.findByPodId(pod.getId()); + for (HostVO host : hostList){ + if (!hostsToUse.contains(host)) { + avoid.addHost(host.getId()); + } + } + } + + } else { + //check all resources under this zone + if (dr != null && dr.size() != 0) { + avoid = updateAvoidList(dr, avoid, dc); + } else if(drOfDomain != null && drOfDomain.size() != 0){ + avoid = updateAvoidList(drOfDomain, avoid, dc); + } else if(drOfParentDomain != null && drOfParentDomain.size() != 0){ + avoid = updateAvoidList(drOfParentDomain, avoid, dc); + } else { + avoid.addDataCenter(dc.getId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("No dedicated resources available for this domain or account"); + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("ExplicitDedicationProcessor returns Avoid List as: Deploy avoids pods: " + avoid.getPodsToAvoid() + ", clusters: " + + avoid.getClustersToAvoid() + ", hosts: " + avoid.getHostsToAvoid()); + } + } + } + } + } + + private ExcludeList updateAvoidList(List dedicatedResources, ExcludeList avoidList, DataCenter dc) { + ExcludeList includeList = new ExcludeList(); + for (DedicatedResourceVO dr : dedicatedResources) { + if (dr.getHostId() != null){ + includeList.addHost(dr.getHostId()); + HostVO dedicatedHost = _hostDao.findById(dr.getHostId()); + includeList.addCluster(dedicatedHost.getClusterId()); + includeList.addPod(dedicatedHost.getPodId()); + } + + if (dr.getClusterId() != null) { + includeList.addCluster(dr.getClusterId()); + //add all hosts inside this in includeList + List hostList = _hostDao.findByClusterId(dr.getClusterId()); + for (HostVO host : hostList) { + DedicatedResourceVO dHost = _dedicatedDao.findByHostId(host.getId()); + if (dHost != null) { + avoidList.addHost(host.getId()); + } else { + includeList.addHost(host.getId()); + } + } + ClusterVO dedicatedCluster = _clusterDao.findById(dr.getClusterId()); + includeList.addPod(dedicatedCluster.getPodId()); + } + + if (dr.getPodId() != null) { + includeList.addPod(dr.getPodId()); + //add all cluster under this pod in includeList + List clusterList = _clusterDao.listByPodId(dr.getPodId()); + for (ClusterVO cluster : clusterList) { + if (_dedicatedDao.findByClusterId(cluster.getId()) != null) { + avoidList.addCluster(cluster.getId()); + } else { + includeList.addCluster(cluster.getId()); + } + } + //add all hosts inside this pod in includeList + List hostList = _hostDao.findByPodId(dr.getPodId()); + for (HostVO host : hostList) { + if (_dedicatedDao.findByHostId(host.getId()) != null) { + avoidList.addHost(host.getId()); + } else { + includeList.addHost(host.getId()); + } + } + } + + if (dr.getDataCenterId() != null) { + includeList.addDataCenter(dr.getDataCenterId()); + //add all Pod under this data center in includeList + List podList = _podDao.listByDataCenterId(dr.getDataCenterId()); + for (HostPodVO pod : podList) { + if (_dedicatedDao.findByPodId(pod.getId()) != null) { + avoidList.addPod(pod.getId()); + } else { + includeList.addPod(pod.getId()); + } + } + List clusterList = _clusterDao.listClustersByDcId(dr.getDataCenterId()); + for (ClusterVO cluster : clusterList) { + if (_dedicatedDao.findByClusterId(cluster.getId()) != null) { + avoidList.addCluster(cluster.getId()); + } else { + includeList.addCluster(cluster.getId()); + } + } + //add all hosts inside this in includeList + List hostList = _hostDao.listByDataCenterId(dr.getDataCenterId()); + for (HostVO host : hostList) { + if (_dedicatedDao.findByHostId(host.getId()) != null) { + avoidList.addHost(host.getId()); + } else { + includeList.addHost(host.getId()); + } + } + } + } + //Update avoid list using includeList. + //add resources in avoid list which are not in include list. + + List pods = _podDao.listByDataCenterId(dc.getId()); + List clusters = _clusterDao.listClustersByDcId(dc.getId()); + List hosts = _hostDao.listByDataCenterId(dc.getId()); + Set podsInIncludeList = includeList.getPodsToAvoid(); + Set clustersInIncludeList = includeList.getClustersToAvoid(); + Set hostsInIncludeList = includeList.getHostsToAvoid(); + + for (HostPodVO pod : pods){ + if (podsInIncludeList != null && !podsInIncludeList.contains(pod.getId())) { + avoidList.addPod(pod.getId()); + } + } + + for (ClusterVO cluster : clusters) { + if (clustersInIncludeList != null && !clustersInIncludeList.contains(cluster.getId())) { + avoidList.addCluster(cluster.getId()); + } + } + + for (HostVO host : hosts) { + if (hostsInIncludeList != null && !hostsInIncludeList.contains(host.getId())) { + avoidList.addHost(host.getId()); + } + } + return avoidList; + } + + private List searchInParentDomainResources(long domainId) { + List domainIds = getDomainParentIds(domainId); + List dr = new ArrayList(); + for (Long id : domainIds) { + List resource = _dedicatedDao.listByDomainId(id); + if(resource != null) { + dr.addAll(resource); + } + } + return dr; + } + + private List searchInDomainResources(long domainId) { + List dr = _dedicatedDao.listByDomainId(domainId); + return dr; + } + + private List getDomainParentIds(long domainId) { + DomainVO domainRecord = _domainDao.findById(domainId); + List domainIds = new ArrayList(); + domainIds.add(domainRecord.getId()); + while (domainRecord.getParent() != null ){ + domainRecord = _domainDao.findById(domainRecord.getParent()); + domainIds.add(domainRecord.getId()); + } + return domainIds; + } + +} diff --git a/plugins/dedicated-resources/pom.xml b/plugins/dedicated-resources/pom.xml new file mode 100644 index 00000000000..4c908f4ff96 --- /dev/null +++ b/plugins/dedicated-resources/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + cloud-plugin-dedicated-resources + Apache CloudStack Plugin - Dedicated Resources + + org.apache.cloudstack + cloudstack-plugins + 4.2.0-SNAPSHOT + ../pom.xml + + diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java new file mode 100644 index 00000000000..58e20dee025 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java @@ -0,0 +1,115 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResources; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "dedicateCluster", description= "Dedicate an existing cluster", responseObject = DedicateClusterResponse.class ) +public class DedicateClusterCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DedicateClusterCmd.class.getName()); + + private static final String s_name = "dedicateclusterresponse"; + @Inject DedicatedService dedicatedService; + + @Parameter(name=ApiConstants.CLUSTER_ID, type=CommandType.UUID, entityType=ClusterResponse.class, + required=true, description="the ID of the Cluster") + private Long clusterId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, required=true, description="the ID of the containing domain") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the name of the account which needs dedication. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getClusterId() { + return clusterId; + } + + public Long getDomainId() { + return domainId; + } + + public String getAccountName() { + return accountName; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE; + } + + @Override + public String getEventDescription() { + return "dedicating a cluster"; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + List result = dedicatedService.dedicateCluster(getClusterId(), getDomainId(), getAccountName()); + ListResponse response = new ListResponse(); + List clusterResponseList = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result) { + DedicateClusterResponse clusterResponse = dedicatedService.createDedicateClusterResponse(resource); + clusterResponseList.add(clusterResponse); + } + response.setResponses(clusterResponseList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate cluster"); + } + } + +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java new file mode 100644 index 00000000000..f0269b11048 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResources; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "dedicateHost", description = "Dedicates a host.", responseObject = DedicateHostResponse.class) +public class DedicateHostCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DedicateHostCmd.class.getName()); + private static final String s_name = "dedicatehostresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.HOST_ID, type=CommandType.UUID, entityType = HostResponse.class, + required=true, description="the ID of the host to update") + private Long hostId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, required=true, description="the ID of the containing domain") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the name of the account which needs dedication. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getHostId() { + return hostId; + } + + public Long getDomainId() { + return domainId; + } + + public String getAccountName() { + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + List result = dedicatedService.dedicateHost(getHostId(), getDomainId(), getAccountName()); + ListResponse response = new ListResponse(); + List hostResponseList = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result) { + DedicateHostResponse hostResponse = dedicatedService.createDedicateHostResponse(resource); + hostResponseList.add(hostResponse); + } + response.setResponses(hostResponseList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate host"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE; + } + + @Override + public String getEventDescription() { + return "dedicating a host"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java new file mode 100644 index 00000000000..be5eac26feb --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java @@ -0,0 +1,120 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResources; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "dedicatePod", description ="Dedicates a Pod.", responseObject = DedicatePodResponse.class) +public class DedicatePodCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DedicatePodCmd.class.getName()); + + private static final String s_name = "dedicatepodresponse"; + @Inject public DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class, + required=true, description="the ID of the Pod") + private Long podId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, required=true, description="the ID of the containing domain") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the name of the account which needs dedication. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getPodId() { + return podId; + } + + public Long getDomainId() { + return domainId; + } + + public String getAccountName() { + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + List result = dedicatedService.dedicatePod(getPodId(), getDomainId(), getAccountName()); + ListResponse response = new ListResponse(); + List podResponseList = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result) { + DedicatePodResponse podresponse = dedicatedService.createDedicatePodResponse(resource); + podResponseList.add(podresponse); + } + response.setResponses(podResponseList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate pod"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE; + } + + @Override + public String getEventDescription() { + return "dedicating a pod"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java new file mode 100644 index 00000000000..134fb972757 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java @@ -0,0 +1,120 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResources; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "dedicateZone", description ="Dedicates a zones.", responseObject = DedicateZoneResponse.class) +public class DedicateZoneCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DedicateZoneCmd.class.getName()); + + private static final String s_name = "dedicatezoneresponse"; + @Inject public DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType=ZoneResponse.class, + required=true, description="the ID of the zone") + private Long zoneId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, required=true, description="the ID of the containing domain") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "the name of the account which needs dedication. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getZoneId() { + return zoneId; + } + + public Long getDomainId() { + return domainId; + } + + public String getAccountName() { + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + List result = dedicatedService.dedicateZone(getZoneId(), getDomainId(), getAccountName()); + ListResponse response = new ListResponse(); + List zoneResponseList = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result) { + DedicateZoneResponse zoneresponse = dedicatedService.createDedicateZoneResponse(resource); + zoneResponseList.add(zoneresponse); + } + response.setResponses(zoneResponseList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate zone"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE; + } + + @Override + public String getEventDescription() { + return "dedicating a zone"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java new file mode 100644 index 00000000000..c60c5240d9a --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.utils.Pair; + +@APICommand(name = "listDedicatedClusters", description = "Lists dedicated clusters.", responseObject = DedicateClusterResponse.class) +public class ListDedicatedClustersCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListDedicatedClustersCmd.class.getName()); + + private static final String s_name = "listdedicatedclustersresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.CLUSTER_ID, type=CommandType.UUID, entityType=ClusterResponse.class, + description="the ID of the cluster") + private Long clusterId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, + description="the ID of the domain associated with the cluster") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, + description = "the name of the account associated with the cluster. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getClusterId() { + return clusterId; + } + + public Long getDomainId(){ + return domainId; + } + + public String getAccountName(){ + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + Pair, Integer> result = dedicatedService.listDedicatedClusters(this); + ListResponse response = new ListResponse(); + List Responses = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result.first()) { + DedicateClusterResponse clusterResponse = dedicatedService.createDedicateClusterResponse(resource); + Responses.add(clusterResponse); + } + response.setResponses(Responses, result.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to list dedicated clusters"); + } + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java new file mode 100644 index 00000000000..2c1ad002e0d --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.utils.Pair; + +@APICommand(name = "listDedicatedHosts", description = "Lists dedicated hosts.", responseObject = DedicateHostResponse.class) +public class ListDedicatedHostsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListDedicatedHostsCmd.class.getName()); + + private static final String s_name = "listdedicatedhostsresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.HOST_ID, type=CommandType.UUID, entityType=HostResponse.class, + description="the ID of the host") + private Long hostId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, + description="the ID of the domain associated with the host") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, + description = "the name of the account associated with the host. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getHostId() { + return hostId; + } + + public Long getDomainId(){ + return domainId; + } + + public String getAccountName(){ + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation///////////////////l + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + Pair, Integer> result = dedicatedService.listDedicatedHosts(this); + ListResponse response = new ListResponse(); + List Responses = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result.first()) { + DedicateHostResponse hostResponse = dedicatedService.createDedicateHostResponse(resource); + Responses.add(hostResponse); + } + response.setResponses(Responses, result.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to list dedicated hosts"); + } + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java new file mode 100644 index 00000000000..31b1ecfbb51 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.utils.Pair; + +@APICommand(name = "listDedicatedPods", description = "Lists dedicated pods.", responseObject = DedicatePodResponse.class) +public class ListDedicatedPodsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListDedicatedPodsCmd.class.getName()); + + private static final String s_name = "listdedicatedpodsresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class, + description="the ID of the pod") + private Long podId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, + description="the ID of the domain associated with the pod") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, + description = "the name of the account associated with the pod. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPodId() { + return podId; + } + + public Long getDomainId(){ + return domainId; + } + + public String getAccountName(){ + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + Pair, Integer> result = dedicatedService.listDedicatedPods(this); + ListResponse response = new ListResponse(); + List Responses = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result.first()) { + DedicatePodResponse podresponse = dedicatedService.createDedicatePodResponse(resource); + Responses.add(podresponse); + } + response.setResponses(Responses, result.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to list dedicated pods"); + } + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java new file mode 100644 index 00000000000..c88c42b82a6 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.utils.Pair; + +@APICommand(name = "listDedicatedZones", description = "List dedicated zones.", responseObject = DedicateZoneResponse.class) +public class ListDedicatedZonesCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListDedicatedZonesCmd.class.getName()); + + private static final String s_name = "listdedicatedzonesresponse"; + @Inject DedicatedService _dedicatedservice; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType=ZoneResponse.class, + description="the ID of the Zone") + private Long zoneId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, + description="the ID of the domain associated with the zone") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, + description = "the name of the account associated with the zone. Must be used with domainId.") + private String accountName; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public Long getDomainId(){ + return domainId; + } + + public String getAccountName(){ + return accountName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + Pair, Integer> result = _dedicatedservice.listDedicatedZones(this); + ListResponse response = new ListResponse(); + List Responses = new ArrayList(); + if (result != null) { + for (DedicatedResources resource : result.first()) { + DedicateZoneResponse zoneResponse = _dedicatedservice.createDedicateZoneResponse(resource); + Responses.add(zoneResponse); + } + response.setResponses(Responses, result.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to list dedicated zones"); + } + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java new file mode 100644 index 00000000000..6a317885f10 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "releaseDedicatedCluster", description = "Release the dedication for cluster", responseObject = SuccessResponse.class) +public class ReleaseDedicatedClusterCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ReleaseDedicatedClusterCmd.class.getName()); + + private static final String s_name = "releasededicatedclusterresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.CLUSTER_ID, type=CommandType.UUID, entityType=ClusterResponse.class, + required=true, description="the ID of the Cluster") + private Long clusterId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getClusterId() { + return clusterId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = dedicatedService.releaseDedicatedResource(null, null, getClusterId(), null); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release dedicated cluster"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE_RELEASE; + } + + @Override + public String getEventDescription() { + return "releasing dedicated cluster"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java new file mode 100644 index 00000000000..29cfdeb9ab5 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "releaseDedicatedHost", description = "Release the dedication for host", responseObject = SuccessResponse.class) +public class ReleaseDedicatedHostCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ReleaseDedicatedHostCmd.class.getName()); + + private static final String s_name = "releasededicatedhostresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.HOST_ID, type=CommandType.UUID, entityType=HostResponse.class, + required=true, description="the ID of the host") + private Long hostId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getHostId() { + return hostId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = dedicatedService.releaseDedicatedResource(null, null, null, getHostId()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release dedicated Host"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE_RELEASE; + } + + @Override + public String getEventDescription() { + return "releasing dedicated host"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java new file mode 100644 index 00000000000..51d3fe20477 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "releaseDedicatedPod", description = "Release the dedication for the pod", responseObject = SuccessResponse.class) +public class ReleaseDedicatedPodCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ReleaseDedicatedPodCmd.class.getName()); + + private static final String s_name = "releasededicatedpodresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class, + required=true, description="the ID of the Pod") + private Long podId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPodId() { + return podId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = dedicatedService.releaseDedicatedResource(null, getPodId(), null, null); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release dedicated pod"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE_RELEASE; + } + + @Override + public String getEventDescription() { + return "releasing dedicated pod"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java new file mode 100644 index 00000000000..23f955711bf --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "releaseDedicatedZone", description = "Release dedication of zone", responseObject = SuccessResponse.class) +public class ReleaseDedicatedZoneCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ReleaseDedicatedZoneCmd.class.getName()); + + private static final String s_name = "releasededicatedzoneresponse"; + @Inject DedicatedService dedicatedService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType= ZoneResponse.class, + required=true, description="the ID of the Zone") + private Long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = dedicatedService.releaseDedicatedResource(getZoneId(), null, null, null); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release dedicated zone"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DEDICATE_RESOURCE_RELEASE; + } + + @Override + public String getEventDescription() { + return "releasing dedicated zone"; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java new file mode 100644 index 00000000000..faa362777f2 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.response; + +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DedicateClusterResponse extends BaseResponse { + @SerializedName("id") @Param(description="the ID of the dedicated resource") + private String id; + + @SerializedName("clusterid") @Param(description="the ID of the cluster") + private String clusterId; + + @SerializedName("clustername") @Param(description="the name of the cluster") + private String clusterName; + + @SerializedName("domainid") @Param(description="the domain ID of the cluster") + private String domainId; + + @SerializedName("accountid") @Param(description="the Account ID of the cluster") + private String accountId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getClusterId() { + return clusterId; + } + + public void setClusterId(String clusterId) { + this.clusterId = clusterId; + } + + public String getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java new file mode 100644 index 00000000000..e48ee35610d --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.response; + +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class DedicateHostResponse extends BaseResponse { + @SerializedName("id") @Param(description="the ID of the dedicated resource") + private String id; + + @SerializedName("hostid") @Param(description="the ID of the host") + private String hostId; + + @SerializedName("hostname") @Param(description="the name of the host") + private String hostName; + + @SerializedName("domainid") @Param(description="the domain ID of the host") + private String domainId; + + @SerializedName("accountid") @Param(description="the Account ID of the host") + private String accountId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java new file mode 100644 index 00000000000..fabaca166ea --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java @@ -0,0 +1,82 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.response; + +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.dc.DedicatedResources; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = DedicatedResources.class) +public class DedicatePodResponse extends BaseResponse { + @SerializedName("id") @Param(description="the ID of the dedicated resource") + private String id; + + @SerializedName("podid") @Param(description="the ID of the Pod") + private String podId; + + @SerializedName("podname") @Param(description="the Name of the Pod") + private String podName; + + @SerializedName("domainid") @Param(description="the domain ID to which the Pod is dedicated") + private String domainId; + + @SerializedName("accountid") @Param(description="the Account Id to which the Pod is dedicated") + private String accountId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPodId() { + return podId; + } + + public void setPodId(String podId) { + this.podId = podId; + } + + public String getPodName() { + return podName; + } + + public void setPodName(String podName) { + this.podName = podName; + } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java new file mode 100644 index 00000000000..06f4877d211 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.api.response; + +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.dc.DedicatedResources; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = DedicatedResources.class) +public class DedicateZoneResponse extends BaseResponse { + @SerializedName("id") @Param(description="the ID of the dedicated resource") + private String id; + + @SerializedName("zoneid") @Param(description="the ID of the Zone") + private String zoneId; + + @SerializedName("zonename") @Param(description="the Name of the Zone") + private String zoneName; + + @SerializedName("domainid") @Param(description="the domain ID to which the Zone is dedicated") + private String domainId; + + @SerializedName("accountid") @Param(description="the Account Id to which the Zone is dedicated") + private String accountId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public String getDomainId() { + return domainId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java new file mode 100755 index 00000000000..51087e2f56a --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java @@ -0,0 +1,816 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.manager; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.dedicated.api.commands.DedicateClusterCmd; +import org.apache.cloudstack.dedicated.api.commands.DedicateHostCmd; +import org.apache.cloudstack.dedicated.api.commands.DedicatePodCmd; +import org.apache.cloudstack.dedicated.api.commands.DedicateZoneCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedClustersCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedHostsCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedPodsCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedZonesCmd; +import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedClusterCmd; +import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedHostCmd; +import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedPodCmd; +import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedZoneCmd; +import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; +import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; +import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.Pod; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.DateUtil; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDao; + +@Component +@Local({DedicatedService.class }) +public class DedicatedResourceManagerImpl implements DedicatedService { + private static final Logger s_logger = Logger.getLogger(DedicatedResourceManagerImpl.class); + + @Inject AccountDao _accountDao; + @Inject DomainDao _domainDao; + @Inject HostPodDao _podDao; + @Inject ClusterDao _clusterDao; + @Inject HostDao _hostDao; + @Inject DedicatedResourceDao _dedicatedDao; + @Inject DataCenterDao _zoneDao; + @Inject AccountManager _accountMgr; + @Inject UserVmDao _userVmDao; + @Inject ConfigurationDao _configDao; + + private int capacityReleaseInterval; + + public boolean configure(final String name, final Map params) throws ConfigurationException { + capacityReleaseInterval = NumbersUtil.parseInt(_configDao.getValue(Config.CapacitySkipcountingHours.key()), 3600); + return true; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_DEDICATE_RESOURCE, eventDescription = "dedicating a Zone") + public List dedicateZone(Long zoneId, Long domainId, String accountName) { + Long accountId = null; + List hosts = null; + if(accountName != null){ + Account caller = UserContext.current().getCaller(); + Account owner = _accountMgr.finalizeOwner(caller, accountName, domainId, null); + accountId = owner.getId(); + } + List childDomainIds = getDomainChildIds(domainId); + childDomainIds.add(domainId); + checkAccountAndDomain(accountId, domainId); + DataCenterVO dc = _zoneDao.findById(zoneId); + if (dc == null) { + throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); + } else { + DedicatedResourceVO dedicatedZone = _dedicatedDao.findByZoneId(zoneId); + //check if zone is dedicated + if(dedicatedZone != null) { + s_logger.error("Zone " + dc.getName() + " is already dedicated"); + throw new CloudRuntimeException("Zone " + dc.getName() + " is already dedicated"); + } + + //check if any resource under this zone is dedicated to different account or sub-domain + List pods = _podDao.listByDataCenterId(dc.getId()); + List podsToRelease = new ArrayList(); + List clustersToRelease = new ArrayList(); + List hostsToRelease = new ArrayList(); + for (HostPodVO pod : pods) { + DedicatedResourceVO dPod = _dedicatedDao.findByPodId(pod.getId()); + if (dPod != null) { + if(!(childDomainIds.contains(dPod.getDomainId()))) { + throw new CloudRuntimeException("Pod " + pod.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + } + if (accountId != null) { + if (dPod.getAccountId() == accountId) { + podsToRelease.add(dPod); + } else { + s_logger.error("Pod " + pod.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + throw new CloudRuntimeException("Pod " + pod.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + } + } else { + if (dPod.getAccountId() == null && dPod.getDomainId() == domainId) { + podsToRelease.add(dPod); + } + } + } + } + + for (DedicatedResourceVO dr : podsToRelease) { + releaseDedicatedResource(null, dr.getPodId(), null, null); + } + + List clusters = _clusterDao.listClustersByDcId(dc.getId()); + for (ClusterVO cluster : clusters) { + DedicatedResourceVO dCluster = _dedicatedDao.findByClusterId(cluster.getId()); + if (dCluster != null) { + if(!(childDomainIds.contains(dCluster.getDomainId()))) { + throw new CloudRuntimeException("Cluster " + cluster.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + } + if (accountId != null) { + if (dCluster.getAccountId() == accountId) { + clustersToRelease.add(dCluster); + } else { + s_logger.error("Cluster " + cluster.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + throw new CloudRuntimeException("Cluster " + cluster.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + } + } else { + if (dCluster.getAccountId() == null && dCluster.getDomainId() == domainId) { + clustersToRelease.add(dCluster); + } + } + } + } + + for (DedicatedResourceVO dr : clustersToRelease) { + releaseDedicatedResource(null, null, dr.getClusterId(), null); + } + + hosts = _hostDao.listByDataCenterId(dc.getId()); + for (HostVO host : hosts) { + DedicatedResourceVO dHost = _dedicatedDao.findByHostId(host.getId()); + if (dHost != null) { + if(!(childDomainIds.contains(dHost.getDomainId()))) { + throw new CloudRuntimeException("Host " + host.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + } + if (accountId != null) { + if (dHost.getAccountId() == accountId) { + hostsToRelease.add(dHost); + } else { + s_logger.error("Host " + host.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + throw new CloudRuntimeException("Host " + host.getName() + " under this Zone " + dc.getName() + " is dedicated to different account/domain"); + } + } else { + if (dHost.getAccountId() == null && dHost.getDomainId() == domainId) { + hostsToRelease.add(dHost); + } + } + } + } + + for (DedicatedResourceVO dr : hostsToRelease) { + releaseDedicatedResource(null, null, null, dr.getHostId()); + } + } + + checkHostsSuitabilityForExplicitDedication(accountId, childDomainIds, hosts); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(zoneId, null, null, null, null, null); + try { + dedicatedResource.setDomainId(domainId); + if (accountId != null) { + dedicatedResource.setAccountId(accountId); + } + dedicatedResource = _dedicatedDao.persist(dedicatedResource); + } catch (Exception e) { + s_logger.error("Unable to dedicate zone due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support."); + } + txn.commit(); + + List result = new ArrayList(); + result.add(dedicatedResource); + return result; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_DEDICATE_RESOURCE, eventDescription = "dedicating a Pod") + public List dedicatePod(Long podId, Long domainId, String accountName) { + Long accountId = null; + if(accountName != null){ + Account caller = UserContext.current().getCaller(); + Account owner = _accountMgr.finalizeOwner(caller, accountName, domainId, null); + accountId = owner.getId(); + } + List childDomainIds = getDomainChildIds(domainId); + childDomainIds.add(domainId); + checkAccountAndDomain(accountId, domainId); + HostPodVO pod = _podDao.findById(podId); + List hosts = null; + if (pod == null) { + throw new InvalidParameterValueException("Unable to find pod by id " + podId); + } else { + DedicatedResourceVO dedicatedPod = _dedicatedDao.findByPodId(podId); + DedicatedResourceVO dedicatedZoneOfPod = _dedicatedDao.findByZoneId(pod.getDataCenterId()); + //check if pod is dedicated + if(dedicatedPod != null ) { + s_logger.error("Pod " + pod.getName() + " is already dedicated"); + throw new CloudRuntimeException("Pod " + pod.getName() + " is already dedicated"); + } + + if (dedicatedZoneOfPod != null) { + boolean domainIdInChildreanList = getDomainChildIds(dedicatedZoneOfPod.getDomainId()).contains(domainId); + //can dedicate a pod to an account/domain if zone is dedicated to parent-domain + if (dedicatedZoneOfPod.getAccountId() != null || (accountId == null && !domainIdInChildreanList) + || (accountId != null && !(dedicatedZoneOfPod.getDomainId() == domainId || domainIdInChildreanList))) { + DataCenterVO zone = _zoneDao.findById(pod.getDataCenterId()); + s_logger.error("Cannot dedicate Pod. Its zone is already dedicated"); + throw new CloudRuntimeException("Pod's Zone " + zone.getName() + " is already dedicated"); + } + } + + //check if any resource under this pod is dedicated to different account or sub-domain + List clusters = _clusterDao.listByPodId(pod.getId()); + List clustersToRelease = new ArrayList(); + List hostsToRelease = new ArrayList(); + for (ClusterVO cluster : clusters) { + DedicatedResourceVO dCluster = _dedicatedDao.findByClusterId(cluster.getId()); + if (dCluster != null) { + if(!(childDomainIds.contains(dCluster.getDomainId()))) { + throw new CloudRuntimeException("Cluster " + cluster.getName() + " under this Pod " + pod.getName() + " is dedicated to different account/domain"); + } + /*if all dedicated resources belongs to same account and domain then we should release dedication + and make new entry for this Pod*/ + if (accountId != null) { + if (dCluster.getAccountId() == accountId) { + clustersToRelease.add(dCluster); + } else { + s_logger.error("Cluster " + cluster.getName() + " under this Pod " + pod.getName() + " is dedicated to different account/domain"); + throw new CloudRuntimeException("Cluster " + cluster.getName() + " under this Pod " + pod.getName() + " is dedicated to different account/domain"); + } + } else { + if (dCluster.getAccountId() == null && dCluster.getDomainId() == domainId) { + clustersToRelease.add(dCluster); + } + } + } + } + + for (DedicatedResourceVO dr : clustersToRelease) { + releaseDedicatedResource(null, null, dr.getClusterId(), null); + } + + hosts = _hostDao.findByPodId(pod.getId()); + for (HostVO host : hosts) { + DedicatedResourceVO dHost = _dedicatedDao.findByHostId(host.getId()); + if (dHost != null) { + if(!(getDomainChildIds(domainId).contains(dHost.getDomainId()))) { + throw new CloudRuntimeException("Host " + host.getName() + " under this Pod " + pod.getName() + " is dedicated to different account/domain"); + } + if (accountId != null) { + if (dHost.getAccountId() == accountId) { + hostsToRelease.add(dHost); + } else { + s_logger.error("Host " + host.getName() + " under this Pod " + pod.getName() + " is dedicated to different account/domain"); + throw new CloudRuntimeException("Host " + host.getName() + " under this Pod " + pod.getName() + " is dedicated to different account/domain"); + } + } else { + if (dHost.getAccountId() == null && dHost.getDomainId() == domainId) { + hostsToRelease.add(dHost); + } + } + } + } + + for (DedicatedResourceVO dr : hostsToRelease) { + releaseDedicatedResource(null, null, null, dr.getHostId()); + } + } + + checkHostsSuitabilityForExplicitDedication(accountId, childDomainIds, hosts); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, podId, null, null, null, null); + try { + dedicatedResource.setDomainId(domainId); + if (accountId != null) { + dedicatedResource.setAccountId(accountId); + } + dedicatedResource = _dedicatedDao.persist(dedicatedResource); + } catch (Exception e) { + s_logger.error("Unable to dedicate pod due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to dedicate pod. Please contact Cloud Support."); + } + txn.commit(); + + List result = new ArrayList(); + result.add(dedicatedResource); + return result; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_DEDICATE_RESOURCE, eventDescription = "dedicating a Cluster") + public List dedicateCluster(Long clusterId, Long domainId, String accountName) { + Long accountId = null; + List hosts = null; + if(accountName != null){ + Account caller = UserContext.current().getCaller(); + Account owner = _accountMgr.finalizeOwner(caller, accountName, domainId, null); + accountId = owner.getId(); + } + List childDomainIds = getDomainChildIds(domainId); + childDomainIds.add(domainId); + checkAccountAndDomain(accountId, domainId); + ClusterVO cluster = _clusterDao.findById(clusterId); + if (cluster == null) { + throw new InvalidParameterValueException("Unable to find cluster by id " + clusterId); + } else { + DedicatedResourceVO dedicatedCluster = _dedicatedDao.findByClusterId(clusterId); + DedicatedResourceVO dedicatedPodOfCluster = _dedicatedDao.findByPodId(cluster.getPodId()); + DedicatedResourceVO dedicatedZoneOfCluster = _dedicatedDao.findByZoneId(cluster.getDataCenterId()); + + //check if cluster is dedicated + if(dedicatedCluster != null) { + s_logger.error("Cluster " + cluster.getName() + " is already dedicated"); + throw new CloudRuntimeException("Cluster "+ cluster.getName() + " is already dedicated"); + } + + if (dedicatedPodOfCluster != null) { + boolean domainIdInChildreanList = getDomainChildIds(dedicatedPodOfCluster.getDomainId()).contains(domainId); + //can dedicate a cluster to an account/domain if pod is dedicated to parent-domain + if (dedicatedPodOfCluster.getAccountId() != null || (accountId == null && !domainIdInChildreanList) + || (accountId != null && !(dedicatedPodOfCluster.getDomainId() == domainId || domainIdInChildreanList))) { + s_logger.error("Cannot dedicate Cluster. Its Pod is already dedicated"); + HostPodVO pod = _podDao.findById(cluster.getPodId()); + throw new CloudRuntimeException("Cluster's Pod " + pod.getName() + " is already dedicated"); + } + } + + if (dedicatedZoneOfCluster != null) { + boolean domainIdInChildreanList = getDomainChildIds(dedicatedZoneOfCluster.getDomainId()).contains(domainId); + //can dedicate a cluster to an account/domain if zone is dedicated to parent-domain + if (dedicatedZoneOfCluster.getAccountId() != null || (accountId == null && !domainIdInChildreanList) + || (accountId != null && !(dedicatedZoneOfCluster.getDomainId() == domainId || domainIdInChildreanList))) { + s_logger.error("Cannot dedicate Cluster. Its zone is already dedicated"); + DataCenterVO zone = _zoneDao.findById(cluster.getDataCenterId()); + throw new CloudRuntimeException("Cluster's Zone "+ zone.getName() + " is already dedicated"); + } + } + + //check if any resource under this cluster is dedicated to different account or sub-domain + hosts = _hostDao.findByClusterId(cluster.getId()); + List hostsToRelease = new ArrayList(); + for (HostVO host : hosts) { + DedicatedResourceVO dHost = _dedicatedDao.findByHostId(host.getId()); + if (dHost != null) { + if(!(childDomainIds.contains(dHost.getDomainId()))) { + throw new CloudRuntimeException("Host " + host.getName() + " under this Cluster " + cluster.getName() + " is dedicated to different account/domain"); + } + /*if all dedicated resources belongs to same account and domain then we should release dedication + and make new entry for this cluster */ + if (accountId != null) { + if (dHost.getAccountId() == accountId) { + hostsToRelease.add(dHost); + } else { + s_logger.error("Cannot dedicate Cluster " + cluster.getName() + " to account" + accountName); + throw new CloudRuntimeException("Cannot dedicate Cluster " + cluster.getName() + " to account" + accountName); + } + } else { + if (dHost.getAccountId() == null && dHost.getDomainId() == domainId) { + hostsToRelease.add(dHost); + } + } + } + } + + for (DedicatedResourceVO dr : hostsToRelease) { + releaseDedicatedResource(null, null, null, dr.getHostId()); + } + } + + checkHostsSuitabilityForExplicitDedication(accountId, childDomainIds, hosts); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, clusterId, null, null, null); + try { + dedicatedResource.setDomainId(domainId); + if (accountId != null) { + dedicatedResource.setAccountId(accountId); + } + dedicatedResource = _dedicatedDao.persist(dedicatedResource); + } catch (Exception e) { + s_logger.error("Unable to dedicate host due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to dedicate cluster. Please contact Cloud Support."); + } + txn.commit(); + + List result = new ArrayList(); + result.add(dedicatedResource); + return result; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_DEDICATE_RESOURCE, eventDescription = "dedicating a Host") + public List dedicateHost(Long hostId, Long domainId, String accountName) { + Long accountId = null; + if(accountName != null){ + Account caller = UserContext.current().getCaller(); + Account owner = _accountMgr.finalizeOwner(caller, accountName, domainId, null); + accountId = owner.getId(); + } + checkAccountAndDomain(accountId, domainId); + HostVO host = _hostDao.findById(hostId); + if (host == null) { + throw new InvalidParameterValueException("Unable to find host by id " + hostId); + } else { + //check if host is of routing type + if (host.getType() != Host.Type.Routing) { + throw new CloudRuntimeException("Invalid host type for host " + host.getName()); + } + + DedicatedResourceVO dedicatedHost = _dedicatedDao.findByHostId(hostId); + DedicatedResourceVO dedicatedClusterOfHost = _dedicatedDao.findByClusterId(host.getClusterId()); + DedicatedResourceVO dedicatedPodOfHost = _dedicatedDao.findByPodId(host.getPodId()); + DedicatedResourceVO dedicatedZoneOfHost = _dedicatedDao.findByZoneId(host.getDataCenterId()); + + if(dedicatedHost != null) { + s_logger.error("Host "+ host.getName() + " is already dedicated"); + throw new CloudRuntimeException("Host "+ host.getName() + " is already dedicated"); + } + + if (dedicatedClusterOfHost != null) { + boolean domainIdInChildreanList = getDomainChildIds(dedicatedClusterOfHost.getDomainId()).contains(domainId); + //can dedicate a host to an account/domain if cluster is dedicated to parent-domain + if (dedicatedClusterOfHost.getAccountId() != null || (accountId == null && !domainIdInChildreanList) + || (accountId != null && !(dedicatedClusterOfHost.getDomainId() == domainId || domainIdInChildreanList))) { + ClusterVO cluster = _clusterDao.findById(host.getClusterId()); + s_logger.error("Host's Cluster " + cluster.getName() + " is already dedicated"); + throw new CloudRuntimeException("Host's Cluster " + cluster.getName() + " is already dedicated"); + } + } + + if (dedicatedPodOfHost != null){ + boolean domainIdInChildreanList = getDomainChildIds(dedicatedPodOfHost.getDomainId()).contains(domainId); + //can dedicate a host to an account/domain if pod is dedicated to parent-domain + if (dedicatedPodOfHost.getAccountId() != null || (accountId == null && !domainIdInChildreanList) + || (accountId != null && !(dedicatedPodOfHost.getDomainId() == domainId || domainIdInChildreanList))) { + HostPodVO pod = _podDao.findById(host.getPodId()); + s_logger.error("Host's Pod " + pod.getName() + " is already dedicated"); + throw new CloudRuntimeException("Host's Pod " + pod.getName() + " is already dedicated"); + } + } + + if (dedicatedZoneOfHost != null) { + boolean domainIdInChildreanList = getDomainChildIds(dedicatedZoneOfHost.getDomainId()).contains(domainId); + //can dedicate a host to an account/domain if zone is dedicated to parent-domain + if (dedicatedZoneOfHost.getAccountId() != null || (accountId == null && !domainIdInChildreanList) + || (accountId != null && !(dedicatedZoneOfHost.getDomainId() == domainId || domainIdInChildreanList))) { + DataCenterVO zone = _zoneDao.findById(host.getDataCenterId()); + s_logger.error("Host's Data Center " + zone.getName() + " is already dedicated"); + throw new CloudRuntimeException("Host's Data Center " + zone.getName() + " is already dedicated"); + } + } + } + + List childDomainIds = getDomainChildIds(domainId); + childDomainIds.add(domainId); + checkHostSuitabilityForExplicitDedication(accountId, childDomainIds, hostId); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, null, hostId, null, null); + try { + dedicatedResource.setDomainId(domainId); + if (accountId != null) { + dedicatedResource.setAccountId(accountId); + } + dedicatedResource = _dedicatedDao.persist(dedicatedResource); + } catch (Exception e) { + s_logger.error("Unable to dedicate host due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to dedicate host. Please contact Cloud Support."); + } + txn.commit(); + + List result = new ArrayList(); + result.add(dedicatedResource); + return result; + } + + private List getVmsOnHost(long hostId) { + List vms = _userVmDao.listUpByHostId(hostId); + List vmsByLastHostId = _userVmDao.listByLastHostId(hostId); + if (vmsByLastHostId.size() > 0) { + // check if any VMs are within skip.counting.hours, if yes we have to consider the host. + for (UserVmVO stoppedVM : vmsByLastHostId) { + long secondsSinceLastUpdate = (DateUtil.currentGMTTime().getTime() - stoppedVM.getUpdateTime() + .getTime()) / 1000; + if (secondsSinceLastUpdate < capacityReleaseInterval) { + vms.add(stoppedVM); + } + } + } + + return vms; + } + + private boolean checkHostSuitabilityForExplicitDedication(Long accountId, List domainIds, long hostId) { + boolean suitable = true; + List allVmsOnHost = getVmsOnHost(hostId); + if (accountId != null) { + for (UserVmVO vm : allVmsOnHost) { + if (vm.getAccountId() != accountId) { + s_logger.info("Host " + vm.getHostId() + " found to be unsuitable for explicit dedication as it is " + + "running instances of another account"); + throw new CloudRuntimeException("Host " + hostId + " found to be unsuitable for explicit dedication as it is " + + "running instances of another account"); + } + } + } else { + for (UserVmVO vm : allVmsOnHost) { + if (!domainIds.contains(vm.getDomainId())) { + s_logger.info("Host " + vm.getHostId() + " found to be unsuitable for explicit dedication as it is " + + "running instances of another domain"); + throw new CloudRuntimeException("Host " + hostId + " found to be unsuitable for explicit dedication as it is " + + "running instances of another domain"); + } + } + } + return suitable; + } + + private boolean checkHostsSuitabilityForExplicitDedication(Long accountId, List domainIds, List hosts) { + boolean suitable = true; + for (HostVO host : hosts){ + checkHostSuitabilityForExplicitDedication(accountId, domainIds, host.getId()); + } + return suitable; + } + + private void checkAccountAndDomain(Long accountId, Long domainId) { + DomainVO domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Unable to find the domain by id " + domainId + ", please specify valid domainId"); + } + //check if account belongs to the domain id + if (accountId != null) { + AccountVO account = _accountDao.findById(accountId); + if (account == null || domainId != account.getDomainId()){ + throw new InvalidParameterValueException("Please specify the domain id of the account: " + account.getAccountName()); + } + } + } + + private List getDomainChildIds(long domainId) { + DomainVO domainRecord = _domainDao.findById(domainId); + List domainIds = new ArrayList(); + domainIds.add(domainRecord.getId()); + // find all domain Ids till leaf + List allChildDomains = _domainDao.findAllChildren(domainRecord.getPath(), domainRecord.getId()); + for (DomainVO domain : allChildDomains) { + domainIds.add(domain.getId()); + } + return domainIds; + } + + @Override + public DedicateZoneResponse createDedicateZoneResponse(DedicatedResources resource) { + DedicateZoneResponse dedicateZoneResponse = new DedicateZoneResponse(); + DataCenterVO dc = _zoneDao.findById(resource.getDataCenterId()); + DomainVO domain = _domainDao.findById(resource.getDomainId()); + AccountVO account = _accountDao.findById(resource.getAccountId()); + dedicateZoneResponse.setId(resource.getUuid()); + dedicateZoneResponse.setZoneId(dc.getUuid()); + dedicateZoneResponse.setZoneName(dc.getName()); + dedicateZoneResponse.setDomainId(domain.getUuid()); + if (account != null) { + dedicateZoneResponse.setAccountId(account.getUuid()); + } + dedicateZoneResponse.setObjectName("dedicatedzone"); + return dedicateZoneResponse; + } + + @Override + public DedicatePodResponse createDedicatePodResponse(DedicatedResources resource) { + DedicatePodResponse dedicatePodResponse = new DedicatePodResponse(); + HostPodVO pod = _podDao.findById(resource.getPodId()); + DomainVO domain = _domainDao.findById(resource.getDomainId()); + AccountVO account = _accountDao.findById(resource.getAccountId()); + dedicatePodResponse.setId(resource.getUuid()); + dedicatePodResponse.setPodId(pod.getUuid()); + dedicatePodResponse.setPodName(pod.getName()); + dedicatePodResponse.setDomainId(domain.getUuid()); + if (account != null) { + dedicatePodResponse.setAccountId(account.getUuid()); + } + dedicatePodResponse.setObjectName("dedicatedpod"); + return dedicatePodResponse; + } + + @Override + public DedicateClusterResponse createDedicateClusterResponse(DedicatedResources resource) { + DedicateClusterResponse dedicateClusterResponse = new DedicateClusterResponse(); + ClusterVO cluster = _clusterDao.findById(resource.getClusterId()); + DomainVO domain = _domainDao.findById(resource.getDomainId()); + AccountVO account = _accountDao.findById(resource.getAccountId()); + dedicateClusterResponse.setId(resource.getUuid()); + dedicateClusterResponse.setClusterId(cluster.getUuid()); + dedicateClusterResponse.setClusterName(cluster.getName()); + dedicateClusterResponse.setDomainId(domain.getUuid()); + if (account != null) { + dedicateClusterResponse.setAccountId(account.getUuid()); + } + dedicateClusterResponse.setObjectName("dedicatedcluster"); + return dedicateClusterResponse; + } + + @Override + public DedicateHostResponse createDedicateHostResponse(DedicatedResources resource) { + DedicateHostResponse dedicateHostResponse = new DedicateHostResponse(); + HostVO host = _hostDao.findById(resource.getHostId()); + DomainVO domain = _domainDao.findById(resource.getDomainId()); + AccountVO account = _accountDao.findById(resource.getAccountId()); + dedicateHostResponse.setId(resource.getUuid()); + dedicateHostResponse.setHostId(host.getUuid()); + dedicateHostResponse.setHostName(host.getName()); + dedicateHostResponse.setDomainId(domain.getUuid()); + if (account != null) { + dedicateHostResponse.setAccountId(account.getUuid()); + } + dedicateHostResponse.setObjectName("dedicatedhost"); + return dedicateHostResponse; + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(DedicateZoneCmd.class); + cmdList.add(DedicatePodCmd.class); + cmdList.add(DedicateClusterCmd.class); + cmdList.add(DedicateHostCmd.class); + cmdList.add(ListDedicatedZonesCmd.class); + cmdList.add(ListDedicatedPodsCmd.class); + cmdList.add(ListDedicatedClustersCmd.class); + cmdList.add(ListDedicatedHostsCmd.class); + cmdList.add(ReleaseDedicatedClusterCmd.class); + cmdList.add(ReleaseDedicatedHostCmd.class); + cmdList.add(ReleaseDedicatedPodCmd.class); + cmdList.add(ReleaseDedicatedZoneCmd.class); + return cmdList; + } + + @Override + public Pair, Integer> listDedicatedZones(ListDedicatedZonesCmd cmd) { + Long zoneId = cmd.getZoneId(); + Long domainId = cmd.getDomainId(); + String accountName = cmd.getAccountName(); + Long accountId = null; + if (accountName != null) { + if (domainId != null) { + Account account = _accountDao.findActiveAccount(accountName, domainId); + if (account != null) { + accountId = account.getId(); + } + } else { + throw new InvalidParameterValueException("Please specify the domain id of the account: " + accountName); + } + } + Pair, Integer> result = _dedicatedDao.searchDedicatedZones(zoneId, domainId, accountId); + return new Pair, Integer>(result.first(), result.second()); + } + + @Override + public Pair, Integer> listDedicatedPods(ListDedicatedPodsCmd cmd) { + Long podId = cmd.getPodId(); + Long domainId = cmd.getDomainId(); + String accountName = cmd.getAccountName(); + Long accountId = null; + if (accountName != null) { + if (domainId != null) { + Account account = _accountDao.findActiveAccount(accountName, domainId); + if (account != null) { + accountId = account.getId(); + } + } else { + throw new InvalidParameterValueException("Please specify the domain id of the account: " + accountName); + } + } + Pair, Integer> result = _dedicatedDao.searchDedicatedPods(podId, domainId, accountId); + return new Pair, Integer>(result.first(), result.second()); + } + + @Override + public Pair, Integer> listDedicatedClusters(ListDedicatedClustersCmd cmd) { + Long clusterId = cmd.getClusterId(); + Long domainId = cmd.getDomainId(); + String accountName = cmd.getAccountName(); + Long accountId = null; + if (accountName != null) { + if (domainId != null) { + Account account = _accountDao.findActiveAccount(accountName, domainId); + if (account != null) { + accountId = account.getId(); + } + } else { + throw new InvalidParameterValueException("Please specify the domain id of the account: " + accountName); + } + } + Pair, Integer> result = _dedicatedDao.searchDedicatedClusters(clusterId, domainId, accountId); + return new Pair, Integer>(result.first(), result.second()); + } + + @Override + public Pair, Integer> listDedicatedHosts(ListDedicatedHostsCmd cmd) { + Long hostId = cmd.getHostId(); + Long domainId = cmd.getDomainId(); + String accountName = cmd.getAccountName(); + Long accountId = null; + if (accountName != null) { + if (domainId != null) { + Account account = _accountDao.findActiveAccount(accountName, domainId); + if (account != null) { + accountId = account.getId(); + } + } else { + throw new InvalidParameterValueException("Please specify the domain id of the account: " + accountName); + } + } + + Pair, Integer> result = _dedicatedDao.searchDedicatedHosts(hostId, domainId, accountId); + return new Pair, Integer>(result.first(), result.second()); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_DEDICATE_RESOURCE_RELEASE, eventDescription = "Releasing dedicated resource") + public boolean releaseDedicatedResource(Long zoneId, Long podId, Long clusterId, Long hostId) throws InvalidParameterValueException{ + DedicatedResourceVO resource = null; + Long resourceId = null; + if (zoneId != null) { + resource = _dedicatedDao.findByZoneId(zoneId); + } + if (podId != null) { + resource = _dedicatedDao.findByPodId(podId); + } + if (clusterId != null) { + resource = _dedicatedDao.findByClusterId(clusterId); + } + if (hostId != null ) { + resource = _dedicatedDao.findByHostId(hostId); + } + if (resource == null){ + throw new InvalidParameterValueException("No Dedicated Resource available to release"); + } else { + Transaction txn = Transaction.currentTxn(); + txn.start(); + resourceId = resource.getId(); + if (!_dedicatedDao.remove(resourceId)) { + throw new CloudRuntimeException("Failed to delete Resource " + resourceId); + } + txn.commit(); + } + return true; + } +} diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java new file mode 100755 index 00000000000..360852ae8e6 --- /dev/null +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.services; + +import java.util.List; + +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedClustersCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedHostsCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedPodsCmd; +import org.apache.cloudstack.dedicated.api.commands.ListDedicatedZonesCmd; +import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; +import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; +import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.utils.Pair; +import com.cloud.utils.component.PluggableService; + +public interface DedicatedService extends PluggableService{ + + DedicatePodResponse createDedicatePodResponse(DedicatedResources resource); + + DedicateClusterResponse createDedicateClusterResponse( + DedicatedResources resource); + + DedicateHostResponse createDedicateHostResponse(DedicatedResources resource); + + Pair, Integer> listDedicatedPods(ListDedicatedPodsCmd cmd); + + Pair, Integer> listDedicatedHosts(ListDedicatedHostsCmd cmd); + + Pair, Integer> listDedicatedClusters(ListDedicatedClustersCmd cmd); + + boolean releaseDedicatedResource(Long zoneId, Long podId, Long clusterId, Long hostId); + + DedicateZoneResponse createDedicateZoneResponse(DedicatedResources resource); + + Pair, Integer> listDedicatedZones(ListDedicatedZonesCmd cmd); + + List dedicateZone(Long zoneId, Long domainId, String accountName); + + List dedicatePod(Long podId, Long domainId, String accountName); + + List dedicateCluster(Long clusterId, Long domainId, String accountName); + + List dedicateHost(Long hostId, Long domainId, String accountName); + +} diff --git a/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java b/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java new file mode 100644 index 00000000000..3cf51299b6e --- /dev/null +++ b/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java @@ -0,0 +1,324 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package org.apache.cloudstack.dedicated.manager; + +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import junit.framework.Assert; + +import org.apache.cloudstack.test.utils.SpringUtils; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.dao.UserVmDao; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +public class DedicatedApiUnitTest { + public static final Logger s_logger = Logger.getLogger(DedicatedApiUnitTest.class); + @Inject + DedicatedResourceManagerImpl _dedicatedService = new DedicatedResourceManagerImpl(); + + @Inject + AccountManager _acctMgr; + + @Inject + AccountDao _accountDao; + + @Inject + DomainDao _domainDao; + + @Inject + UserVmDao _vmDao; + + @Inject + DedicatedResourceDao _dedicatedDao; + + @Inject + DataCenterDao _dcDao; + + @Inject + HostPodDao _podDao; + + @Inject + ClusterDao _clusterDao; + + @Inject + HostDao _hostDao; + + @Inject + ConfigurationDao _configDao; + + private static long domainId = 5L; + private static long accountId = 5L; + private static String accountName = "admin"; + + @BeforeClass + public static void setUp() throws ConfigurationException { + + } + + @Before + public void testSetUp() { + ComponentContext.initComponentsLifeCycle(); + AccountVO account = new AccountVO(accountName, domainId, "networkDomain", Account.ACCOUNT_TYPE_NORMAL, "uuid"); + DomainVO domain = new DomainVO("rootDomain", 5L, 5L, "networkDomain"); + + UserContext.registerContext(1, account, null, true); + when(_acctMgr.finalizeOwner((Account) anyObject(), anyString(), anyLong(), anyLong())).thenReturn(account); + when(_accountDao.findByIdIncludingRemoved(0L)).thenReturn(account); + when(_accountDao.findById(anyLong())).thenReturn(account); + when(_domainDao.findById(domainId)).thenReturn(domain); + } + + @Test(expected = InvalidParameterValueException.class) + public void InvalidDomainIDForAccountTest() { + _dedicatedService.dedicateZone(10L, domainId, accountName); + } + + @Test(expected = InvalidParameterValueException.class) + public void dedicateResourceInvalidAccountIDTest() { + _dedicatedService.dedicateZone(10L, domainId, accountName); + } + + @Test + public void releaseDedicatedZoneInvalidIdTest() { + when(_dedicatedDao.findByZoneId(10L)).thenReturn(null); + try { + _dedicatedService.releaseDedicatedResource(10L, null, null, null); + } catch (InvalidParameterValueException e) { + Assert.assertTrue(e.getMessage().contains( + "No Dedicated Resource available to release")); + } + } + +/* @Test + public void runDedicateZoneTest() { + DataCenterVO dc = new DataCenterVO(10L, "TestZone", "Dedicated", + "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", null, null, + NetworkType.Basic, null, null); + when(_dcDao.findById(10L)).thenReturn(dc); + try { + List result = _dedicatedService.dedicateZone(10L, domainId, accountName); + Assert.assertNotNull(result); + } catch (Exception e) { + s_logger.info("exception in testing dedication of zone " + + e.toString()); + } + } + + @Test + public void runDedicatePodTest() { + HostPodVO pod = new HostPodVO("TestPod", 20L, "10.0.0.1", "10.0.0.0", + 22, null); + when(_podDao.findById(10L)).thenReturn(pod); + try { + List result = _dedicatedService.dedicatePod(10L, domainId, accountName); + Assert.assertNotNull(result); + } catch (Exception e) { + s_logger.info("exception in testing dedication of pod " + + e.toString()); + } + } + + @Test + public void runDedicateClusterTest() { + ClusterVO cluster = new ClusterVO(10L, 10L, "TestCluster"); + when(_clusterDao.findById(10L)).thenReturn(cluster); + try { + List result = _dedicatedService.dedicateCluster(10L, domainId, accountName); + Assert.assertNotNull(result); + } catch (Exception e) { + s_logger.info("exception in testing dedication of cluster " + + e.toString()); + } + } + + @Test + public void runDedicateHostTest() { + HostVO host = new HostVO(10L, "Host-1", Host.Type.Routing, null, + "10.0.0.0", null, null, null, null, null, null, null, null, + Status.Up, null, null, null, 10L, 10L, 30L, 10233, null, null, + null, 0, null); + when(_hostDao.findById(10L)).thenReturn(host); + try { + List result = _dedicatedService.dedicateHost(10L, domainId, accountName); + Assert.assertNotNull(result); + } catch (Exception e) { + s_logger.info("exception in testing dedication of host " + + e.toString()); + } + } +*/ + + @Test(expected = CloudRuntimeException.class) + public void dedicateZoneExistTest() { + DedicatedResourceVO dr = new DedicatedResourceVO(10L, null, null, null, domainId, accountId); + when(_dedicatedDao.findByZoneId(10L)).thenReturn(dr); + _dedicatedService.dedicateZone(10L, domainId, accountName); + } + + @Test(expected = CloudRuntimeException.class) + public void dedicatePodExistTest() { + DedicatedResourceVO dr = new DedicatedResourceVO(null, 10L, null, null, domainId, accountId); + when(_dedicatedDao.findByPodId(10L)).thenReturn(dr); + _dedicatedService.dedicatePod(10L, domainId, accountName); + } + + @Test(expected = CloudRuntimeException.class) + public void dedicateClusterExistTest() { + DedicatedResourceVO dr = new DedicatedResourceVO(null, null, 10L, null, domainId, accountId); + when(_dedicatedDao.findByClusterId(10L)).thenReturn(dr); + _dedicatedService.dedicateCluster(10L, domainId, accountName); + } + + @Test(expected = CloudRuntimeException.class) + public void dedicateHostExistTest() { + DedicatedResourceVO dr = new DedicatedResourceVO(null, null, null, 10L, domainId, accountId); + when(_dedicatedDao.findByHostId(10L)).thenReturn(dr); + _dedicatedService.dedicateHost(10L, domainId, accountName); + } + + @Test(expected = InvalidParameterValueException.class) + public void releaseDedicatedPodInvalidIdTest() { + when(_dedicatedDao.findByPodId(10L)).thenReturn(null); + _dedicatedService.releaseDedicatedResource(null, 10L, null, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void releaseDedicatedClusterInvalidIdTest() { + when(_dedicatedDao.findByClusterId(10L)).thenReturn(null); + _dedicatedService.releaseDedicatedResource(null, null, 10L, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void releaseDedicatedHostInvalidIdTest() { + when(_dedicatedDao.findByHostId(10L)).thenReturn(null); + _dedicatedService.releaseDedicatedResource(null, null, null, 10L); + } + + @Configuration + @ComponentScan(basePackageClasses = {DedicatedResourceManagerImpl.class}, + includeFilters = {@Filter(value = TestConfiguration.Library.class, + type = FilterType.CUSTOM)}, useDefaultFilters = false) + public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { + + @Bean + public AccountDao accountDao() { + return Mockito.mock(AccountDao.class); + } + + @Bean + public DomainDao domainDao() { + return Mockito.mock(DomainDao.class); + } + + @Bean + public DedicatedResourceDao dedicatedDao() { + return Mockito.mock(DedicatedResourceDao.class); + } + + @Bean + public HostDao hostDao() { + return Mockito.mock(HostDao.class); + } + + @Bean + public AccountManager acctManager() { + return Mockito.mock(AccountManager.class); + } + + @Bean + public UserVmDao userVmDao() { + return Mockito.mock(UserVmDao.class); + } + @Bean + public DataCenterDao dataCenterDao() { + return Mockito.mock(DataCenterDao.class); + } + @Bean + public HostPodDao hostPodDao() { + return Mockito.mock(HostPodDao.class); + } + + @Bean + public ClusterDao clusterDao() { + return Mockito.mock(ClusterDao.class); + } + + @Bean + public ConfigurationDao configDao() { + return Mockito.mock(ConfigurationDao.class); + } + + public static class Library implements TypeFilter { + @Override + public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { + ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); + return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); + } + } + } +} diff --git a/plugins/dedicated-resources/test/resource/dedicatedContext.xml b/plugins/dedicated-resources/test/resource/dedicatedContext.xml new file mode 100644 index 00000000000..9ce8362d4b0 --- /dev/null +++ b/plugins/dedicated-resources/test/resource/dedicatedContext.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/pom.xml b/plugins/pom.xml index 2efa2488e86..eab47554e6b 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -35,11 +35,13 @@ api/rate-limit api/discovery acl/static-role-based - affinity-group-processors/host-anti-affinity + affinity-group-processors/host-anti-affinity + affinity-group-processors/explicit-dedication deployment-planners/user-concentrated-pod deployment-planners/user-dispersing deployment-planners/implicit-dedication host-allocators/random + dedicated-resources hypervisors/ovm hypervisors/xen hypervisors/kvm diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 5a25732bca0..28aecfc223d 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -97,6 +97,8 @@ import com.cloud.api.query.vo.UserAccountJoinVO; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -255,6 +257,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { @Inject private AffinityGroupJoinDao _affinityGroupJoinDao; + @Inject + private DedicatedResourceDao _dedicatedDao; /* (non-Javadoc) * @see com.cloud.api.query.QueryService#searchForUsers(org.apache.cloudstack.api.command.admin.user.ListUsersCmd) */ @@ -2252,12 +2256,14 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sc.addAnd("name", SearchCriteria.Op.SC, ssc); } - if (domainId != null) { + /*List all resources due to Explicit Dedication except the dedicated resources of other account + * if (domainId != null) { // for domainId != null // right now, we made the decision to only list zones associated // with this domain, private zone sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); - } else if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) { + } else */ + if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) { // it was decided to return all zones for the user's domain, and // everything above till root // list all zones belonging to this domain, and all of its @@ -2287,6 +2293,12 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { // remove disabled zones sc.addAnd("allocationState", SearchCriteria.Op.NEQ, Grouping.AllocationState.Disabled); + //remove Dedicated zones not dedicated to this domainId or subdomainId + List dedicatedZoneIds = removeDedicatedZoneNotSuitabe(domainIds); + if(!dedicatedZoneIds.isEmpty()){ + sdc.addAnd("id", SearchCriteria.Op.NIN, dedicatedZoneIds.toArray(new Object[dedicatedZoneIds.size()])); + } + } else if (account.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || account.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) { // it was decided to return all zones for the domain admin, and // everything above till root, as well as zones till the domain leaf @@ -2316,6 +2328,12 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { // remove disabled zones sc.addAnd("allocationState", SearchCriteria.Op.NEQ, Grouping.AllocationState.Disabled); + + //remove Dedicated zones not dedicated to this domainId or subdomainId + List dedicatedZoneIds = removeDedicatedZoneNotSuitabe(domainIds); + if(!dedicatedZoneIds.isEmpty()){ + sdc.addAnd("id", SearchCriteria.Op.NIN, dedicatedZoneIds.toArray(new Object[dedicatedZoneIds.size()])); + } } // handle available=FALSE option, only return zones with at least one VM running there @@ -2341,6 +2359,17 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { return _dcJoinDao.searchAndCount(sc, searchFilter); } + private List removeDedicatedZoneNotSuitabe(List domainIds) { + //remove dedicated zone of other domain + List dedicatedZoneIds = new ArrayList(); + List dedicatedResources = _dedicatedDao.listZonesNotInDomainIds(domainIds); + for (DedicatedResourceVO dr : dedicatedResources) { + if(dr != null) { + dedicatedZoneIds.add(dr.getDataCenterId()); + } + } + return dedicatedZoneIds; + } // This method is used for permissions check for both disk and service // offerings diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 79375f9a86e..52c5e2e3f87 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -96,6 +96,7 @@ import com.cloud.dc.DataCenterIpAddressVO; import com.cloud.dc.DataCenterLinkLocalIpAddressVO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.DcDetailVO; +import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; @@ -310,9 +311,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati NicSecondaryIpDao _nicSecondaryIpDao; @Inject NicIpAliasDao _nicIpAliasDao; - @Inject public ManagementService _mgr; + @Inject + DedicatedResourceDao _dedicatedDao; // FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao? @Inject protected DataCenterLinkLocalIpAddressDao _LinkLocalIpAllocDao; @@ -952,6 +954,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new CloudRuntimeException("Failed to delete pod " + podId); } + // remove from dedicated resources + DedicatedResourceVO dr = _dedicatedDao.findByPodId(podId); + if (dr != null) { + _dedicatedDao.remove(dr.getId()); + } txn.commit(); return true; @@ -1412,6 +1419,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (success) { // delete all capacity records for the zone _capacityDao.removeBy(null, zoneId, null, null, null); + // remove from dedicated resources + DedicatedResourceVO dr = _dedicatedDao.findByZoneId(zoneId); + if (dr != null) { + _dedicatedDao.remove(dr.getId()); + } } txn.commit(); @@ -1804,15 +1816,20 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati try { txn.start(); // Create the new zone in the database - DataCenterVO zone = new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, guestCidr, domain, domainId, zoneType, zoneToken, networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); + DataCenterVO zone = new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, guestCidr, null, null, zoneType, zoneToken, networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); if (allocationStateStr != null && !allocationStateStr.isEmpty()) { Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr); zone.setAllocationState(allocationState); } else { - // Zone will be disabled since 3.0. Admin shoul enable it after physical network and providers setup. + // Zone will be disabled since 3.0. Admin should enable it after physical network and providers setup. zone.setAllocationState(Grouping.AllocationState.Disabled); } zone = _zoneDao.persist(zone); + if (domainId != null) { + //zone is explicitly dedicated to this domain + DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(zone.getId(), null, null, null, domainId, null); + _dedicatedDao.persist(dedicatedResource); + } // Create default system networks createDefaultSystemNetworks(zone.getId()); diff --git a/server/src/com/cloud/dc/DedicatedResourceVO.java b/server/src/com/cloud/dc/DedicatedResourceVO.java new file mode 100644 index 00000000000..a4c88f57e02 --- /dev/null +++ b/server/src/com/cloud/dc/DedicatedResourceVO.java @@ -0,0 +1,136 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package com.cloud.dc; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="dedicated_resources") +public class DedicatedResourceVO implements DedicatedResources{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="id") + long id; + + @Column(name="data_center_id") + Long dataCenterId; + + @Column(name="pod_id") + Long podId; + + @Column(name="cluster_id") + Long clusterId; + + @Column(name="host_id") + Long hostId; + + @Column(name="uuid") + String uuid; + + @Column(name = "domain_id") + private Long domainId; + + @Column(name = "account_id") + private Long accountId; + + public DedicatedResourceVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public DedicatedResourceVO(Long dataCenterId, Long podId, Long clusterId, Long hostId, Long domainId, Long accountId) { + this.dataCenterId = dataCenterId; + this.podId = podId; + this.clusterId = clusterId; + this.hostId = hostId; + this.domainId = domainId; + this.accountId = accountId; + this.uuid = UUID.randomUUID().toString(); + } + + public long getId() { + return id; + } + + public Long getDataCenterId() { + return dataCenterId; + } + + public void setDataCenterId(long dataCenterId) { + this.dataCenterId = dataCenterId; + } + + public Long getPodId() { + return podId; + } + + public void setPodId(long podId) { + this.podId = podId; + } + + public Long getClusterId() { + return clusterId; + } + + public void setClusterId(long clusterId) { + this.clusterId = clusterId; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public DedicatedResourceVO(long dedicatedResourceId) { + this.id = dedicatedResourceId; + } + + public Long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public String getUuid() { + return this.uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} diff --git a/server/src/com/cloud/dc/dao/DedicatedResourceDao.java b/server/src/com/cloud/dc/dao/DedicatedResourceDao.java new file mode 100644 index 00000000000..a5d65d46c8e --- /dev/null +++ b/server/src/com/cloud/dc/dao/DedicatedResourceDao.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package com.cloud.dc.dao; + +import java.util.List; + +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + + +public interface DedicatedResourceDao extends GenericDao { + + DedicatedResourceVO findByZoneId(Long zoneId); + + DedicatedResourceVO findByPodId(Long podId); + + DedicatedResourceVO findByClusterId(Long clusterId); + + DedicatedResourceVO findByHostId(Long hostId); + + Pair, Integer> searchDedicatedHosts(Long hostId, Long domainId, Long accountId); + + Pair, Integer> searchDedicatedClusters(Long clusterId, Long domainId, Long accountId); + + Pair, Integer> searchDedicatedPods(Long podId, Long domainId, Long accountId); + + Pair, Integer> searchDedicatedZones(Long dataCenterId, Long domainId, Long accountId); + + List listByAccountId(Long accountId); + + List listByDomainId(Long domainId); + + List listZonesNotInDomainIds(List domainIds); +} \ No newline at end of file diff --git a/server/src/com/cloud/dc/dao/DedicatedResourceDaoImpl.java b/server/src/com/cloud/dc/dao/DedicatedResourceDaoImpl.java new file mode 100644 index 00000000000..2a3b4690a0c --- /dev/null +++ b/server/src/com/cloud/dc/dao/DedicatedResourceDaoImpl.java @@ -0,0 +1,304 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package com.cloud.dc.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.springframework.stereotype.Component; + +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; + +@Component +@Local(value={DedicatedResourceDao.class}) @DB(txn = false) +public class DedicatedResourceDaoImpl extends GenericDaoBase implements DedicatedResourceDao { + protected final SearchBuilder ZoneSearch; + protected final SearchBuilder PodSearch; + protected final SearchBuilder ClusterSearch; + protected final SearchBuilder HostSearch; + + protected SearchBuilder ListZonesByDomainIdSearch; + protected SearchBuilder ListPodsByDomainIdSearch; + protected SearchBuilder ListClustersByDomainIdSearch; + protected SearchBuilder ListHostsByDomainIdSearch; + + protected SearchBuilder ListZonesByAccountIdSearch; + protected SearchBuilder ListPodsByAccountIdSearch; + protected SearchBuilder ListClustersByAccountIdSearch; + protected SearchBuilder ListHostsByAccountIdSearch; + + protected SearchBuilder ListAllZonesSearch; + protected SearchBuilder ListAllPodsSearch; + protected SearchBuilder ListAllClustersSearch; + protected SearchBuilder ListAllHostsSearch; + + protected SearchBuilder ListByAccountId; + protected SearchBuilder ListByDomainId; + + protected SearchBuilder ZoneByDomainIdsSearch; + + protected DedicatedResourceDaoImpl() { + PodSearch = createSearchBuilder(); + PodSearch.and("podId", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); + PodSearch.done(); + + ZoneSearch = createSearchBuilder(); + ZoneSearch.and("zoneId", ZoneSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ZoneSearch.done(); + + ClusterSearch = createSearchBuilder(); + ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + ClusterSearch.done(); + + HostSearch = createSearchBuilder(); + HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ); + HostSearch.done(); + + ListZonesByDomainIdSearch = createSearchBuilder(); + ListZonesByDomainIdSearch.and("zoneId", ListZonesByDomainIdSearch.entity().getDataCenterId(), SearchCriteria.Op.NNULL); + ListZonesByDomainIdSearch.and("domainId", ListZonesByDomainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + ListZonesByDomainIdSearch.and("accountId", ListZonesByDomainIdSearch.entity().getAccountId(), SearchCriteria.Op.NULL); + ListZonesByDomainIdSearch.done(); + + ListZonesByAccountIdSearch = createSearchBuilder(); + ListZonesByAccountIdSearch.and("zoneId", ListZonesByAccountIdSearch.entity().getDataCenterId(), SearchCriteria.Op.NNULL); + ListZonesByAccountIdSearch.and("accountId", ListZonesByAccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + ListZonesByAccountIdSearch.done(); + + ListPodsByDomainIdSearch = createSearchBuilder(); + ListPodsByDomainIdSearch.and("podId", ListPodsByDomainIdSearch.entity().getPodId(), SearchCriteria.Op.NNULL); + ListPodsByDomainIdSearch.and("domainId", ListPodsByDomainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + ListPodsByDomainIdSearch.and("accountId", ListPodsByDomainIdSearch.entity().getAccountId(), SearchCriteria.Op.NULL); + ListPodsByDomainIdSearch.done(); + + ListPodsByAccountIdSearch = createSearchBuilder(); + ListPodsByAccountIdSearch.and("podId", ListPodsByAccountIdSearch.entity().getPodId(), SearchCriteria.Op.NNULL); + ListPodsByAccountIdSearch.and("accountId", ListPodsByAccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + ListPodsByAccountIdSearch.done(); + + ListClustersByDomainIdSearch = createSearchBuilder(); + ListClustersByDomainIdSearch.and("clusterId", ListClustersByDomainIdSearch.entity().getClusterId(), SearchCriteria.Op.NNULL); + ListClustersByDomainIdSearch.and("domainId", ListClustersByDomainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + ListClustersByDomainIdSearch.and("accountId", ListClustersByDomainIdSearch.entity().getAccountId(), SearchCriteria.Op.NULL); + ListClustersByDomainIdSearch.done(); + + ListClustersByAccountIdSearch = createSearchBuilder(); + ListClustersByAccountIdSearch.and("clusterId", ListClustersByAccountIdSearch.entity().getClusterId(), SearchCriteria.Op.NNULL); + ListClustersByAccountIdSearch.and("accountId", ListClustersByAccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + ListClustersByAccountIdSearch.done(); + + ListHostsByDomainIdSearch = createSearchBuilder(); + ListHostsByDomainIdSearch.and("hostId", ListHostsByDomainIdSearch.entity().getHostId(), SearchCriteria.Op.NNULL); + ListHostsByDomainIdSearch.and("domainId", ListHostsByDomainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + ListHostsByDomainIdSearch.and("accountId", ListHostsByDomainIdSearch.entity().getAccountId(), SearchCriteria.Op.NULL); + ListHostsByDomainIdSearch.done(); + + ListHostsByAccountIdSearch = createSearchBuilder(); + ListHostsByAccountIdSearch.and("hostId", ListHostsByAccountIdSearch.entity().getHostId(), SearchCriteria.Op.NNULL); + ListHostsByAccountIdSearch.and("accountId", ListHostsByAccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + ListHostsByAccountIdSearch.done(); + + ListAllZonesSearch = createSearchBuilder(); + ListAllZonesSearch.and("zoneId", ListAllZonesSearch.entity().getDataCenterId(), Op.EQ); + ListAllZonesSearch.and("podId", ListAllZonesSearch.entity().getPodId(), Op.NULL); + ListAllZonesSearch.and("clusterId", ListAllZonesSearch.entity().getClusterId(), Op.NULL); + ListAllZonesSearch.and("hostId", ListAllZonesSearch.entity().getHostId(), Op.NULL); + ListAllZonesSearch.and("accountId", ListAllZonesSearch.entity().getAccountId(), Op.EQ); + ListAllZonesSearch.and("domainId", ListAllZonesSearch.entity().getDomainId(), Op.EQ); + ListAllZonesSearch.done(); + + ListAllPodsSearch = createSearchBuilder(); + ListAllPodsSearch.and("zoneId", ListAllPodsSearch.entity().getDataCenterId(), Op.NULL); + ListAllPodsSearch.and("podId", ListAllPodsSearch.entity().getPodId(), Op.EQ); + ListAllPodsSearch.and("clusterId", ListAllPodsSearch.entity().getClusterId(), Op.NULL); + ListAllPodsSearch.and("hostId", ListAllPodsSearch.entity().getHostId(), Op.NULL); + ListAllPodsSearch.and("accountId", ListAllPodsSearch.entity().getAccountId(), Op.EQ); + ListAllPodsSearch.and("domainId", ListAllPodsSearch.entity().getDomainId(), Op.EQ); + ListAllPodsSearch.done(); + + ListAllClustersSearch = createSearchBuilder(); + ListAllClustersSearch.and("zoneId", ListAllClustersSearch.entity().getDataCenterId(), Op.NULL); + ListAllClustersSearch.and("podId", ListAllClustersSearch.entity().getPodId(), Op.NULL); + ListAllClustersSearch.and("clusterId", ListAllClustersSearch.entity().getClusterId(), Op.EQ); + ListAllClustersSearch.and("hostId", ListAllClustersSearch.entity().getHostId(), Op.NULL); + ListAllClustersSearch.and("accountId", ListAllClustersSearch.entity().getAccountId(), Op.EQ); + ListAllClustersSearch.and("domainId", ListAllClustersSearch.entity().getDomainId(), Op.EQ); + ListAllClustersSearch.done(); + + ListAllHostsSearch = createSearchBuilder(); + ListAllHostsSearch.and("zoneId", ListAllHostsSearch.entity().getDataCenterId(), Op.NULL); + ListAllHostsSearch.and("podId", ListAllHostsSearch.entity().getPodId(), Op.NULL); + ListAllHostsSearch.and("clusterId", ListAllHostsSearch.entity().getClusterId(), Op.NULL); + ListAllHostsSearch.and("hostId", ListAllHostsSearch.entity().getHostId(), Op.EQ); + ListAllHostsSearch.and("accountId", ListAllHostsSearch.entity().getAccountId(), Op.EQ); + ListAllHostsSearch.and("domainId", ListAllHostsSearch.entity().getDomainId(), Op.EQ); + ListAllHostsSearch.done(); + + ListByAccountId = createSearchBuilder(); + ListByAccountId.and("accountId", ListByAccountId.entity().getAccountId(), SearchCriteria.Op.EQ); + ListByAccountId.done(); + + ListByDomainId = createSearchBuilder(); + ListByDomainId.and("accountId", ListByDomainId.entity().getAccountId(), SearchCriteria.Op.NULL); + ListByDomainId.and("domainId", ListByDomainId.entity().getDomainId(), SearchCriteria.Op.EQ); + ListByDomainId.done(); + + ZoneByDomainIdsSearch = createSearchBuilder(); + ZoneByDomainIdsSearch.and("zoneId", ZoneByDomainIdsSearch.entity().getDataCenterId(), SearchCriteria.Op.NNULL); + ZoneByDomainIdsSearch.and("domainId", ZoneByDomainIdsSearch.entity().getDomainId(), SearchCriteria.Op.NIN); + ZoneByDomainIdsSearch.done(); + } + + @Override + public DedicatedResourceVO findByZoneId(Long zoneId) { + SearchCriteria sc = ZoneSearch.create(); + sc.setParameters("zoneId", zoneId); + return findOneBy(sc); + } + + @Override + public DedicatedResourceVO findByPodId(Long podId) { + SearchCriteria sc = PodSearch.create(); + sc.setParameters("podId", podId); + + return findOneBy(sc); + } + + @Override + public DedicatedResourceVO findByClusterId(Long clusterId) { + SearchCriteria sc = ClusterSearch.create(); + sc.setParameters("clusterId", clusterId); + + return findOneBy(sc); + } + + @Override + public DedicatedResourceVO findByHostId(Long hostId) { + SearchCriteria sc = HostSearch.create(); + sc.setParameters("hostId", hostId); + + return findOneBy(sc); + } + + @Override + public Pair, Integer> searchDedicatedZones(Long dataCenterId, Long domainId, Long accountId){ + SearchCriteria sc = ListAllZonesSearch.create(); + if (dataCenterId != null) { + sc.setParameters("dataCenterId", dataCenterId); + } + if(domainId != null) { + sc.setParameters("domainId", domainId); + if(accountId != null) { + sc.setParameters("accountId", accountId); + } else { + sc.setParameters("accountId", (Object)null); + } + } + return searchAndCount(sc, null); + } + @Override + public Pair, Integer> searchDedicatedPods(Long podId, Long domainId, Long accountId){ + SearchCriteria sc = ListAllPodsSearch.create(); + if (podId != null) { + sc.setParameters("podId", podId); + } + if(domainId != null) { + sc.setParameters("domainId", domainId); + if(accountId != null) { + sc.setParameters("accountId", accountId); + } else { + sc.setParameters("accountId", (Object)null); + } + } + return searchAndCount(sc, null); + } + + @Override + public Pair, Integer> searchDedicatedClusters(Long clusterId, Long domainId, Long accountId){ + SearchCriteria sc = ListAllClustersSearch.create(); + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + if(domainId != null) { + sc.setParameters("domainId", domainId); + if(accountId != null) { + sc.setParameters("accountId", accountId); + } else { + sc.setParameters("accountId", (Object)null); + } + } + return searchAndCount(sc, null); + } + + @Override + public Pair, Integer> searchDedicatedHosts(Long hostId, Long domainId, Long accountId){ + SearchCriteria sc = ListAllHostsSearch.create(); + if (hostId != null) { + sc.setParameters("hostId", hostId); + } + if(domainId != null) { + sc.setParameters("domainId", domainId); + if(accountId != null) { + sc.setParameters("accountId", accountId); + } else { + sc.setParameters("accountId", (Object)null); + } + } + return searchAndCount(sc, null); + } + + @Override + public List listByAccountId(Long accountId){ + SearchCriteria sc = ListByAccountId.create(); + sc.setParameters("accountId", accountId); + return listBy(sc); + } + + @Override + public List listByDomainId(Long domainId){ + SearchCriteria sc = ListByDomainId.create(); + sc.setParameters("domainId", domainId); + return listBy(sc); + } + + @Override + public List listZonesNotInDomainIds(List domainIds) { + SearchCriteria sc = ZoneByDomainIdsSearch.create(); + sc.setParameters("domainId", domainIds.toArray(new Object[domainIds.size()])); + return listBy(sc); + } + + @Override + public boolean remove(Long id) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + DedicatedResourceVO resource = createForUpdate(); + update(id, resource); + + boolean result = super.remove(id); + txn.commit(); + return result; + } +} diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 74bd6d04a3e..c6e8d7d7729 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -30,6 +30,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.*; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd; import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd; @@ -84,6 +85,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterVSMMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterIpAddressDao; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.PlannerHostReservationVO; import com.cloud.deploy.dao.PlannerHostReservationDao; @@ -219,6 +221,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, protected StorageService _storageSvr; @Inject PlannerHostReservationDao _plannerHostReserveDao; + @Inject + protected DedicatedResourceDao _dedicatedDao; protected List _discoverers; public List getDiscoverers() { @@ -1026,6 +1030,11 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, hostCapacitySC.addAnd("capacityType", SearchCriteria.Op.IN, capacityTypes); _capacityDao.remove(hostCapacitySC); + // remove from dedicated resources + DedicatedResourceVO dr = _dedicatedDao.findByHostId(hostId); + if (dr != null) { + _dedicatedDao.remove(dr.getId()); + } txn.commit(); return true; } @@ -1100,11 +1109,16 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, && Boolean.parseBoolean(_configDao .getValue(Config.VmwareUseNexusVSwitch .toString()))) { - _clusterVSMMapDao.removeByClusterId(cmd.getId()); - } - } + _clusterVSMMapDao.removeByClusterId(cmd.getId()); + } + // remove from dedicated resources + DedicatedResourceVO dr = _dedicatedDao.findByClusterId(cluster.getId()); + if (dr != null) { + _dedicatedDao.remove(dr.getId()); + } + } - txn.commit(); + txn.commit(); return true; } catch (CloudRuntimeException e) { throw e; diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index e72005e8214..3f06e419cdb 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -61,8 +61,10 @@ import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; import com.cloud.configuration.dao.ResourceLimitDao; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterVnetDao; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -229,6 +231,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Inject private AffinityGroupDao _affinityGroupDao; @Inject + private AccountGuestVlanMapDao _accountGuestVlanMapDao; @Inject private DataCenterVnetDao _dataCenterVnetDao; @@ -236,6 +239,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M private ResourceLimitService _resourceLimitMgr; @Inject private ResourceLimitDao _resourceLimitDao; + @Inject + private DedicatedResourceDao _dedicatedDao; private List _userAuthenticators; List _userPasswordEncoders; @@ -738,7 +743,16 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M s_logger.debug("Releasing portable ip " + ip + " as a part of account id=" + accountId + " cleanup"); _networkMgr.releasePortableIpAddress(ip.getId()); } - + //release dedication if any + List dedicatedResources = _dedicatedDao.listByAccountId(accountId); + if (dedicatedResources != null && !dedicatedResources.isEmpty()) { + s_logger.debug("Releasing dedicated resources for account " + accountId); + for (DedicatedResourceVO dr : dedicatedResources){ + if (!_dedicatedDao.remove(dr.getId())) { + s_logger.warn("Fail to release dedicated resources for account " + accountId); + } + } + } return true; } catch (Exception ex) { s_logger.warn("Failed to cleanup account " + account + " due to ", ex); @@ -1488,6 +1502,16 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M try { List accountsForCleanupInDomain = _accountDao.findCleanupsForRemovedAccounts(domainId); if (accountsForCleanupInDomain.isEmpty()) { + //release dedication if any, before deleting the domain + List dedicatedResources = _dedicatedDao.listByDomainId(domainId); + if (dedicatedResources != null && !dedicatedResources.isEmpty()) { + s_logger.debug("Releasing dedicated resources for domain" + domainId); + for (DedicatedResourceVO dr : dedicatedResources){ + if (!_dedicatedDao.remove(dr.getId())) { + s_logger.warn("Fail to release dedicated resources for domain " + domainId); + } + } + } s_logger.debug("Removing inactive domain id=" + domainId); _domainMgr.removeDomain(domainId); } else { diff --git a/server/src/com/cloud/user/DomainManagerImpl.java b/server/src/com/cloud/user/DomainManagerImpl.java index 00a779e2ff9..20537bae926 100644 --- a/server/src/com/cloud/user/DomainManagerImpl.java +++ b/server/src/com/cloud/user/DomainManagerImpl.java @@ -35,6 +35,8 @@ import com.cloud.configuration.Resource.ResourceOwnerType; import com.cloud.configuration.ResourceLimit; import com.cloud.configuration.dao.ResourceCountDao; import com.cloud.configuration.dao.ResourceLimitDao; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -87,6 +89,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom private RegionManager _regionMgr; @Inject private ResourceLimitDao _resourceLimitDao; + @Inject + private DedicatedResourceDao _dedicatedDao; @Override public Domain getDomain(long domainId) { @@ -237,6 +241,17 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom CloudRuntimeException e = new CloudRuntimeException("Delete failed on domain " + domain.getName() + " (id: " + domain.getId() + "); Please make sure all users and sub domains have been removed from the domain before deleting"); e.addProxyObject(domain.getUuid(), "domainId"); throw e; + } else { + //release dedication if any, before deleting the domain + List dedicatedResources = _dedicatedDao.listByDomainId(domain.getId()); + if (dedicatedResources != null && !dedicatedResources.isEmpty()) { + s_logger.debug("Releasing dedicated resources for domain" + domain.getId()); + for (DedicatedResourceVO dr : dedicatedResources){ + if (!_dedicatedDao.remove(dr.getId())) { + s_logger.warn("Fail to release dedicated resources for domain " + domain.getId()); + } + } + } } } else { rollBackState = true; @@ -333,6 +348,17 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom boolean deleteDomainSuccess = true; List accountsForCleanup = _accountDao.findCleanupsForRemovedAccounts(domainId); if (accountsForCleanup.isEmpty()) { + //release dedication if any, before deleting the domain + List dedicatedResources = _dedicatedDao.listByDomainId(domainId); + if (dedicatedResources != null && !dedicatedResources.isEmpty()) { + s_logger.debug("Releasing dedicated resources for domain" + domainId); + for (DedicatedResourceVO dr : dedicatedResources){ + if (!_dedicatedDao.remove(dr.getId())) { + s_logger.warn("Fail to release dedicated resources for domain " + domainId); + } + } + } + //delete domain deleteDomainSuccess = _domainDao.remove(domainId); // Delete resource count and resource limits entries set for this domain (if there are any). diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index a55c6f8fb81..86bdb14ff1a 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -93,9 +93,11 @@ import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; @@ -402,6 +404,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @Inject AffinityGroupDao _affinityGroupDao; @Inject + DedicatedResourceDao _dedicatedDao; + @Inject ConfigurationServer _configServer; protected ScheduledExecutorService _executor = null; @@ -2362,8 +2366,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + zone.getId()); } - if (zone.getDomainId() != null) { - DomainVO domain = _domainDao.findById(zone.getDomainId()); + boolean isExplicit = false; + // check affinity group type Explicit dedication + if (affinityGroupIdList != null) { + for (Long affinityGroupId : affinityGroupIdList) { + AffinityGroupVO ag = _affinityGroupDao.findById(affinityGroupId); + String agType = ag.getType(); + if (agType.equals("ExplicitDedication")) { + isExplicit = true; + } + } + } + // check if zone is dedicated + DedicatedResourceVO dedicatedZone = _dedicatedDao.findByZoneId(zone.getId()); + if (isExplicit && dedicatedZone != null) { + DomainVO domain = _domainDao.findById(dedicatedZone.getDomainId()); if (domain == null) { throw new CloudRuntimeException("Unable to find the domain " + zone.getDomainId() + " for the zone: " + zone); @@ -3676,6 +3693,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + destinationHost.getResourceState()); } + HostVO srcHost = _hostDao.findById(srcHostId); + HostVO destHost = _hostDao.findById(destinationHost.getId()); + //if srcHost is dedicated and destination Host is not + if (checkIfHostIsDedicated(srcHost) && !checkIfHostIsDedicated(destHost)) { + //raise an alert + String msg = "VM is migrated on a non-dedicated host " + destinationHost.getName(); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg); + } + //if srcHost is non dedicated but destination Host is. + if (!checkIfHostIsDedicated(srcHost) && checkIfHostIsDedicated(destHost)) { + //raise an alert + String msg = "VM is migrated on a dedicated host " + destinationHost.getName(); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg); + } + // call to core process DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId()); HostPodVO pod = _podDao.findById(destinationHost.getPodId()); @@ -3703,6 +3735,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use return migratedVm; } + private boolean checkIfHostIsDedicated(HostVO host) { + long hostId = host.getId(); + DedicatedResourceVO dedicatedHost = _dedicatedDao.findByHostId(hostId); + DedicatedResourceVO dedicatedClusterOfHost = _dedicatedDao.findByClusterId(host.getClusterId()); + DedicatedResourceVO dedicatedPodOfHost = _dedicatedDao.findByPodId(host.getPodId()); + if(dedicatedHost != null || dedicatedClusterOfHost != null || dedicatedPodOfHost != null) { + return true; + } else { + return false; + } + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, @@ -3822,7 +3866,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use VMInstanceVO migratedVm = _itMgr.migrateWithStorage(vm, srcHostId, destinationHost.getId(), volToPoolObjectMap); return migratedVm; - } +} @DB @Override diff --git a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java index f862a2a4760..3f6fe9c4e1b 100644 --- a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java +++ b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java @@ -49,6 +49,7 @@ import com.cloud.dc.dao.DataCenterIpAddressDaoImpl; import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDao; import com.cloud.dc.dao.DataCenterVnetDaoImpl; import com.cloud.dc.dao.DcDetailsDaoImpl; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDaoImpl; import com.cloud.dc.dao.PodVlanDaoImpl; import com.cloud.dc.dao.PodVlanMapDaoImpl; @@ -173,7 +174,7 @@ import org.apache.cloudstack.region.PortableIpRangeDaoImpl; }, includeFilters={@Filter(value=ChildTestConfiguration.Library.class, type=FilterType.CUSTOM)}, useDefaultFilters=false -) + ) public class ChildTestConfiguration { @@ -332,6 +333,11 @@ public class ChildTestConfiguration { return Mockito.mock(NetworkDao.class); } + @Bean + public DedicatedResourceDao DedicatedResourceDao() { + return Mockito.mock(DedicatedResourceDao.class); + } + @Bean public NetworkOfferingServiceMapDao networkOfferingServiceMapDao() { return Mockito.mock(NetworkOfferingServiceMapDao.class); @@ -339,7 +345,7 @@ public class ChildTestConfiguration { @Bean public DataCenterLinkLocalIpAddressDao datacenterLinkLocalIpAddressDao() { - return Mockito.mock(DataCenterLinkLocalIpAddressDao.class); + return Mockito.mock(DataCenterLinkLocalIpAddressDao.class); } @Bean @@ -357,7 +363,6 @@ public class ChildTestConfiguration { return Mockito.mock(AccountDetailsDao.class); } - public static class Library implements TypeFilter { @Override diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index b1feb022836..79550aee1bb 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -2453,7 +2453,6 @@ CREATE TABLE `cloud`.`resource_tags` ( CONSTRAINT `uc_resource_tags__uuid` UNIQUE (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - CREATE TABLE `cloud`.`external_nicira_nvp_devices` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `uuid` varchar(255) UNIQUE, diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index e081a252386..196706f1b15 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -178,6 +178,27 @@ CREATE TABLE `cloud`.`affinity_group_vm_map` ( +CREATE TABLE `cloud`.`dedicated_resources` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40), + `data_center_id` bigint unsigned COMMENT 'data center id', + `pod_id` bigint unsigned COMMENT 'pod id', + `cluster_id` bigint unsigned COMMENT 'cluster id', + `host_id` bigint unsigned COMMENT 'host id', + `domain_id` bigint unsigned COMMENT 'domain id of the domain to which resource is dedicated', + `account_id` bigint unsigned COMMENT 'account id of the account to which resource is dedicated', + PRIMARY KEY (`id`), + CONSTRAINT `fk_dedicated_resources__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `cloud`.`data_center`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_dedicated_resources__pod_id` FOREIGN KEY (`pod_id`) REFERENCES `cloud`.`host_pod_ref`(`id`), + CONSTRAINT `fk_dedicated_resources__cluster_id` FOREIGN KEY (`cluster_id`) REFERENCES `cloud`.`cluster`(`id`), + CONSTRAINT `fk_dedicated_resources__host_id` FOREIGN KEY (`host_id`) REFERENCES `cloud`.`host`(`id`), + CONSTRAINT `fk_dedicated_resources__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`), + CONSTRAINT `fk_dedicated_resources__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`), + INDEX `i_dedicated_resources_domain_id`(`domain_id`), + INDEX `i_dedicated_resources_account_id`(`account_id`), + CONSTRAINT `uc_dedicated_resources__uuid` UNIQUE (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE nic_secondary_ips ( `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, `uuid` varchar(40), @@ -203,6 +224,9 @@ ALTER TABLE `cloud`.`event` ADD COLUMN `archived` tinyint(1) unsigned NOT NULL D INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'alert.purge.interval', '86400', 'The interval (in seconds) to wait before running the alert purge thread'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'alert.purge.delay', '0', 'Alerts older than specified number days will be purged. Set this value to 0 to never delete alerts'); +INSERT INTO `cloud`.`dedicated_resources` (`data_center_id`, `domain_id`) SELECT `id`, `domain_id` FROM `cloud`.`data_center` WHERE `domain_id` IS NOT NULL; +UPDATE `cloud`.`data_center` SET `domain_id` = NULL WHERE `domain_id` IS NOT NULL; + DROP VIEW IF EXISTS `cloud`.`event_view`; CREATE VIEW `cloud`.`event_view` AS select @@ -1720,7 +1744,8 @@ UPDATE `cloud`.`snapshots` set swift_id=null where swift_id=0; -- Re-enable foreign key checking, at the end of the upgrade path -SET foreign_key_checks = 1; +SET foreign_key_checks = 1; + UPDATE `cloud`.`snapshot_policy` set uuid=id WHERE uuid is NULL; #update shared sg enabled network with not null name in Advance Security Group enabled network UPDATE `cloud`.`networks` set name='Shared SG enabled network', display_text='Shared SG enabled network' WHERE name IS null AND traffic_type='Guest' AND data_center_id IN (select id from data_center where networktype='Advanced' and is_security_group_enabled=1) AND acl_type='Domain'; diff --git a/test/integration/component/test_explicit_dedication.py b/test/integration/component/test_explicit_dedication.py new file mode 100644 index 00000000000..21a4904e71b --- /dev/null +++ b/test/integration/component/test_explicit_dedication.py @@ -0,0 +1,231 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with 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. +""" P1 tests for Storage motion +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from nose.plugins.attrib import attr +#Import System modules +import time + +_multiprocess_shared_ = True +class Services: + """Test explicit dedication + """ + + def __init__(self): + self.services = { + "disk_offering":{ + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "testexplicit", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + "virtual_machine" : + { + "affinity": { + "name": "explicit", + "type": "ExplicitDedication", + }, + "hypervisor" : "XenServer", + }, + "small": + # Create a small virtual machine instance with disk offering + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": + { + "for-explicit": + { + # Small service offering ID to for change VM + # service offering from medium to small + "name": "For explicit", + "displaytext": "For explicit", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512 + } + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "passwordenabled": True, + }, + "diskdevice": '/dev/xvdd', + # Disk device where ISO is attached to instance + "mount_dir": "/mnt/tmp", + "sleep": 60, + "timeout": 10, + "ostype": 'CentOS 5.3 (64-bit)' + } + +class TestExplicitDedication(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestExplicitDedication, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = cls.template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.small_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["for-explicit"] + ) + + #cls.ag = AffinityGroup.create(cls.api_client, cls.services["virtual_machine"]["affinity"], + # account=cls.services["account"], domainid=cls.domain.id) + + cls._cleanup = [ + cls.small_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + cls.api_client = super(TestExplicitDedication, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + #Clean up, terminate the created ISOs + cleanup_resources(self.apiclient, self.cleanup) + return + + # This test requires multi host and at least one host which is empty (no vms should + # be running on that host). It explicitly dedicates empty host to an account, deploys + # a vm for that account and verifies that the vm gets deployed to the dedicated host. + @attr(tags = ["advanced", "basic", "multihosts", "explicitdedication"]) + def test_01_deploy_vm_with_explicit_dedication(self): + """Test explicit dedication is placing vms of an account on dedicated hosts. + """ + # Validate the following + # 1. Find and dedicate an empty host to an account. + # 2. Create an affinity group for explicit dedication. + # 3. Create a vm deployment by passing the affinity group as a parameter. + # 4. Validate the vm got deployed on the dedicated host. + # 5. Cleanup. + + # list and find an empty hosts + all_hosts = list_hosts( + self.apiclient, + type='Routing', + ) + + empty_host = None + for host in all_hosts: + vms_on_host = list_virtual_machines( + self.api_client, + hostid=host.id) + if not vms_on_host: + empty_host = host + break + + # Create an affinity group for explicit dedication. + agCmd = createAffinityGroup.createAffinityGroupCmd() + agCmd.name = "explicit-affinity" + agCmd.displayText = "explicit-affinity" + agCmd.account = self.account.name + agCmd.domainid = self.account.domainid + agCmd.type = self.services['virtual_machine']['affinity']['type'] + self.apiclient.createAffinityGroup(agCmd) + + # dedicate the empty host to this account. + dedicateCmd = dedicateHost.dedicateHostCmd() + dedicateCmd.hostid = empty_host.id + dedicateCmd.domainid = self.domain.id + self.apiclient.dedicateHost(dedicateCmd) + + # deploy vm on the dedicated resource. + vm = VirtualMachine.create( + self.api_client, + self.services["small"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.small_offering.id, + affinitygroupnames=["explicit-affinity"], + mode=self.services["mode"] + ) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=vm.id + ) + + vm_response = list_vm_response[0] + + self.assertEqual( + vm_response.hostid, + empty_host.id, + "Check destination hostID of deployed VM" + ) + + # release the dedicated host to this account. + releaseCmd = releaseDedicatedHost.releaseDedicatedHostCmd() + releaseCmd.hostid = empty_host.id + releaseCmd.domainid = self.domain.id + self.apiclient.releaseDedicatedHost(releaseCmd) + + #Deletion of the created VM and affinity group is taken care as part of account clean + + return diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 793f7209449..cef50ee8702 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -143,7 +143,9 @@ known_categories = { 'AffinityGroup': 'Affinity Group', 'InternalLoadBalancer': 'Internal LB', 'DeploymentPlanners': 'Configuration', - 'PortableIp': 'Portable IP' + 'PortableIp': 'Portable IP', + 'dedicateHost': 'Dedicate Resources', + 'releaseDedicatedHost': 'Dedicate Resources' } From 965c7b9c35fc5680b2f35a8c8fed35c1232a643b Mon Sep 17 00:00:00 2001 From: Saksham Srivastava Date: Thu, 30 May 2013 01:03:38 -0700 Subject: [PATCH 195/412] Patch 2: CLOUDSTACK-681: Dedicated Resources - Explicit Dedication, Private zone, pod, cluster or host Patch 2 for https://reviews.apache.org/r/11379/ Created for files server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java, server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java, server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java --- .../ConfigurationManagerImpl.java | 1 + .../deploy/DeploymentPlanningManagerImpl.java | 60 ++++++++++++++++++- .../vm/DeploymentPlanningManagerImplTest.java | 9 +++ .../affinity/AffinityApiUnitTest.java | 10 ++++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 52c5e2e3f87..59e70cfcc5a 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -109,6 +109,7 @@ import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterIpAddressDao; import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDao; import com.cloud.dc.dao.DcDetailsDao; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 795b526c403..d954c8bf3e2 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -30,6 +30,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.affinity.AffinityGroupProcessor; +import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; @@ -53,9 +54,12 @@ import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage; @@ -91,6 +95,7 @@ import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.DiskProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; @@ -157,6 +162,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy @Inject protected HostDao _hostDao; @Inject protected HostPodDao _podDao; @Inject protected ClusterDao _clusterDao; + @Inject protected DedicatedResourceDao _dedicatedDao; @Inject protected GuestOSDao _guestOSDao = null; @Inject protected GuestOSCategoryDao _guestOSCategoryDao = null; @Inject protected DiskOfferingDao _diskOfferingDao; @@ -196,6 +202,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy // call affinitygroup chain VirtualMachine vm = vmProfile.getVirtualMachine(); long vmGroupCount = _affinityGroupVMMapDao.countAffinityGroupsForVm(vm.getId()); + DataCenter dc = _dcDao.findById(vm.getDataCenterId()); if (vmGroupCount > 0) { for (AffinityGroupProcessor processor : _affinityProcessors) { @@ -203,13 +210,14 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } } + checkForNonDedicatedResources(vm, dc, avoids); if (s_logger.isDebugEnabled()) { s_logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid()); } // call planners - DataCenter dc = _dcDao.findById(vm.getDataCenterId()); + //DataCenter dc = _dcDao.findById(vm.getDataCenterId()); // check if datacenter is in avoid set if (avoids.shouldAvoid(dc)) { if (s_logger.isDebugEnabled()) { @@ -430,6 +438,56 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy return dest; } + private void checkForNonDedicatedResources(VirtualMachine vm, DataCenter dc, ExcludeList avoids) { + boolean isExplicit = false; + // check affinity group of type Explicit dedication exists + List vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), "ExplicitDedication"); + + if (vmGroupMappings != null && !vmGroupMappings.isEmpty()){ + isExplicit = true; + } + + if (!isExplicit && vm.getType() == VirtualMachine.Type.User) { + //add explicitly dedicated resources in avoidList + DedicatedResourceVO dedicatedZone = _dedicatedDao.findByZoneId(dc.getId()); + if (dedicatedZone != null) { + throw new CloudRuntimeException("Failed to deploy VM. Zone " + dc.getName() + " is dedicated."); + } + + List podsInDc = _podDao.listByDataCenterId(dc.getId()); + for (HostPodVO pod : podsInDc) { + DedicatedResourceVO dedicatedPod = _dedicatedDao.findByPodId(pod.getId()); + if (dedicatedPod != null) { + avoids.addPod(dedicatedPod.getPodId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot use this dedicated pod " + pod.getName() + "."); + } + } + } + + List clusterInDc = _clusterDao.listClustersByDcId(dc.getId()); + for (ClusterVO cluster : clusterInDc) { + DedicatedResourceVO dedicatedCluster = _dedicatedDao.findByClusterId(cluster.getId()); + if (dedicatedCluster != null) { + avoids.addCluster(dedicatedCluster.getClusterId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot use this dedicated Cluster " + cluster.getName() + "."); + } + } + } + List hostInDc = _hostDao.listByDataCenterId(dc.getId()); + for (HostVO host : hostInDc) { + DedicatedResourceVO dedicatedHost = _dedicatedDao.findByHostId(host.getId()); + if (dedicatedHost != null) { + avoids.addHost(dedicatedHost.getHostId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot use this dedicated host " + host.getName() + "."); + } + } + } + } + } + private void resetAvoidSet(ExcludeList avoidSet, ExcludeList removeSet) { if (avoidSet.getDataCentersToAvoid() != null && removeSet.getDataCentersToAvoid() != null) { avoidSet.getDataCentersToAvoid().removeAll(removeSet.getDataCentersToAvoid()); diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java index e3b7d311ba7..442c2be3969 100644 --- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -40,6 +40,7 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; @@ -113,6 +114,9 @@ public class DeploymentPlanningManagerImplTest { @Inject ClusterDao _clusterDao; + @Inject + DedicatedResourceDao _dedicatedDao; + private static long domainId = 5L; private static long dataCenterId = 1L; @@ -252,6 +256,11 @@ public class DeploymentPlanningManagerImplTest { } @Bean + public DedicatedResourceDao dedicatedResourceDao() { + return Mockito.mock(DedicatedResourceDao.class); + } + + @Bean public GuestOSDao guestOSDao() { return Mockito.mock(GuestOSDao.class); } diff --git a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java index 24c5d3d4e10..5816b2829f2 100644 --- a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java +++ b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java @@ -52,6 +52,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; +import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.event.EventUtils; import com.cloud.event.EventVO; import com.cloud.event.dao.EventDao; @@ -105,6 +106,10 @@ public class AffinityApiUnitTest { @Inject EventDao _eventDao; + @Inject + DedicatedResourceDao _dedicatedDao; + + private static long domainId = 5L; @@ -219,6 +224,11 @@ public class AffinityApiUnitTest { } @Bean + public DedicatedResourceDao dedicatedResourceDao() { + return Mockito.mock(DedicatedResourceDao.class); + } + + @Bean public AccountManager accountManager() { return Mockito.mock(AccountManager.class); } From 0a5e3fa60778ccd4ac22a99809498372399405f6 Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Thu, 30 May 2013 15:10:41 +0530 Subject: [PATCH 196/412] CLOUDSTACK-2060 Global config to turn off dynamically scale vm functionality --- .../xen/resource/CitrixResourceBase.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 4680fde9980..7626d1205c7 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -627,6 +627,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe protected void scaleVM(Connection conn, VM vm, VirtualMachineTO vmSpec, Host host) throws XenAPIException, XmlRpcException { + Long staticMemoryMax = vm.getMemoryStaticMax(conn); + Long staticMemoryMin = vm.getMemoryStaticMin(conn); + Long newDynamicMemoryMin = vmSpec.getMinRam() * 1024 * 1024; + Long newDynamicMemoryMax = vmSpec.getMaxRam() * 1024 * 1024; + if (staticMemoryMin > newDynamicMemoryMin || newDynamicMemoryMax > staticMemoryMax) { + throw new CloudRuntimeException("Cannot scale up the vm because of memory constraint violation: 0 <= memory-static-min <= memory-dynamic-min <= memory-dynamic-max <= memory-static-max "); + } + vm.setMemoryDynamicRange(conn, vmSpec.getMinRam() * 1024 * 1024, vmSpec.getMaxRam() * 1024 * 1024); vm.setVCPUsNumberLive(conn, (long)vmSpec.getCpus()); @@ -663,10 +671,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe // If DMC is not enable then don't execute this command. if (!isDmcEnabled(conn, host)) { - String msg = "Unable to scale the vm: " + vmName + " as DMC - Dynamic memory control is not enabled for the XenServer:" + _host.uuid + " ,check your license and hypervisor version."; - s_logger.info(msg); - return new ScaleVmAnswer(cmd, false, msg); + throw new CloudRuntimeException("Unable to scale the vm: " + vmName + " as DMC - Dynamic memory control is not enabled for the XenServer:" + _host.uuid + " ,check your license and hypervisor version."); } + // stop vm which is running on this host or is in halted state Iterator iter = vms.iterator(); while ( iter.hasNext() ) { @@ -686,13 +693,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe for (VM vm : vms) { VM.Record vmr = vm.getRecord(conn); try { - Map hostParams = new HashMap(); - hostParams = host.getLicenseParams(conn); - if (hostParams.get("restrict_dmc").equalsIgnoreCase("true")) { - throw new CloudRuntimeException("Host "+ _host.uuid + " does not support Dynamic Memory Control, so we cannot scale up the vm"); - } scaleVM(conn, vm, vmSpec, host); - } catch (Exception e) { String msg = "Catch exception " + e.getClass().getName() + " when scaling VM:" + vmName + " due to " + e.toString(); s_logger.debug(msg); From 6cbd85f0957095c6c2762aee37f1e099dcaf4c03 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Thu, 30 May 2013 15:27:18 +0530 Subject: [PATCH 197/412] Implicit Dedication UI changes --- ui/scripts/configuration.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 211d7b786b7..43dd68f65d8 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -164,7 +164,7 @@ } }, - plannerKey:{label:'Planner Key' , docID:'helpImplicitPlannerKey'}, + // plannerKey:{label:'Planner Key' , docID:'helpImplicitPlannerKey'}, plannerMode:{ label:'Planner Mode', select:function(args){ @@ -213,9 +213,9 @@ }; var array1 =[]; - if(args.data.plannerMode != null && args.data.plannerKey !=""){ - array1.push("&serviceofferingdetails[0]." + args.data.plannerKey + "=" + args.data.plannerMode); - } + if(args.data.deploymentPlanner == "ImplicitDedicationPlanner" && args.data.plannerMode != ""){ + array1.push("&serviceofferingdetails[0].ImplicitDedicationMode" + "=" + args.data.plannerMode); + } if(args.data.networkRate != null && args.data.networkRate.length > 0) { $.extend(data, { From 35fe8e86c0a37214994fc669cc7648d1ce99ffd8 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Thu, 30 May 2013 15:30:32 +0530 Subject: [PATCH 198/412] removing the tooltip helper for implicit dedication --- ui/scripts/docs.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index 7c1aaf83c35..65233c10dda 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -16,14 +16,6 @@ // under the License. cloudStack.docs = { - //Implicit Planner - - helpImplicitPlannerKey:{ - - desc:'Please provide a Planner key for the Implicit Planner you are going to use and then select its mode below .Eg - Planner Key :ImplicitDedicationMode', - externalLink:'' - - }, //Delete/archive events helpEventsDeleteType:{ From 08e5a45def1ff7685f9067114c769037e54fb411 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 30 May 2013 16:17:28 +0530 Subject: [PATCH 199/412] CLOUDSTACK-769 --- docs/en-US/add-loadbalancer-rule-vpc.xml | 364 ++++++++++++++++++----- docs/en-US/images/vpc-lb.png | Bin 0 -> 181811 bytes 2 files changed, 286 insertions(+), 78 deletions(-) create mode 100644 docs/en-US/images/vpc-lb.png diff --git a/docs/en-US/add-loadbalancer-rule-vpc.xml b/docs/en-US/add-loadbalancer-rule-vpc.xml index bba3e5ad134..4c1ac1e5108 100644 --- a/docs/en-US/add-loadbalancer-rule-vpc.xml +++ b/docs/en-US/add-loadbalancer-rule-vpc.xml @@ -21,103 +21,311 @@ -->
    Adding Load Balancing Rules on a VPC - A &PRODUCT; user or administrator may create load balancing rules that balance traffic - received at a public IP to one or more VMs that belong to a network tier that provides load - balancing service in a VPC. A user creates a rule, specifies an algorithm, and assigns the rule - to a set of VMs within a VPC. - - - Log in to the &PRODUCT; UI as an administrator or end user. - - - In the left navigation, choose Network. - - - In the Select view, select VPC. - All the VPCs that you have created for the account is listed in the page. - - - Click the Configure button of the VPC to which you want to configure load balancing - rules. - The VPC page is displayed where all the tiers you created are listed in a - diagram. - - - Click the Settings icon. - The following options are displayed. - + In a VPC, you can configure two types of load balancing—external LB and internal LB. + External LB is nothing but load balancing the traffic received at a public IP of the VPC virtual + router. The traffic is load balanced within a tier based on your configuration. Supported + service providers are Citrix NetScaler and VPC virtual router. When you use internal LB service, + traffic received at a tier is load balanced across different tiers within the VPC. External load + balancing devices are not supported for internal LB. +
    + Load Balancing Within a Tier (External LB) + A &PRODUCT; user or administrator may create load balancing rules that balance traffic + received at a public IP to one or more VMs that belong to a network tier that provides load + balancing service in a VPC. A user creates a rule, specifies an algorithm, and assigns the + rule to a set of VMs within a VPC. + + + Log in to the &PRODUCT; UI as an administrator or end user. + + + In the left navigation, choose Network. + + + In the Select view, select VPC. + All the VPCs that you have created for the account is listed in the page. + + + Click the Configure button of the VPC, for which you want to configure load balancing + rules. + The VPC page is displayed where all the tiers you created listed in a diagram. + + + Click the Settings icon. + For each tier, the following options are displayed: + + + Internal LB + + + Public LB IP + + + Static NAT + + + Virtual Machines + + + CIDR + + + The following router information is displayed: + + + Private Gateways + + + Public IP Addresses + + + Site-to-Site VPNs + + + Network ACL Lists + + + + + In the Router node, select Public IP Addresses. + The IP Addresses page is displayed. + + + Click the IP address for which you want to create the rule, then click the + Configuration tab. + + + In the Load Balancing node of the diagram, click View All. + + + Select the tier to which you want to apply the rule. + + + Specify the following: + + + Name: A name for the load balancer rule. + + + Public Port: The port that receives the incoming + traffic to be balanced. + + + Private Port: The port that the VMs will use to + receive the traffic. + + + Algorithm. Choose the load balancing algorithm + you want &PRODUCT; to use. &PRODUCT; supports the following well-known + algorithms: + + + Round-robin + + + Least connections + + + Source + + + + + Stickiness. (Optional) Click Configure and choose + the algorithm for the stickiness policy. See Sticky Session Policies for Load Balancer + Rules. + + + Add VMs: Click Add VMs, then select two or more + VMs that will divide the load of incoming traffic, and click Apply. + + + + + The new load balancing rule appears in the list. You can repeat these steps to add more + load balancing rules for this IP address. +
    +
    + Load Balancing Across Tiers + &PRODUCT; supports sharing workload across different tiers within your VPC. Assume that + multiple tiers are set up in your environment, such as Web tier and Application tier. Traffic + to each tier is balanced on the VPC virtual router on the public side, as explained in . If you want the traffic coming from the Web tier to + the Application tier to be balanced, use the internal load balancing feature offered by + &PRODUCT;. +
    + How Does Internal LB Work in VPC? + In this figure, a public LB rule is created for the public IP 72.52.125.10 with public + port 80 and private port 81. The LB rule, created on the VPC virtual router, is applied on + the traffic coming from the Internet to the VMs on the Web tier. On the Application tier two + internal load balancing rules are created. An internal LB rule for the guest IP 10.10.10.4 + with load balancer port 23 and instance port 25 is configured on the VM, InternalLBVM1. + Another internal LB rule for the guest IP 10.10.10.4 with load balancer port 45 and instance + port 46 is configured on the VM, InternalLBVM1. Another internal LB rule for the guest IP + 10.10.10.6, with load balancer port 23 and instance port 25 is configured on the VM, + InternalLBVM2. + + + + + + vpc-lb.png: Configuring internal LB for VPC + + +
    +
    + Enabling Internal LB on a VPC Tier + - IP Addresses + Create a network offering, as given in . - Gateways + Create an internal load balancing rule and apply, as given in . + + +
    +
    + Creating a Network Offering for Internal LB + To have internal LB support on VPC, create a network offering as follows: + + + Log in to the &PRODUCT; UI as a user or admin. - Site-to-Site VPN + From the Select Offering drop-down, choose Network Offering. - Network ACLs - - - - - Select IP Addresses. - The IP Addresses page is displayed. - - - Click the IP address for which you want to create the rule, then click the Configuration - tab. - - - In the Load Balancing node of the diagram, click View All. - - - Select the tier to which you want to apply the rule. - - In a VPC, the load balancing service is supported only on a single tier. - - - - Specify the following: - - - Name: A name for the load balancer rule. + Click Add Network Offering. - Public Port: The port that receives the incoming - traffic to be balanced. - - - Private Port: The port that the VMs will use to - receive the traffic. - - - Algorithm. Choose the load balancing algorithm you - want &PRODUCT; to use. &PRODUCT; supports the following well-known algorithms: + In the dialog, make the following choices: - Round-robin + Name: Any desired name for the network + offering. - Least connections + Description: A short description of the + offering that can be displayed to users. - Source + Network Rate: Allowed data transfer rate in MB + per second. + + + Traffic Type: The type of network traffic that + will be carried on the network. + + + Guest Type: Choose whether the guest network is + isolated or shared. + + + Persistent: Indicate whether the guest network + is persistent or not. The network that you can provision without having to deploy a + VM on it is termed persistent network. + + + VPC: This option indicate whether the guest + network is Virtual Private Cloud-enabled. A Virtual Private Cloud (VPC) is a + private, isolated part of &PRODUCT;. A VPC can have its own virtual network topology + that resembles a traditional physical network. For more information on VPCs, see + . + + + Specify VLAN: (Isolated guest networks only) + Indicate whether a VLAN should be specified when this offering is used. + + + Supported Services: Select Load Balancer. + Select InternalLbVM from the provider list. + + + Load Balancer Type: Select Internal LB from the + drop-down. + + + System Offering: Choose the system service + offering that you want virtual routers to use in this network. + + + Conserve mode: Indicate whether to use conserve + mode. In this mode, network resources are allocated only when the first virtual + machine starts in the network. - Stickiness. (Optional) Click Configure and choose - the algorithm for the stickiness policy. See Sticky Session Policies for Load Balancer - Rules. + Click OK and the network offering is created. + + +
    +
    + Creating an Internal LB Rule + + + Log in to the &PRODUCT; UI as an administrator or end user. - Add VMs: Click Add VMs, then select two or more VMs - that will divide the load of incoming traffic, and click Apply. + In the left navigation, choose Network. - - - - The new load balancing rule appears in the list. You can repeat these steps to add more load - balancing rules for this IP address. -
    \ No newline at end of file + + In the Select view, select VPC. + All the VPCs that you have created for the account is listed in the page. + + + Locate the VPC for which you want to configure internal LB, then click + Configure. + The VPC page is displayed where all the tiers you created listed in a + diagram. + + + Locate the Tier for which you want to configure an internal LB rule, click Internal + LB. + In the Internal LB page, click Add Internal LB. + + + In the dialog, specify the following: + + + Name: A name for the load balancer rule. + + + Description: A short description of the rule + that can be displayed to users. + + + Source IP Address: The source IP from which + traffic originates. Typically, this is the IP of an instance on another tier within + your VPC. + + + Source Port: The port associated with the + source IP. Traffic on this port is load balanced. + + + Instance Port: The port of the internal LB + VM. + + + Algorithm. Choose the load balancing algorithm + you want &PRODUCT; to use. &PRODUCT; supports the following well-known + algorithms: + + + Round-robin + + + Least connections + + + Source + + + + + + +
    +
    + diff --git a/docs/en-US/images/vpc-lb.png b/docs/en-US/images/vpc-lb.png new file mode 100644 index 0000000000000000000000000000000000000000..4269e8b9f9e9307000609d74aa17599c20858f66 GIT binary patch literal 181811 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 z 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

    Zi}2#ae37C!(W7e)?iOGXt-D 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

    BX7&n+I&8^!XU#t~~n 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 Date: Thu, 30 May 2013 16:33:48 +0530 Subject: [PATCH 200/412] more additions on internal lb-vpc --- docs/en-US/add-loadbalancer-rule-vpc.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/en-US/add-loadbalancer-rule-vpc.xml b/docs/en-US/add-loadbalancer-rule-vpc.xml index 4c1ac1e5108..02e701357bd 100644 --- a/docs/en-US/add-loadbalancer-rule-vpc.xml +++ b/docs/en-US/add-loadbalancer-rule-vpc.xml @@ -22,11 +22,13 @@

    Adding Load Balancing Rules on a VPC In a VPC, you can configure two types of load balancing—external LB and internal LB. - External LB is nothing but load balancing the traffic received at a public IP of the VPC virtual - router. The traffic is load balanced within a tier based on your configuration. Supported - service providers are Citrix NetScaler and VPC virtual router. When you use internal LB service, - traffic received at a tier is load balanced across different tiers within the VPC. External load - balancing devices are not supported for internal LB. + External LB is nothing but a LB rule created to redirect the traffic received at a public IP of + the VPC virtual router. The traffic is load balanced within a tier based on your configuration. + Citrix NetScaler and VPC virtual router are supported for external LB. When you use internal LB + service, traffic received at a tier is load balanced across different tiers within the VPC. For + example, traffic reached at Web tier is redirected to Application tier. External load balancing + devices are not supported for internal LB. The service is provided by a internal LB VM + configured on the target tier.
    Load Balancing Within a Tier (External LB) A &PRODUCT; user or administrator may create load balancing rules that balance traffic From 3115ddf007ae2eea8b3bd2981638819c29dde549 Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Thu, 30 May 2013 16:34:49 +0530 Subject: [PATCH 201/412] Added networkAclId to listNetworkResponse. Log ACL provider while applying network ACLs --- .../cloudstack/api/response/NetworkResponse.java | 12 ++++++++++++ server/src/com/cloud/api/ApiResponseHelper.java | 7 +++++++ .../com/cloud/network/vpc/NetworkACLManagerImpl.java | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java index d6847d55846..70c3d79c4c0 100644 --- a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java @@ -166,6 +166,10 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes @SerializedName(ApiConstants.DISPLAY_NETWORK) @Param(description="an optional field, whether to the display the network to the end user or not.") private Boolean displayNetwork; + @SerializedName(ApiConstants.ACL_ID) @Param(description="ACL Id associated with the VPC network") + private String aclId; + + public Boolean getDisplayNetwork() { return displayNetwork; } @@ -352,4 +356,12 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes public void setIp6Cidr(String ip6Cidr) { this.ip6Cidr = ip6Cidr; } + + public String getAclId() { + return aclId; + } + + public void setAclId(String aclId) { + this.aclId = aclId; + } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 89739cfde22..cf79ff89296 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -2489,6 +2489,13 @@ public class ApiResponseHelper implements ResponseGenerator { } response.setTags(tagResponses); + if(network.getNetworkACLId() != null){ + NetworkACL acl = ApiDBUtils.findByNetworkACLId(network.getNetworkACLId()); + if(acl != null){ + response.setAclId(acl.getUuid()); + } + } + response.setObjectName("network"); return response; } diff --git a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java index e26dad98f60..cef6454ab5b 100644 --- a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java +++ b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java @@ -376,16 +376,22 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana public boolean applyACLItemsToNetwork(long networkId, List rules) throws ResourceUnavailableException { Network network = _networkDao.findById(networkId); boolean handled = false; + boolean foundProvider = false; for (NetworkACLServiceProvider element: _networkAclElements) { Network.Provider provider = element.getProvider(); boolean isAclProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.NetworkACL, provider); if (!isAclProvider) { continue; } + foundProvider = true; + s_logger.debug("Applying NetworkACL for network: "+network.getId()+" with Network ACL service provider"); handled = element.applyNetworkACLs(network, rules); if (handled) break; } + if(!foundProvider){ + s_logger.debug("Unable to find NetworkACL service provider for network: "+network.getId()); + } return handled; } From fd79f6b6020ae653c4f2d1f6222685becb792b77 Mon Sep 17 00:00:00 2001 From: Koushik Das Date: Thu, 30 May 2013 17:20:44 +0530 Subject: [PATCH 202/412] CLOUDSTACK-2276: NPE while attaching the volume to the instance which is created from ROOT Disk Snapshot The NPE comes as the storage pool id is set to null in the in-memory volume object which is not in sync with the db. entry. Reloading the volume from db. fixes the issue --- server/src/com/cloud/storage/VolumeManagerImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index aedb68e1316..c20625257fe 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -1725,6 +1725,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } } + // reload the volume from db + volumeOnPrimaryStorage = volFactory.getVolume(volumeOnPrimaryStorage.getId()); boolean moveVolumeNeeded = needMoveVolume(rootVolumeOfVm, volumeOnPrimaryStorage); if (moveVolumeNeeded) { From 3000cb31af91437fae8ab64a05b1564730a6b80d Mon Sep 17 00:00:00 2001 From: Abhinandan Prateek Date: Thu, 30 May 2013 17:35:30 +0530 Subject: [PATCH 203/412] CLOUDSTACK-2434: Enable irq balance on system VM --- tools/appliance/definitions/systemvmtemplate/postinstall.sh | 3 +++ tools/appliance/definitions/systemvmtemplate64/postinstall.sh | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index f532f88537c..7c067962700 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -42,6 +42,9 @@ install_packages() { apt-get --no-install-recommends -q -y --force-yes install dnsmasq dnsmasq-utils # nfs client apt-get --no-install-recommends -q -y --force-yes install nfs-common + # nfs irqbalance + apt-get --no-install-recommends -q -y --force-yes install irqbalance + # vpn stuff apt-get --no-install-recommends -q -y --force-yes install xl2tpd bcrelay ppp ipsec-tools tdb-tools diff --git a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh index 3ccf3cefdef..35a4e4ac72d 100644 --- a/tools/appliance/definitions/systemvmtemplate64/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate64/postinstall.sh @@ -42,6 +42,8 @@ install_packages() { apt-get --no-install-recommends -q -y --force-yes install dnsmasq dnsmasq-utils # nfs client apt-get --no-install-recommends -q -y --force-yes install nfs-common + # nfs irqbalance + apt-get --no-install-recommends -q -y --force-yes install irqbalance # vpn stuff apt-get --no-install-recommends -q -y --force-yes install xl2tpd bcrelay ppp ipsec-tools tdb-tools From dd9b75020cf0bb0dcbabcb95775160c0c2c83f94 Mon Sep 17 00:00:00 2001 From: radhikap Date: Thu, 30 May 2013 18:24:10 +0530 Subject: [PATCH 204/412] CLOUDSTACK-2764 --- docs/en-US/add-loadbalancer-rule-vpc.xml | 3 - docs/en-US/configure-acl.xml | 102 ++++++++++++----------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/docs/en-US/add-loadbalancer-rule-vpc.xml b/docs/en-US/add-loadbalancer-rule-vpc.xml index 02e701357bd..b7b9e3e7613 100644 --- a/docs/en-US/add-loadbalancer-rule-vpc.xml +++ b/docs/en-US/add-loadbalancer-rule-vpc.xml @@ -50,9 +50,6 @@ Click the Configure button of the VPC, for which you want to configure load balancing rules. The VPC page is displayed where all the tiers you created listed in a diagram. - - - Click the Settings icon. For each tier, the following options are displayed: diff --git a/docs/en-US/configure-acl.xml b/docs/en-US/configure-acl.xml index 299196c5502..e7459e68dbf 100644 --- a/docs/en-US/configure-acl.xml +++ b/docs/en-US/configure-acl.xml @@ -37,31 +37,66 @@ All the VPCs that you have created for the account is listed in the page. - Click the Settings icon. - The following options are displayed. + Click the Configure button of the VPC, for which you want to configure load balancing + rules. + For each tier, the following options are displayed: - IP Addresses + Internal LB - Gateways + Public LB IP - Site-to-Site VPN + Static NAT - Network ACLs + Virtual Machines + + + CIDR + + + The following router information is displayed: + + + Private Gateways + + + Public IP Addresses + + + Site-to-Site VPNs + + + Network ACL Lists - Select Network ACLs. - The Network ACLs page is displayed. + Select Network ACL Lists. + The following default rules are displayed in the Network ACLs page: default_allow, + default_deny. - Click Add Network ACLs. + Click Add ACL Lists, and specify the following: + + + ACL List Name: A name for the ACL list. + + + Description: A short description of the ACL list + that can be displayed to users. + + + + + Select the ACL list. + + + Select the ACL List Rules tab. To add an ACL rule, fill in the following fields to specify what kind of network traffic - is allowed in this tier. + is allowed in the VPC. CIDR: The CIDR acts as the Source CIDR for the @@ -74,7 +109,8 @@ Protocol: The networking protocol that sources use to send traffic to the tier. The TCP and UDP protocols are typically used for data exchange and end-user communications. The ICMP protocol is typically used to send error - messages or network monitoring data. + messages or network monitoring data. All supports all the traffic. Other option is + Protocol Number. Start Port, End @@ -83,8 +119,10 @@ fields. - Select Tier: Select the tier for which you want to - add this ACL rule. + Protocol Number: The protocol number associated + with IPv4 or IPv6. For more information, see Protocol + Numbers. ICMP Type, ICMP @@ -92,48 +130,14 @@ sent. - Traffic Type: Select the traffic type you want to - apply. - - - Egress: To add an egress rule, select Egress - from the Traffic type drop-down box and click Add. This specifies what type of - traffic is allowed to be sent out of VM instances in this tier. If no egress rules - are specified, all traffic from the tier is allowed out at the VPC virtual router. - Once egress rules are specified, only the traffic specified in egress rules and the - responses to any traffic that has been allowed in through an ingress rule are - allowed out. No egress rule is required for the VMs in a tier to communicate with - each other. - - - Ingress: To add an ingress rule, select Ingress - from the Traffic type drop-down box and click Add. This specifies what network - traffic is allowed into the VM instances in this tier. If no ingress rules are - specified, then no traffic will be allowed in, except for responses to any traffic - that has been allowed out through an egress rule. - - - - By default, all incoming and outgoing traffic to the guest networks is blocked. To - open the ports, create a new network ACL. - + Action: What action to be taken. Click Add. The ACL rule is added. - To view the list of ACL rules you have added, click the desired tier from the Network - ACLs page, then select the Network ACL tab. - - - - - - network-acl.png: adding, editing, deleting an ACL rule. - - You can edit the tags assigned to the ACL rules and delete the ACL rules you have - created. Click the appropriate button in the Actions column. + created. Click the appropriate button in the Details tab.
    From 4d5033f263c86694af4029b2a1401c035af490a6 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 6 May 2013 10:31:13 +0530 Subject: [PATCH 205/412] Cluster/Pod/Host Explicit Dedication front end UI --- ui/scripts/docs.js | 16 +++++ ui/scripts/system.js | 144 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index 65233c10dda..48db443f7a8 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -16,6 +16,22 @@ // under the License. cloudStack.docs = { + //Dedicate Resource + + helpDedicateResource:{ + + desc:'Check this box to dedicate the resources to specific domain/account', + externalLink:'' + + }, + + helpAccountForDedication:{ + + desc:'Please enter an account name which belongs to the above selected domain in order to dedicate this resource to this account', + externalLink:'' + + }, + //Delete/archive events helpEventsDeleteType:{ diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8b9a81fe7c7..b90a11d2fe4 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8507,7 +8507,54 @@ label: 'label.end.reserved.system.IP', docID: 'helpPodEndIP', validation: { required: false } + }, + + isDedicated:{ + label:'Dedicate', + isBoolean:true, + isChecked:false, + docID:'helpDedicateResource' + + + }, + + domainId:{ + label:'Domain', + isHidden:true, + validation:{required:true}, + dependsOn:'isDedicated', + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + } + }, + + accountId:{ + label:'Account', + isHidden:true, + dependsOn:'isDedicated', + docID:'helpAccountForDedication', + validation:{required:false} + } + } }, @@ -9007,8 +9054,55 @@ }, - //hypervisor==VMWare begins here + + isDedicated:{ + label:'Dedicate', + isBoolean:true, + isChecked:false, + docID:'helpDedicateResource' + + }, + + domainId:{ + label:'Domain', + isHidden:true, + validation:{required:true}, + dependsOn:'isDedicated', + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + } + }, + + accountId:{ + label:'Account', + isHidden:true, + dependsOn:'isDedicated', + docID:'helpAccountForDedication', + validation:{required:false} + + }, + + //hypervisor==VMWare begins here + vCenterHost: { label: 'label.vcenter.host', docID: 'helpClustervCenterHost', @@ -9996,7 +10090,55 @@ validation: { required: true }, isHidden: true, isPassword: true + }, + + isDedicated:{ + label:'Dedicate', + isBoolean:true, + isChecked:false, + docID:'helpDedicateResource' + + + }, + + domainId:{ + label:'Domain', + isHidden:true, + validation:{required:true}, + dependsOn:'isDedicated', + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + + } + }, + + accountId:{ + label:'Account', + isHidden:true, + dependsOn:'isDedicated', + docID:'helpAccountForDedication', + validation:{required:false} + }, + //input_group="general" ends here //input_group="VMWare" starts here From 25643459af07e194ab3f6d136dfa05207007bbbf Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 14 May 2013 16:37:54 +0530 Subject: [PATCH 206/412] Dedicate POD -UI and API integration code --- ui/scripts/system.js | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index b90a11d2fe4..fc0c70f5354 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8578,7 +8578,7 @@ dataType: "json", success: function(json) { var item = json.createpodresponse.pod; - args.response.success({ + /* args.response.success({ data:item }); }, @@ -8586,7 +8586,41 @@ var errorMsg = parseXMLHttpResponse(XMLHttpResponse); args.response.error(errorMsg); } - }); + });*/ + + //EXPLICIT DEDICATION + if(args.$form.find('.form-item[rel=isDedicated]').find('input[type=checkbox]').is(':Checked')== true){ + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + + if(podId != null){ + $.ajax({ + url:createURL("dedicatePod&podId=" +podId +"&domainId=" +args.data.domainId + array2.join("")), + dataType:"json", + success:function(json){ + var dedicatedObj = json.dedicatepodresponse.host; + args.response.success({data:item}); + + }, + + error:function(json){ + + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + + } + } + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + + }); + + }, notification: { From aab0bb4d31a7ee602ea7d2b30f58ffb5a569f534 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 14 May 2013 16:46:02 +0530 Subject: [PATCH 207/412] Dedicate POD -UI and API integration code --- ui/scripts/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index fc0c70f5354..1b1f61d61bd 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8600,7 +8600,7 @@ dataType:"json", success:function(json){ var dedicatedObj = json.dedicatepodresponse.host; - args.response.success({data:item}); + args.response.success({ data: $.extend(item, dedicatedObj)}); }, From 97cb514f052ea8abf75e1073cb84238fa0e164be Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 15 May 2013 01:19:21 +0530 Subject: [PATCH 208/412] Pod dedication action button functionality --- ui/scripts/system.js | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 1b1f61d61bd..b0bc9a10d79 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8714,6 +8714,71 @@ } }, + dedicate:{ + label: 'Dedicate Pod', + messages: { + confirm: function(args) { + return 'Do you really want to dedicate this pod to a domain/account? '; + }, + notification: function(args) { + return 'Pod Dedicated'; + } + }, + createForm:{ + title:'Dedicate Pod', + fields:{ + domainId:{ + label:'Domain', + validation:{required:true}, + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + } + }, + + accountId:{ + label:'Account', + // docID:'helpAccountForDedication', + validation:{required:false} + + } + + + } + }, + action: function(args) { + $.ajax({ + url: createURL("dedicatePod&podId=" + args.context.pods[0].id), + dataType: "json", + success: function(json) { + var item = json.dedicatepodresponse.pod; + args.response.success({ + actionFilter: podActionfilter, + data:item + }); + } + }); + } + + }, + disable: { label: 'label.action.disable.pod', messages: { @@ -12557,6 +12622,7 @@ var podObj = args.context.item; var allowedActions = []; allowedActions.push("edit"); + allowedActions.push("dedicate"); if(podObj.allocationstate == "Disabled") allowedActions.push("enable"); else if(podObj.allocationstate == "Enabled") From 60ff005939fa81d9820ffaab0f58126a33af67e2 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 15 May 2013 10:48:43 +0530 Subject: [PATCH 209/412] Explicit Dedication - Host UI/API Integration code --- ui/scripts/system.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index b0bc9a10d79..8133509e3ba 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8599,7 +8599,7 @@ url:createURL("dedicatePod&podId=" +podId +"&domainId=" +args.data.domainId + array2.join("")), dataType:"json", success:function(json){ - var dedicatedObj = json.dedicatepodresponse.host; + var dedicatedObj = json.dedicatepodresponse.pod; args.response.success({ data: $.extend(item, dedicatedObj)}); }, @@ -10354,21 +10354,48 @@ }); } } - + + var hostId = null; $.ajax({ url: createURL("addHost"), type: "POST", data: data, success: function(json) { var item = json.addhostresponse.host[0]; - args.response.success({ + hostId = json.addhostresponse.host[0].id; + + /* args.response.success({ data: item }); }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); args.response.error(errorMsg); - } + }*/ + + //EXPLICIT DEDICATION + if(args.$form.find('.form-item[rel=isDedicated]').find('input[type=checkbox]').is(':Checked')== true){ + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + } + + if(hostId != null){ + $.ajax({ + url:createURL("dedicateHost&hostId=" +hostId +"&domainId=" +args.data.domainId + array2.join("")), + dataType:"json", + success:function(json){ + var dedicatedObj = json.dedicatehostresponse.host; + args.response.success({ data: $.extend(item, dedicatedObj) }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + } + } }); }, From c5493f0778d71d8c8335712b0e383a074f91fbef Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 15 May 2013 11:19:18 +0530 Subject: [PATCH 210/412] Explicit Dedication - Cluster UI/API Integration code --- ui/scripts/system.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8133509e3ba..f76b534d210 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9430,21 +9430,47 @@ clusterName = hostname + "/" + dcName + "/" + clusterName; //override clusterName } array1.push("&clustername=" + todb(clusterName)); - + var clusterId = null; $.ajax({ url: createURL("addCluster" + array1.join("")), dataType: "json", async: true, success: function(json) { var item = json.addclusterresponse.cluster[0]; - args.response.success({ + clusterId= json.addclusterresponse.cluster[0].id; + /* args.response.success({ data: $.extend(item, { state: 'Enabled' }) }); }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); args.response.error(errorMsg); - } + }*/ + + //EXPLICIT DEDICATION + if(args.$form.find('.form-item[rel=isDedicated]').find('input[type=checkbox]').is(':Checked')== true){ + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + } + + if(hostId != null){ + $.ajax({ + url:createURL("dedicateCluster&clusterId=" +clusterId +"&domainId=" +args.data.domainId + array2.join("")), + dataType:"json", + success:function(json){ + var dedicatedObj = json.dedicateclusterresponse.cluster; + args.response.success({ data: $.extend(item, dedicatedObj , {state:'Enabled'}) }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + } + } + }); }, From 5c18ba648be20b5446c141a2de1e3c66bcf9eb77 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 15 May 2013 18:19:05 +0530 Subject: [PATCH 211/412] Zone Dedication UI-API Integration Code --- ui/scripts/zoneWizard.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index a2998b4a9a5..917f9ca5f04 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -570,6 +570,16 @@ }); } }, + + accountId:{ + label:'Account', + isHidden:true, + dependsOn:'ispublic', + //docID:'helpAccountForDedication', + validation:{required:false} + + }, + localstorageenabled: { label: 'label.local.storage.enabled', isBoolean: true, @@ -1593,6 +1603,8 @@ if(args.data.zone.networkdomain != null && args.data.zone.networkdomain.length > 0) array1.push("&domain=" + todb(args.data.zone.networkdomain)); + + var dedicatedZoneid = null; $.ajax({ url: createURL("createZone" + array1.join("")), @@ -1604,6 +1616,33 @@ returnedZone: json.createzoneresponse.zone }) }); + + dedicatedZoneId = json.createzoneresponse.zone.id; + //EXPLICIT ZONE DEDICATION + if(args.data.pluginFrom == null && args.data.zone.ispublic == null){ + var array2 = []; + if(args.data.zone.accountId != "") + array2.push("&accountId=" +todb(args.data.zone.accountId)); + + if(dedicatedZoneId != null){ + $.ajax({ + url:createURL("dedicateZone&ZoneId=" +ZoneId +"&domain=" +args.data.zone.domain + array2.join("")), + dataType:"json", + success:function(json){ + var dedicatedObj = json.dedicatezoneresponse.zone; + //args.response.success({ data: $.extend(item, dedicatedObj)}); + + }, + + error:function(json){ + + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + + } + } + }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); From 1553ec65808656e261df1bfc382f52cf09c7a7b4 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Fri, 24 May 2013 17:35:48 +0530 Subject: [PATCH 212/412] explicit dedication API-UI integration --- ui/scripts/system.js | 21 ++++++++++++++++----- ui/scripts/zoneWizard.js | 6 +++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index f76b534d210..f85e20f229d 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8571,13 +8571,15 @@ var endip = args.data.reservedSystemEndIp; //optional if (endip != null && endip.length > 0) array1.push("&endIp=" + todb(endip)); - + var podId = null; $.ajax({ url: createURL("createPod" + array1.join("")), data: appendData, dataType: "json", success: function(json) { var item = json.createpodresponse.pod; + podId = json.createpodresponse.pod.id; + /* args.response.success({ data:item }); @@ -8599,8 +8601,17 @@ url:createURL("dedicatePod&podId=" +podId +"&domainId=" +args.data.domainId + array2.join("")), dataType:"json", success:function(json){ - var dedicatedObj = json.dedicatepodresponse.pod; - args.response.success({ data: $.extend(item, dedicatedObj)}); + var jid = json.dedicatepodresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + + data:item + }); }, @@ -9454,12 +9465,12 @@ array2.push("&accountId=" +todb(args.data.accountId)); } - if(hostId != null){ + if(clusterId != null){ $.ajax({ url:createURL("dedicateCluster&clusterId=" +clusterId +"&domainId=" +args.data.domainId + array2.join("")), dataType:"json", success:function(json){ - var dedicatedObj = json.dedicateclusterresponse.cluster; + var jid = json.dedicateclusterresponse.jobid; args.response.success({ data: $.extend(item, dedicatedObj , {state:'Enabled'}) }); }, diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 917f9ca5f04..5a8bcfdaae8 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -1604,7 +1604,7 @@ if(args.data.zone.networkdomain != null && args.data.zone.networkdomain.length > 0) array1.push("&domain=" + todb(args.data.zone.networkdomain)); - var dedicatedZoneid = null; + var dedicatedZoneId = null; $.ajax({ url: createURL("createZone" + array1.join("")), @@ -1626,10 +1626,10 @@ if(dedicatedZoneId != null){ $.ajax({ - url:createURL("dedicateZone&ZoneId=" +ZoneId +"&domain=" +args.data.zone.domain + array2.join("")), + url:createURL("dedicateZone&ZoneId=" +dedicatedZoneId +"&domain=" +args.data.zone.domain + array2.join("")), dataType:"json", success:function(json){ - var dedicatedObj = json.dedicatezoneresponse.zone; + var dedicatedObj = json.dedicatezoneresponse.jobid; //args.response.success({ data: $.extend(item, dedicatedObj)}); }, From a52b3139c525afd5d8c13cef4129e5dea03d2747 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Sat, 25 May 2013 02:15:44 +0530 Subject: [PATCH 213/412] Pod explicit dedication Detail View --- ui/scripts/system.js | 58 +++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index f85e20f229d..70fb0e7a7fb 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8866,23 +8866,53 @@ }, label: 'label.allocation.state' } - } + }, + + { + + isdedicated:{label:'Dedicated'}, + domainid:{label:'Domain ID'} + + } + ], dataProvider: function(args) { - $.ajax({ - url: createURL("listPods&id=" + args.context.pods[0].id), - dataType: "json", - async: true, - success: function(json) { - var item = json.listpodsresponse.pod[0]; - args.response.success({ - actionFilter: podActionfilter, - data:item - }); - } - }); - } + + $.ajax({ + url: createURL("listPods&id=" + args.context.pods[0].id), + dataType: "json", + async: false, + success: function(json) { + var item = json.listpodsresponse.pod[0]; + + + $.ajax({ + url:createURL("listDedicatedPods&podid=" +args.context.pods[0].id), + dataType:"json", + async:false, + success:function(json){ + var podItem = json.listdedicatedpodsresponse ? json.listdedicatedpodsresponse.dedicatedpod[0]:[]; + if (podItem.domainid != null) { + $.extend(item, podItem , { isdedicated: 'Yes' }); + } + }, + error:function(json){ + $.extend(item ,{ isdedicated: 'No' }) + + + } + }); + args.response.success({ + actionFilter: podActionfilter, + data: item + }); + + } + }); + + + } }, ipAllocations: { From 9f76c0559c761e33bec98043cd573afc88473e1e Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 27 May 2013 15:44:02 +0530 Subject: [PATCH 214/412] Explicit Dedication - Cluster UI-API Integration code --- ui/scripts/system.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 70fb0e7a7fb..45b0f43baad 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9501,7 +9501,17 @@ dataType:"json", success:function(json){ var jid = json.dedicateclusterresponse.jobid; - args.response.success({ data: $.extend(item, dedicatedObj , {state:'Enabled'}) }); + //args.response.success({ data: $.extend(item, dedicatedObj , {state:'Enabled'}) }); + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + + data:$.extend(item, {state:'Enabled'}) + }); }, From 6aa2268efb9bc1f45a932c2a8a11b65f3e3215f7 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 27 May 2013 16:05:03 +0530 Subject: [PATCH 215/412] Explicit Dedication - Host UI-API Integration code --- ui/scripts/system.js | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 45b0f43baad..e39171a371b 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9520,7 +9520,12 @@ } }); } - } + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); }, @@ -10441,14 +10446,6 @@ var item = json.addhostresponse.host[0]; hostId = json.addhostresponse.host[0].id; - /* args.response.success({ - data: item - }); - }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - args.response.error(errorMsg); - }*/ //EXPLICIT DEDICATION if(args.$form.find('.form-item[rel=isDedicated]').find('input[type=checkbox]').is(':Checked')== true){ @@ -10462,8 +10459,19 @@ url:createURL("dedicateHost&hostId=" +hostId +"&domainId=" +args.data.domainId + array2.join("")), dataType:"json", success:function(json){ - var dedicatedObj = json.dedicatehostresponse.host; - args.response.success({ data: $.extend(item, dedicatedObj) }); + var jid = json.dedicatehostresponse.host.jobid; + //args.response.success({ data: $.extend(item, dedicatedObj) }); + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + + data:item + + }); }, @@ -10472,7 +10480,13 @@ } }); } - } + }, + + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); }, From e95ddb656231cd6825a241a85aca3eca6e614c21 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 27 May 2013 16:15:49 +0530 Subject: [PATCH 216/412] Explicit Dedication - Cluster detail View UI-API Integration code --- ui/scripts/system.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index e39171a371b..1ee2ab2c93c 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9769,7 +9769,13 @@ //allocationstate: { label: 'label.allocation.state' }, //managedstate: { label: 'Managed State' }, state: { label: 'label.state' } - } + }, + + { + isdedicated:{label:'Dedicated'}, + domainid:{label:'Domain ID'} + } + ], dataProvider: function(args) { $.ajax({ From 55d317633273a5a66e5d08e7f7ac0da7f3cb38f6 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 15:39:48 +0530 Subject: [PATCH 217/412] explicit dedication - POD UI-API detail view integration --- ui/scripts/system.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 1ee2ab2c93c..815ed999582 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8892,14 +8892,17 @@ dataType:"json", async:false, success:function(json){ - var podItem = json.listdedicatedpodsresponse ? json.listdedicatedpodsresponse.dedicatedpod[0]:[]; + if(json.listdedicatedpodsresponse.dedicatedpod != undefined){ + var podItem = json.listdedicatedpodsresponse.dedicatedpod[0]; if (podItem.domainid != null) { $.extend(item, podItem , { isdedicated: 'Yes' }); - } + } + } + else + $.extend(item ,{ isdedicated: 'No' }) }, error:function(json){ - $.extend(item ,{ isdedicated: 'No' }) - + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); } }); From fd72c129674375e911df018ee7e1e0ba99b73586 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 15:52:53 +0530 Subject: [PATCH 218/412] explicit dedication - dedicatePod API call action item integration --- ui/scripts/system.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 815ed999582..c68def0f32a 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8775,16 +8775,31 @@ } }, action: function(args) { + + //EXPLICIT DEDICATION + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + $.ajax({ - url: createURL("dedicatePod&podId=" + args.context.pods[0].id), + url: createURL("dedicatePod&podId=" + args.context.pods[0].id + "&domainId=" +args.data.domainId + array2.join("")), dataType: "json", success: function(json) { - var item = json.dedicatepodresponse.pod; - args.response.success({ - actionFilter: podActionfilter, - data:item - }); - } + var jid = json.dedicatepodresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:podActionfilter + }); + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + + } }); } From 1c9a34abccdbd14d00b5fe6f4bb64f6fed1ef2d1 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 16:00:24 +0530 Subject: [PATCH 219/412] explicit Dedication - Pod action filter for release/dedicate action items --- ui/scripts/system.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index c68def0f32a..933f271c729 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -12763,8 +12763,21 @@ var podActionfilter = function(args) { var podObj = args.context.item; var allowedActions = []; + + $.ajax({ + url:createURL("listDedicatedPods&podId=" + args.context.pods[0].id), + success:function(json){ + if(json.listdedicatedpodsresponse.dedicatedpod != undefined){ + var dedicatedPodObj = json.listdedicatedpodsresponse.dedicatedpod[0]; + if(dedicatedPodObj.domainid != null) + allowedActions.push("release"); + } + else + allowedActions.push("dedicate"); + } + }); + allowedActions.push("edit"); - allowedActions.push("dedicate"); if(podObj.allocationstate == "Disabled") allowedActions.push("enable"); else if(podObj.allocationstate == "Enabled") From cee82eca5581676539ff8e250159cb7ed06ff040 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 16:52:43 +0530 Subject: [PATCH 220/412] explicit Dedication - Pod action filter for releasing dedicated pod (API Call) --- ui/css/cloudstack3.css | 6 +++-- ui/scripts/system.js | 51 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index da647877f27..df433d96288 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11725,11 +11725,13 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it background-position: 0px -613px; } -.restart .icon { +.restart .icon, +.release .icon { background-position: 0px -63px; } -.restart:hover .icon { +.restart:hover .icon, +.release:hover .icon { background-position: 0px -645px; } diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 933f271c729..7508e948bbd 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8805,6 +8805,43 @@ }, + release:{ + label:'Release Dedicated Pod', + messages:{ + confirm: function(args) { + return 'Do you want to release this dedicated pod ?'; + }, + notification: function(args) { + return 'Pod dedication released'; + } + }, + action:function(args){ + $.ajax({ + url:createURL("releaseDedicatedPod&podid=" + args.context.pods[0].id), + dataType:"json", + async:true, + success:function(json){ + var jid = json.releasededicatedpodresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:podActionfilter + + }); + }, + error:function(args){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + + } + }, + + disable: { label: 'label.action.disable.pod', messages: { @@ -12763,20 +12800,26 @@ var podActionfilter = function(args) { var podObj = args.context.item; var allowedActions = []; - + var flag = 0; $.ajax({ url:createURL("listDedicatedPods&podId=" + args.context.pods[0].id), success:function(json){ if(json.listdedicatedpodsresponse.dedicatedpod != undefined){ var dedicatedPodObj = json.listdedicatedpodsresponse.dedicatedpod[0]; if(dedicatedPodObj.domainid != null) - allowedActions.push("release"); - } + flag =1; + } else - allowedActions.push("dedicate"); + flag =0; } }); + // if(flag == 0) + allowedActions.push("dedicate"); + + // if(flag == 1) + allowedActions.push("release"); + allowedActions.push("edit"); if(podObj.allocationstate == "Disabled") allowedActions.push("enable"); From 2489d858f953dd063fdbf19c2a67aaeed10316f8 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 18:04:22 +0530 Subject: [PATCH 221/412] explicit Dedication - Pod action filter for release/dedicate action items --- ui/scripts/system.js | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 7508e948bbd..695829286c8 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -12799,26 +12799,14 @@ var podActionfilter = function(args) { var podObj = args.context.item; + var dedicatedPodObj = args.context.podItem; var allowedActions = []; - var flag = 0; - $.ajax({ - url:createURL("listDedicatedPods&podId=" + args.context.pods[0].id), - success:function(json){ - if(json.listdedicatedpodsresponse.dedicatedpod != undefined){ - var dedicatedPodObj = json.listdedicatedpodsresponse.dedicatedpod[0]; - if(dedicatedPodObj.domainid != null) - flag =1; - } - else - flag =0; - } - }); - - // if(flag == 0) + + if(podObj.domainid != null) + allowedActions.push("release"); + else allowedActions.push("dedicate"); - // if(flag == 1) - allowedActions.push("release"); allowedActions.push("edit"); if(podObj.allocationstate == "Disabled") From e03e2aab06a5e49b3f0446730e92ae6d4549c41b Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 19:51:29 +0530 Subject: [PATCH 222/412] explicit Dedication - Cluster API integration action item for dedication/release --- ui/scripts/system.js | 119 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 695829286c8..3b0cf86ef14 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9716,6 +9716,120 @@ } }, + dedicate:{ + label: 'Dedicate Cluster', + messages: { + confirm: function(args) { + return 'Do you really want to dedicate this cluster to a domain/account? '; + }, + notification: function(args) { + return 'Cluster Dedicated'; + } + }, + createForm:{ + title:'Dedicate Cluster', + fields:{ + domainId:{ + label:'Domain', + validation:{required:true}, + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + } + }, + + accountId:{ + label:'Account', + // docID:'helpAccountForDedication', + validation:{required:false} + + } + + } + }, + action: function(args) { + //EXPLICIT DEDICATION + + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + + $.ajax({ + url: createURL("dedicateCluster&clusterId=" + args.context.clusters[0].id + "&domainId=" +args.data.domainId + array2.join("") ), + dataType: "json", + success: function(json) { + var jid = json.dedicateclusterresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:clusterActionfilter + + + }); + } + }); + } + + }, + + release:{ + label:'Release Dedicated Cluster', + messages:{ + confirm: function(args) { + return 'Do you want to release this dedicated cluster ?'; + }, + notification: function(args) { + return 'Cluster dedication released'; + } + }, + action:function(args){ + $.ajax({ + url:createURL("releaseDedicatedCluster&clusterid=" + args.context.clusters[0].id), + dataType:"json", + async:true, + success:function(json){ + var jid = json.releasededicatedclusterresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:clusterActionfilter + + }); + }, + error:function(args){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + + } + }, + + manage: { label: 'label.action.manage.cluster', messages: { @@ -12846,6 +12960,11 @@ var clusterActionfilter = function(args) { var jsonObj = args.context.item; var allowedActions = []; + + if(jsonObj.domainid != null) + allowedActions.push("release"); + else + allowedActions.push("dedicate"); if(jsonObj.state == "Enabled") {//managed, allocation enabled allowedActions.push("unmanage"); From 1545b3f3ed7ba21a4d5c475418b130f521c7d1f0 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 20:21:02 +0530 Subject: [PATCH 223/412] host and cluster action item API integration calls --- ui/scripts/system.js | 220 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 201 insertions(+), 19 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 3b0cf86ef14..02e13bdd61a 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9953,14 +9953,40 @@ success: function(json) { var item = json.listclustersresponse.cluster[0]; addExtraPropertiesToClusterObject(item); - args.response.success({ - actionFilter: clusterActionfilter, - data: item - }); - } - }); - } - }, + $.ajax({ + url:createURL("listDedicatedClusters&clusterid=" +args.context.clusters[0].id), + dataType:"json", + async:false, + success:function(json){ + if(json.listdedicatedclustersresponse.dedicatedcluster != undefined){ + var clusterItem = json.listdedicatedclustersresponse.dedicatedcluster[0]; + if (clusterItem.domainid != null) { + $.extend(item, clusterItem , { isdedicated: 'Yes' }); + } + } + else + $.extend(item ,{ isdedicated: 'No' }) + + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + args.response.success({ + actionFilter: clusterActionfilter, + data: item + }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + + } + + }); + } + }, nexusVswitch: { title:'label.nexusVswitch', listView: { @@ -10709,6 +10735,127 @@ } }, + + dedicate:{ + label: 'Dedicate Host', + messages: { + confirm: function(args) { + return 'Do you really want to dedicate this host to a domain/account? '; + }, + notification: function(args) { + return 'Host Dedicated'; + } + }, + createForm:{ + title:'Dedicate Host', + fields:{ + domainId:{ + label:'Domain', + validation:{required:true}, + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + } + }, + accountId:{ + label:'Account', + // docID:'helpAccountForDedication', + validation:{required:false} + + } + + + } + }, + + action: function(args) { + //EXPLICIT DEDICATION + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + + $.ajax({ + url: createURL("dedicateHost&hostId=" + args.context.hosts[0].id + "&domainId=" +args.data.domainId + array2.join("") ), + dataType: "json", + success: function(json) { + var jid = json.dedicatehostresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:hostActionfilter + + + }); + + /* args.response.success({ + actionFilter: podActionfilter, + data:item + });*/ + } + }); + } + + }, + + + release:{ + label:'Release Dedicated Host', + messages:{ + confirm: function(args) { + return 'Do you want to release this dedicated host ?'; + }, + notification: function(args) { + return 'Host dedication released'; + } + }, + action:function(args){ + $.ajax({ + url:createURL("releaseDedicatedHost&hostid=" + args.context.hosts[0].id), + dataType:"json", + async:true, + success:function(json){ + var jid = json.releasededicatedhostresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:hostActionfilter + + }); + }, + error:function(args){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + + } + }, + + enableMaintenanceMode: { label: 'label.action.enable.maintenance.mode', action: function(args) { @@ -10927,20 +11074,49 @@ ipaddress: { label: 'label.ip.address' }, version: { label: 'label.version' }, disconnected: { label: 'label.last.disconnected' } - } + }, + + { + + isdedicated:{label:'Dedicated'}, + domainid:{label:'Domain ID'} + + } + + ], dataProvider: function(args) { - $.ajax({ - url: createURL("listHosts&id=" + args.context.hosts[0].id), - dataType: "json", - async: true, - success: function(json) { - var item = json.listhostsresponse.host[0]; - args.response.success({ - actionFilter: hostActionfilter, - data: item - }); + $.ajax({ + url: createURL("listHosts&id=" + args.context.hosts[0].id), + dataType: "json", + async: true, + success: function(json) { + var item = json.listhostsresponse.host[0]; + $.ajax({ + url:createURL("listDedicatedHosts&hostid=" +args.context.hosts[0].id), + dataType:"json", + async:false, + success:function(json){ + if(json.listdedicatedhostsresponse.dedicatedhost != undefined){ + var hostItem = json.listdedicatedhostsresponse.dedicatedhost[0]; + if (hostItem.domainid != null) { + $.extend(item, hostItem , { isdedicated: 'Yes' }); + } + } + else + $.extend(item ,{ isdedicated: 'No' }) + + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + args.response.success({ + actionFilter: hostActionfilter, + data: item + }); + } }); } @@ -12991,6 +13167,12 @@ var jsonObj = args.context.item; var allowedActions = []; + if(jsonObj.domainid != null) + allowedActions.push("release"); + else + allowedActions.push("dedicate"); + + if (jsonObj.resourcestate == "Enabled") { allowedActions.push("edit"); allowedActions.push("enableMaintenanceMode"); From d847ef7470a28e3debd8830b9a959d593514a1d4 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 20:55:33 +0530 Subject: [PATCH 224/412] Explicit dedication dedicate icon --- ui/css/cloudstack3.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index df433d96288..36f4ac65ec1 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11887,14 +11887,16 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .create .icon, .createTemplate .icon, .enableSwift .icon, -.addVM .icon { +.addVM .icon, +.dedicate .icon { background-position: -69px -63px; } .create:hover .icon, .createTemplate:hover .icon, .enableSwift:hover .icon, -.addVM:hover .icon { +.addVM:hover .icon, +.dedicate:hover .icon { background-position: -69px -645px; } From 8c79aa8cc1f349bc7d86c640f8a6ef3ddf317c2a Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 28 May 2013 21:44:45 +0530 Subject: [PATCH 225/412] Explicit Dedication - Zone action item dedication/release API integration --- ui/scripts/system.js | 175 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 162 insertions(+), 13 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 02e13bdd61a..c8c747dc180 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -5436,6 +5436,121 @@ } }, + dedicate:{ + label: 'Dedicate Zone', + messages: { + confirm: function(args) { + return 'Do you really want to dedicate this zone to a domain/account? '; + }, + notification: function(args) { + return 'Zone Dedicated'; + } + }, + createForm:{ + title:'Dedicate Zone', + fields:{ + domainId:{ + label:'Domain', + validation:{required:true}, + select:function(args){ + $.ajax({ + url:createURL("listDomains&listAll=true"), + dataType:"json", + async:false, + success: function(json) { + var domainObjs= json.listdomainsresponse.domain; + var items=[]; + + $(domainObjs).each(function() { + items.push({id:this.id ,description:this.name }); + }); + + args.response.success({ + data: items + }); + } + + + }); + } + }, + + accountId:{ + label:'Account', + // docID:'helpAccountForDedication', + validation:{required:false} + + } + + + } + }, + + action: function(args) { + //EXPLICIT DEDICATION + var array2 = []; + if(args.data.accountId != "") + array2.push("&accountId=" +todb(args.data.accountId)); + + $.ajax({ + url: createURL("dedicateZone&zoneId=" + args.context.physicalResources[0].id + "&domainId=" +args.data.domainId + array2.join("") ), + dataType: "json", + success: function(json) { + var jid = json.dedicatezoneresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:zoneActionfilter + + + }); + + } + }); + } + + }, + + release:{ + label:'Release Dedicated Zone', + messages:{ + confirm: function(args) { + return 'Do you want to release this dedicated zone ?'; + }, + notification: function(args) { + return 'Zone dedication released'; + } + }, + action:function(args){ + $.ajax({ + url:createURL("releaseDedicatedZone&zoneid=" + args.context.physicalResources[0].id), + dataType:"json", + async:true, + success:function(json){ + var jid = json.releasededicatedzoneresponse.jobid; + args.response.success({ + _custom: + { jobId: jid + }, + notification: { + poll: pollAsyncJobResult + }, + actionFilter:zoneActionfilter + + }); + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + } + }); + + } + }, + 'remove': { label: 'label.action.delete.zone', messages: { @@ -5533,7 +5648,15 @@ isEditable: true, converter:cloudStack.converters.toBooleanText } - } + }, + + { + + isdedicated:{label:'Dedicated'}, + domainid:{label:'Domain ID'} + + } + ], dataProvider: function(args) { $.ajax({ @@ -5542,12 +5665,32 @@ id: args.context.physicalResources[0].id }, success: function(json) { - selectedZoneObj = json.listzonesresponse.zone[0]; - args.response.success({ - data: json.listzonesresponse.zone[0], - actionFilter: zoneActionfilter - }); - } + selectedZoneObj = json.listzonesresponse.zone[0]; + $.ajax({ + url:createURL("listDedicatedZones&zoneid=" +args.context.physicalResources[0].id), + dataType:"json", + async:false, + success:function(json){ + if(json.listdedicatedzonesresponse.dedicatedzone != undefined){ + var zoneItem = json.listdedicatedzonesresponse.dedicatedzone[0]; + if (zoneItem.domainid != null) { + $.extend(selectedZoneObj, zoneItem , { isdedicated: 'Yes' }); + } + } + else + $.extend(selectedZoneObj,{ isdedicated: 'No' }) + + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + } + }); + args.response.success({ + actionFilter: zoneActionfilter, + data: selectedZoneObj + }); + + } }); } }, @@ -8833,8 +8976,8 @@ }); }, - error:function(args){ - args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); } }); @@ -9821,8 +9964,8 @@ }); }, - error:function(args){ - args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); } }); @@ -10847,8 +10990,8 @@ }); }, - error:function(args){ - args.response.error(parseXMLHttpResponse(XMLHttpResponse)); + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); } }); @@ -13065,6 +13208,12 @@ var zoneActionfilter = function(args) { var jsonObj = args.context.item; var allowedActions = ['enableSwift']; + + if(jsonObj.domainid != null) + allowedActions.push("release"); + else + allowedActions.push("dedicate"); + allowedActions.push("edit"); if(jsonObj.allocationstate == "Disabled") allowedActions.push("enable"); From 5fe0d028a2ae0b62d84169c78ecd72c1d499437b Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Thu, 30 May 2013 16:28:25 +0530 Subject: [PATCH 226/412] Explicit Dedication through zone wizard --- ui/scripts/zoneWizard.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 5a8bcfdaae8..809b83a6302 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -543,8 +543,8 @@ ispublic: { isReverse: true, isBoolean: true, - label: 'label.public', - isChecked: true //checked by default (public zone) + label: 'Dedicate', + isChecked: false //checked by default (public zone) }, domain: { label: 'label.domain', @@ -1597,8 +1597,8 @@ array1.push("&internaldns2=" + todb(internaldns2)); if(args.data.pluginFrom == null) { //from zone wizard, not from quick instsaller(args.data.pluginFrom != null && args.data.pluginFrom.name == 'installWizard') who doesn't have public checkbox - if(args.data.zone.ispublic == null) //public checkbox in zone wizard is unchecked - array1.push("&domainid=" + args.data.zone.domain); + // if(args.data.zone.ispublic != null) //public checkbox in zone wizard is unchecked + // array1.push("&domainid=" + args.data.zone.domain); } if(args.data.zone.networkdomain != null && args.data.zone.networkdomain.length > 0) @@ -1619,8 +1619,10 @@ dedicatedZoneId = json.createzoneresponse.zone.id; //EXPLICIT ZONE DEDICATION - if(args.data.pluginFrom == null && args.data.zone.ispublic == null){ + if(args.data.pluginFrom == null && args.data.zone.ispublic != null){ var array2 = []; + if(args.data.zone.domain != null) + array2.push("&domainid=" + args.data.zone.domain); if(args.data.zone.accountId != "") array2.push("&accountId=" +todb(args.data.zone.accountId)); From e94c8176d2401f3bd59770a7c77ada3021b02c70 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Thu, 30 May 2013 18:08:04 +0530 Subject: [PATCH 227/412] Explicit Dedication : Zone --- ui/scripts/zoneWizard.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 809b83a6302..04ed495383b 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -348,11 +348,18 @@ } - setTimeout(function() { + /* setTimeout(function() { if ($form.find('input[name=ispublic]').is(':checked')) { - $form.find('[rel=domain]').hide(); + $form.find('[rel=domain]').show(); + $form.find('[rel=accountId]').show(); } - }); + + else{ + + $form.find('[rel=domain]').hide(); + $form.find('[rel=accountId]').hide(); + } + });*/ }, fields: { name: { @@ -541,7 +548,7 @@ validation: { required: false } }, ispublic: { - isReverse: true, + //isReverse: true, isBoolean: true, label: 'Dedicate', isChecked: false //checked by default (public zone) @@ -1597,8 +1604,10 @@ array1.push("&internaldns2=" + todb(internaldns2)); if(args.data.pluginFrom == null) { //from zone wizard, not from quick instsaller(args.data.pluginFrom != null && args.data.pluginFrom.name == 'installWizard') who doesn't have public checkbox - // if(args.data.zone.ispublic != null) //public checkbox in zone wizard is unchecked + // if(args.data.zone.ispublic != null){ //public checkbox in zone wizard is unchecked // array1.push("&domainid=" + args.data.zone.domain); + + // } } if(args.data.zone.networkdomain != null && args.data.zone.networkdomain.length > 0) @@ -1622,13 +1631,13 @@ if(args.data.pluginFrom == null && args.data.zone.ispublic != null){ var array2 = []; if(args.data.zone.domain != null) - array2.push("&domainid=" + args.data.zone.domain); + array2.push("&domainid=" + args.data.zone.domain); if(args.data.zone.accountId != "") array2.push("&accountId=" +todb(args.data.zone.accountId)); if(dedicatedZoneId != null){ $.ajax({ - url:createURL("dedicateZone&ZoneId=" +dedicatedZoneId +"&domain=" +args.data.zone.domain + array2.join("")), + url:createURL("dedicateZone&ZoneId=" +dedicatedZoneId + array2.join("")), dataType:"json", success:function(json){ var dedicatedObj = json.dedicatezoneresponse.jobid; From c42da47aa6694f987e6fda36f65efbbdb208f9f5 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Thu, 30 May 2013 21:36:27 +0530 Subject: [PATCH 228/412] formatting the code for better readability --- ui/scripts/system.js | 39 ++++----------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index c8c747dc180..3044c83771e 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -5477,12 +5477,10 @@ accountId:{ label:'Account', - // docID:'helpAccountForDedication', + docID:'helpAccountForDedication', validation:{required:false} } - - } }, @@ -5506,9 +5504,7 @@ }, actionFilter:zoneActionfilter - }); - } }); } @@ -8723,16 +8719,6 @@ var item = json.createpodresponse.pod; podId = json.createpodresponse.pod.id; - /* args.response.success({ - data:item - }); - }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - args.response.error(errorMsg); - } - });*/ - //EXPLICIT DEDICATION if(args.$form.find('.form-item[rel=isDedicated]').find('input[type=checkbox]').is(':Checked')== true){ var array2 = []; @@ -8909,7 +8895,7 @@ accountId:{ label:'Account', - // docID:'helpAccountForDedication', + docID:'helpAccountForDedication', validation:{required:false} } @@ -9677,14 +9663,6 @@ success: function(json) { var item = json.addclusterresponse.cluster[0]; clusterId= json.addclusterresponse.cluster[0].id; - /* args.response.success({ - data: $.extend(item, { state: 'Enabled' }) - }); - }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - args.response.error(errorMsg); - }*/ //EXPLICIT DEDICATION if(args.$form.find('.form-item[rel=isDedicated]').find('input[type=checkbox]').is(':Checked')== true){ @@ -9699,7 +9677,6 @@ dataType:"json", success:function(json){ var jid = json.dedicateclusterresponse.jobid; - //args.response.success({ data: $.extend(item, dedicatedObj , {state:'Enabled'}) }); args.response.success({ _custom: { jobId: jid @@ -9900,7 +9877,7 @@ accountId:{ label:'Account', - // docID:'helpAccountForDedication', + docID:'helpAccountForDedication', validation:{required:false} } @@ -9927,8 +9904,6 @@ poll: pollAsyncJobResult }, actionFilter:clusterActionfilter - - }); } }); @@ -10804,7 +10779,6 @@ dataType:"json", success:function(json){ var jid = json.dedicatehostresponse.host.jobid; - //args.response.success({ data: $.extend(item, dedicatedObj) }); args.response.success({ _custom: { jobId: jid @@ -10919,7 +10893,7 @@ }, accountId:{ label:'Account', - // docID:'helpAccountForDedication', + docID:'helpAccountForDedication', validation:{required:false} } @@ -10950,11 +10924,6 @@ }); - - /* args.response.success({ - actionFilter: podActionfilter, - data:item - });*/ } }); } From 9378820b9e821903d0338a010b1918b76117ea86 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Thu, 30 May 2013 09:54:42 -0700 Subject: [PATCH 229/412] Revert 285e8213fed8b0d6a8afc73b686b99a4fcfe5b4a, since it is already covered by recent commit 08ac8fb4687fb14cf9524a022527a64e033be9ab in a more robust way to handle upgrade. --- setup/db/db/schema-40to410.sql | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index f0e8cf31846..67e2048dce2 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -173,8 +173,6 @@ ALTER TABLE upload ADD uuid VARCHAR(40); ALTER TABLE async_job modify job_cmd VARCHAR(255); -ALTER TABLE `cloud`.`alert` ADD INDEX `last_sent` (`last_sent` DESC) ; - ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `is_persistent` int(1) unsigned NOT NULL DEFAULT 0 COMMENT 'true if the network offering provides an ability to create persistent networks'; From 0ea409546ef8de9043b37a3c3a50fbd2f6cb1629 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 28 May 2013 16:00:46 -0700 Subject: [PATCH 230/412] RulesManager interface: changed visibility for methods that are being called only from RulesManagerImpl class, from public and defined in the interface, to private/protected --- .../src/com/cloud/api/ApiResponseHelper.java | 194 +++--------------- server/src/com/cloud/api/ApiServer.java | 5 +- .../network/firewall/FirewallManagerImpl.java | 1 + .../com/cloud/network/rules/RulesManager.java | 27 --- .../cloud/network/rules/RulesManagerImpl.java | 91 +------- 5 files changed, 36 insertions(+), 282 deletions(-) diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index cf79ff89296..7e8eda09ca1 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -29,35 +29,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.StringTokenizer; import java.util.TimeZone; import javax.inject.Inject; -import com.cloud.network.GuestVlan; -import com.cloud.network.IpAddress; -import com.cloud.network.Network; -import com.cloud.network.NetworkModel; -import com.cloud.network.NetworkProfile; -import com.cloud.network.PhysicalNetwork; -import com.cloud.network.PhysicalNetworkServiceProvider; -import com.cloud.network.PhysicalNetworkTrafficType; -import com.cloud.network.RemoteAccessVpn; -import com.cloud.network.Site2SiteCustomerGateway; -import com.cloud.network.Site2SiteVpnConnection; -import com.cloud.network.Site2SiteVpnGateway; -import com.cloud.network.VirtualRouterProvider; -import com.cloud.network.VpnUser; -import com.cloud.network.VpnUserVO; -import com.cloud.network.dao.LoadBalancerVO; -import com.cloud.network.rules.FirewallRule; -import com.cloud.network.rules.FirewallRuleVO; -import com.cloud.network.rules.HealthCheckPolicy; -import com.cloud.network.rules.LoadBalancer; -import com.cloud.network.rules.PortForwardingRule; -import com.cloud.network.rules.PortForwardingRuleVO; -import com.cloud.network.rules.StaticNatRule; -import com.cloud.network.rules.StickinessPolicy; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -165,7 +140,6 @@ import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; import org.apache.cloudstack.region.Region; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.usage.UsageService; import org.apache.cloudstack.usage.UsageTypes; @@ -217,11 +191,26 @@ import com.cloud.event.Event; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.hypervisor.HypervisorCapabilities; +import com.cloud.network.GuestVlan; +import com.cloud.network.IpAddress; +import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkTrafficType; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.Site2SiteCustomerGateway; +import com.cloud.network.Site2SiteVpnConnection; +import com.cloud.network.Site2SiteVpnGateway; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.network.VpnUser; +import com.cloud.network.VpnUserVO; import com.cloud.network.as.AutoScalePolicy; import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.network.as.AutoScaleVmProfile; @@ -230,10 +219,19 @@ import com.cloud.network.as.Condition; import com.cloud.network.as.ConditionVO; import com.cloud.network.as.Counter; import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.router.VirtualRouter; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.HealthCheckPolicy; +import com.cloud.network.rules.LoadBalancer; import com.cloud.network.rules.LoadBalancerContainer.Scheme; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.rules.StaticNatRule; +import com.cloud.network.rules.StickinessPolicy; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupVO; import com.cloud.network.security.SecurityRule; @@ -254,7 +252,6 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectAccount; import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; -import com.cloud.server.Criteria; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.service.ServiceOfferingVO; @@ -265,7 +262,6 @@ import com.cloud.storage.S3; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.StoragePool; import com.cloud.storage.Swift; @@ -1875,148 +1871,6 @@ public class ApiResponseHelper implements ResponseGenerator { return ApiDBUtils.newEventResponse(vEvent); } - private List sumCapacities(List hostCapacities) { - Map totalCapacityMap = new HashMap(); - Map usedCapacityMap = new HashMap(); - - Set poolIdsToIgnore = new HashSet(); - Criteria c = new Criteria(); - // TODO: implement - List allStoragePools = ApiDBUtils.searchForStoragePools(c); - for (StoragePoolVO pool : allStoragePools) { - StoragePoolType poolType = pool.getPoolType(); - if (!(poolType.isShared())) {// All the non shared storages shouldn't show up in the capacity calculation - poolIdsToIgnore.add(pool.getId()); - } - } - - float cpuOverprovisioningFactor = ApiDBUtils.getCpuOverprovisioningFactor(); - - // collect all the capacity types, sum allocated/used and sum total...get one capacity number for each - for (Capacity capacity : hostCapacities) { - - // check if zone exist - DataCenter zone = ApiDBUtils.findZoneById(capacity.getDataCenterId()); - if (zone == null) { - continue; - } - - short capacityType = capacity.getCapacityType(); - - // If local storage then ignore - if ((capacityType == Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED || capacityType == Capacity.CAPACITY_TYPE_STORAGE) - && poolIdsToIgnore.contains(capacity.getHostOrPoolId())) { - continue; - } - - String key = capacity.getCapacityType() + "_" + capacity.getDataCenterId(); - String keyForPodTotal = key + "_-1"; - - boolean sumPodCapacity = false; - if (capacity.getPodId() != null) { - key += "_" + capacity.getPodId(); - sumPodCapacity = true; - } - - Long totalCapacity = totalCapacityMap.get(key); - Long usedCapacity = usedCapacityMap.get(key); - - // reset overprovisioning factor to 1 - float overprovisioningFactor = 1; - if (capacityType == Capacity.CAPACITY_TYPE_CPU) { - overprovisioningFactor = cpuOverprovisioningFactor; - } - - if (totalCapacity == null) { - totalCapacity = new Long((long) (capacity.getTotalCapacity() * overprovisioningFactor)); - } else { - totalCapacity = new Long((long) (capacity.getTotalCapacity() * overprovisioningFactor)) + totalCapacity; - } - - if (usedCapacity == null) { - usedCapacity = new Long(capacity.getUsedCapacity()); - } else { - usedCapacity = new Long(capacity.getUsedCapacity() + usedCapacity); - } - - if (capacityType == Capacity.CAPACITY_TYPE_CPU || capacityType == Capacity.CAPACITY_TYPE_MEMORY) { // Reserved - // Capacity - // accounts - // for - // stopped - // vms - // that - // have been - // stopped - // within - // an - // interval - usedCapacity += capacity.getReservedCapacity(); - } - - totalCapacityMap.put(key, totalCapacity); - usedCapacityMap.put(key, usedCapacity); - - if (sumPodCapacity) { - totalCapacity = totalCapacityMap.get(keyForPodTotal); - usedCapacity = usedCapacityMap.get(keyForPodTotal); - - overprovisioningFactor = 1; - if (capacityType == Capacity.CAPACITY_TYPE_CPU) { - overprovisioningFactor = cpuOverprovisioningFactor; - } - - if (totalCapacity == null) { - totalCapacity = new Long((long) (capacity.getTotalCapacity() * overprovisioningFactor)); - } else { - totalCapacity = new Long((long) (capacity.getTotalCapacity() * overprovisioningFactor)) + totalCapacity; - } - - if (usedCapacity == null) { - usedCapacity = new Long(capacity.getUsedCapacity()); - } else { - usedCapacity = new Long(capacity.getUsedCapacity() + usedCapacity); - } - - if (capacityType == Capacity.CAPACITY_TYPE_CPU || capacityType == Capacity.CAPACITY_TYPE_MEMORY) { // Reserved - // Capacity - // accounts - // for - // stopped - // vms - // that - // have - // been - // stopped - // within - // an - // interval - usedCapacity += capacity.getReservedCapacity(); - } - - totalCapacityMap.put(keyForPodTotal, totalCapacity); - usedCapacityMap.put(keyForPodTotal, usedCapacity); - } - } - - List summedCapacities = new ArrayList(); - for (String key : totalCapacityMap.keySet()) { - CapacityVO summedCapacity = new CapacityVO(); - - StringTokenizer st = new StringTokenizer(key, "_"); - summedCapacity.setCapacityType(Short.parseShort(st.nextToken())); - summedCapacity.setDataCenterId(Long.parseLong(st.nextToken())); - if (st.hasMoreTokens()) { - summedCapacity.setPodId(Long.parseLong(st.nextToken())); - } - - summedCapacity.setTotalCapacity(totalCapacityMap.get(key)); - summedCapacity.setUsedCapacity(usedCapacityMap.get(key)); - - summedCapacities.add(summedCapacity); - } - return summedCapacities; - } @Override public List createCapacityResponse(List result, DecimalFormat format) { diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index e748a35a747..05fa6982564 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -65,7 +65,6 @@ import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd; import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.user.ListUsersCmd; -import com.cloud.event.ActionEventUtils; import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; @@ -81,7 +80,6 @@ import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd; import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd; import org.apache.cloudstack.api.response.ExceptionResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.region.RegionManager; import org.apache.commons.codec.binary.Base64; import org.apache.http.ConnectionClosedException; import org.apache.http.HttpException; @@ -123,6 +121,7 @@ import com.cloud.configuration.ConfigurationVO; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; +import com.cloud.event.ActionEventUtils; import com.cloud.exception.AccountLimitException; import com.cloud.exception.CloudAuthenticationException; import com.cloud.exception.InsufficientCapacityException; @@ -167,8 +166,6 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer @Inject List _pluggableServices; @Inject List _apiAccessCheckers; - @Inject private final RegionManager _regionMgr = null; - private static int _workerCount = 0; private static ApiServer s_instance = null; private static final DateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index 334a5a108e6..f7275b0e237 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -159,6 +159,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType()); } + @Override public FirewallRule createIngressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException { Account caller = UserContext.current().getCaller(); Long sourceIpAddressId = rule.getSourceIpAddressId(); diff --git a/server/src/com/cloud/network/rules/RulesManager.java b/server/src/com/cloud/network/rules/RulesManager.java index cede987280d..201d79db9c6 100644 --- a/server/src/com/cloud/network/rules/RulesManager.java +++ b/server/src/com/cloud/network/rules/RulesManager.java @@ -32,47 +32,20 @@ import com.cloud.vm.VirtualMachine; */ public interface RulesManager extends RulesService { - boolean applyPortForwardingRules(long ipAddressId, boolean continueOnError, Account caller); - - boolean applyStaticNatRulesForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke); - boolean applyPortForwardingRulesForNetwork(long networkId, boolean continueOnError, Account caller); boolean applyStaticNatRulesForNetwork(long networkId, boolean continueOnError, Account caller); - void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, Account caller); - void checkRuleAndUserVm(FirewallRule rule, UserVm userVm, Account caller); boolean revokeAllPFAndStaticNatRulesForIp(long ipId, long userId, Account caller) throws ResourceUnavailableException; boolean revokeAllPFStaticNatRulesForNetwork(long networkId, long userId, Account caller) throws ResourceUnavailableException; - List listFirewallRulesByIp(long ipAddressId); - - /** - * Returns a list of port forwarding rules that are ready for application - * to the network elements for this ip. - * - * @param ip - * @return List of PortForwardingRule - */ - List listPortForwardingRulesForApplication(long ipId); - - List gatherPortForwardingRulesForApplication(List addrs); - boolean revokePortForwardingRulesForVm(long vmId); - boolean revokeStaticNatRulesForVm(long vmId); - FirewallRule[] reservePorts(IpAddress ip, String protocol, FirewallRule.Purpose purpose, boolean openFirewall, Account caller, int... ports) throws NetworkRuleConflictException; - boolean releasePorts(long ipId, String protocol, FirewallRule.Purpose purpose, int... ports); - - List listByNetworkId(long networkId); - - boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke); - boolean applyStaticNatsForNetwork(long networkId, boolean continueOnError, Account caller); void getSystemIpAndEnableStaticNatForVm(VirtualMachine vm, boolean getNewIp) throws InsufficientAddressCapacityException; diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index dd5f99ba574..41bf2b3af65 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -24,7 +24,6 @@ import java.util.Set; import javax.ejb.Local; import javax.inject.Inject; -import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.user.firewall.ListPortForwardingRulesCmd; import org.apache.log4j.Logger; @@ -54,7 +53,6 @@ import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.rules.FirewallRule.FirewallRuleType; import com.cloud.network.rules.FirewallRule.Purpose; -import com.cloud.network.rules.FirewallRule.TrafficType; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.NetworkOffering; @@ -69,7 +67,6 @@ import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; @@ -139,8 +136,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules @Inject LoadBalancerVMMapDao _loadBalancerVMMapDao; - @Override - public void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, Account caller) { + + protected void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, Account caller) { if (ipAddress == null || ipAddress.getAllocatedTime() == null || ipAddress.getAllocatedToAccountId() == null) { throw new InvalidParameterValueException("Unable to create ip forwarding rule on address " + ipAddress + ", invalid IP address specified."); } @@ -706,6 +703,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return true; } + private boolean revokeStaticNatRuleInternal(long ruleId, Account caller, long userId, boolean apply) { FirewallRuleVO rule = _firewallDao.findById(ruleId); @@ -756,45 +754,6 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return success; } - @Override - public boolean revokeStaticNatRulesForVm(long vmId) { - boolean success = true; - - UserVmVO vm = _vmDao.findByIdIncludingRemoved(vmId); - if (vm == null) { - return false; - } - - List rules = _firewallDao.listStaticNatByVmId(vm.getId()); - Set ipsToReprogram = new HashSet(); - - if (rules == null || rules.isEmpty()) { - s_logger.debug("No static nat rules are found for vm id=" + vmId); - return true; - } - - for (FirewallRuleVO rule : rules) { - // mark static nat as Revoked, but don't revoke it yet (apply = false) - revokeStaticNatRuleInternal(rule.getId(), _accountMgr.getSystemAccount(), Account.ACCOUNT_ID_SYSTEM, false); - ipsToReprogram.add(rule.getSourceIpAddressId()); - } - - // apply rules for all ip addresses - for (Long ipId : ipsToReprogram) { - s_logger.debug("Applying static nat rules for ip address id=" + ipId + " as a part of vm expunge"); - if (!applyStaticNatRulesForIp(ipId, true, _accountMgr.getSystemAccount(), true)) { - success = false; - s_logger.warn("Failed to apply static nat rules for ip id=" + ipId); - } - } - - return success; - } - - @Override - public List listPortForwardingRulesForApplication(long ipId) { - return _portForwardingDao.listForApplication(ipId); - } @Override public Pair, Integer> listPortForwardingRules(ListPortForwardingRulesCmd cmd) { @@ -872,8 +831,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return _firewallCidrsDao.getSourceCidrs(ruleId); } - @Override - public boolean applyPortForwardingRules(long ipId, boolean continueOnError, Account caller) { + + protected boolean applyPortForwardingRules(long ipId, boolean continueOnError, Account caller) { List rules = _portForwardingDao.listForApplication(ipId); if (rules.size() == 0) { @@ -897,8 +856,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return true; } - @Override - public boolean applyStaticNatRulesForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { + + protected boolean applyStaticNatRulesForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { List rules = _firewallDao.listByIpAndPurpose(sourceIpId, Purpose.StaticNat); List staticNatRules = new ArrayList(); @@ -1172,15 +1131,6 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return success && rules.size() == 0; } - @Override - public List listFirewallRulesByIp(long ipId) { - return null; - } - - @Override - public boolean releasePorts(long ipId, String protocol, FirewallRule.Purpose purpose, int... ports) { - return _firewallDao.releasePorts(ipId, protocol, purpose, ports); - } @Override @DB @@ -1221,29 +1171,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules } } - @Override - public List gatherPortForwardingRulesForApplication(List addrs) { - List allRules = new ArrayList(); - for (IpAddress addr : addrs) { - if (!addr.readyToUse()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Skipping " + addr + " because it is not ready for propation yet."); - } - continue; - } - allRules.addAll(_portForwardingDao.listForApplication(addr.getId())); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found " + allRules.size() + " rules to apply for the addresses."); - } - - return allRules; - } - - @Override - public List listByNetworkId(long networkId) { + private List listByNetworkId(long networkId) { return _portForwardingDao.listByNetwork(networkId); } @@ -1367,8 +1296,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return new StaticNatRuleImpl(ruleVO, dstIp); } - @Override - public boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { + + protected boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { IpAddress sourceIp = _ipAddressDao.findById(sourceIpId); List staticNats = createStaticNatForIp(sourceIp, caller, forRevoke); From 4701dd760b3ddec5463bf6a8e21d35ce2d982b7d Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 28 May 2013 16:18:52 -0700 Subject: [PATCH 231/412] Removed unused code from ManagementServerImpl --- .../com/cloud/server/ManagementServer.java | 34 ---------------- .../cloud/server/ManagementServerImpl.java | 40 ++----------------- 2 files changed, 4 insertions(+), 70 deletions(-) diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index 969bc6557e1..f60ce488e10 100755 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -16,19 +16,11 @@ // under the License. package com.cloud.server; -import java.util.Date; import java.util.List; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.ManagementServerException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.VirtualMachineMigrationException; -import org.apache.cloudstack.api.command.admin.systemvm.ScaleSystemVMCmd; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import com.cloud.event.EventVO; import com.cloud.host.HostVO; -import com.cloud.info.ConsoleProxyInfo; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; import com.cloud.utils.component.PluggableService; @@ -59,30 +51,6 @@ public interface ManagementServer extends ManagementService, PluggableService { */ HostVO getHostBy(long hostId); - /** - * Retrieves all Events between the start and end date specified - * - * @param userId - * unique id of the user, pass in -1 to retrieve events for all users - * @param accountId - * unique id of the account (which could be shared by many users), pass in -1 to retrieve events for all accounts - * @param domainId - * the id of the domain in which to search for users (useful when -1 is passed in for userId) - * @param type - * the type of the event. - * @param level - * INFO, WARN, or ERROR - * @param startDate - * inclusive. - * @param endDate - * inclusive. If date specified is greater than the current time, the system will use the current time. - * @return List of events - */ - List getEvents(long userId, long accountId, Long domainId, String type, String level, Date startDate, Date endDate); - - //FIXME - move all console proxy related commands to corresponding managers - ConsoleProxyInfo getConsoleProxyForVm(long dataCenterId, long userVmId); - String getConsoleAccessUrlRoot(long vmId); GuestOSVO getGuestOs(Long guestOsId); @@ -103,7 +71,5 @@ public interface ManagementServer extends ManagementService, PluggableService { String getEncryptionKey(); String getEncryptionIV(); void resetEncryptionKeyIV(); - - public void enableAdminUser(String password); } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index d5d95f8eadb..cf50e61d6ac 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -922,38 +922,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } - @Override - public List getEvents(long userId, long accountId, Long domainId, String type, String level, Date startDate, Date endDate) { - SearchCriteria sc = _eventDao.createSearchCriteria(); - if (userId > 0) { - sc.addAnd("userId", SearchCriteria.Op.EQ, userId); - } - if (accountId > 0) { - sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); - } - if (domainId != null) { - sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); - } - if (type != null) { - sc.addAnd("type", SearchCriteria.Op.EQ, type); - } - if (level != null) { - sc.addAnd("level", SearchCriteria.Op.EQ, level); - } - if (startDate != null && endDate != null) { - startDate = massageDate(startDate, 0, 0, 0); - endDate = massageDate(endDate, 23, 59, 59); - sc.addAnd("createDate", SearchCriteria.Op.BETWEEN, startDate, endDate); - } else if (startDate != null) { - startDate = massageDate(startDate, 0, 0, 0); - sc.addAnd("createDate", SearchCriteria.Op.GTEQ, startDate); - } else if (endDate != null) { - endDate = massageDate(endDate, 23, 59, 59); - sc.addAnd("createDate", SearchCriteria.Op.LTEQ, endDate); - } - - return _eventDao.search(sc, null); - } @Override public boolean archiveEvents(ArchiveEventsCmd cmd) { @@ -2229,8 +2197,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return new Pair, Integer>(result.first(), result.second()); } - @Override - public ConsoleProxyInfo getConsoleProxyForVm(long dataCenterId, long userVmId) { + + protected ConsoleProxyInfo getConsoleProxyForVm(long dataCenterId, long userVmId) { return _consoleProxyMgr.assignProxy(dataCenterId, userVmId); } @@ -4134,8 +4102,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } - @Override - public void enableAdminUser(String password) { + + private void enableAdminUser(String password) { String encodedPassword = null; UserVO adminUser = _userDao.getUser(2); From 973fc84d6c7b3e747b33711880b9c52b6eb342c6 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 28 May 2013 16:22:13 -0700 Subject: [PATCH 232/412] AccountManager: fixed unchecked conversion warning --- .../cloudstack/api/ResponseGenerator.java | 19 ++++++++++--------- server/src/com/cloud/user/AccountManager.java | 2 +- .../com/cloud/user/AccountManagerImpl.java | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 0732e77a781..7da40109a4f 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -21,15 +21,8 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; -import com.cloud.vm.NicSecondaryIp; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; -import com.cloud.network.vpc.NetworkACL; -import com.cloud.network.vpc.NetworkACLItem; -import com.cloud.network.vpc.PrivateGateway; -import com.cloud.network.vpc.StaticRoute; -import com.cloud.network.vpc.Vpc; -import com.cloud.network.vpc.VpcOffering; import org.apache.cloudstack.api.ApiConstants.HostDetails; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; @@ -67,6 +60,7 @@ import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; import org.apache.cloudstack.api.response.LDAPConfigResponse; import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; import org.apache.cloudstack.api.response.NetworkResponse; @@ -74,6 +68,8 @@ import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.PortableIpRangeResponse; +import org.apache.cloudstack.api.response.PortableIpResponse; import org.apache.cloudstack.api.response.PrivateGatewayResponse; import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectInvitationResponse; @@ -116,11 +112,10 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.api.response.*; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; -import org.apache.cloudstack.region.Region; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.Region; import org.apache.cloudstack.usage.Usage; import com.cloud.async.AsyncJob; @@ -164,6 +159,12 @@ import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.StickinessPolicy; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityRule; +import com.cloud.network.vpc.NetworkACL; +import com.cloud.network.vpc.NetworkACLItem; +import com.cloud.network.vpc.PrivateGateway; +import com.cloud.network.vpc.StaticRoute; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOffering; import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java index 6ba1f6a7f96..42476c15297 100755 --- a/server/src/com/cloud/user/AccountManager.java +++ b/server/src/com/cloud/user/AccountManager.java @@ -51,7 +51,7 @@ public interface AccountManager extends AccountService { Long checkAccessAndSpecifyAuthority(Account caller, Long zoneId); - Account createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details, String uuid); + Account createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details, String uuid); UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID); diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 3f06e419cdb..93a9fb61bec 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -54,9 +54,9 @@ import com.cloud.api.query.dao.UserAccountJoinDao; import com.cloud.api.query.vo.ControlledViewEntity; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.Resource.ResourceOwnerType; import com.cloud.configuration.ResourceCountVO; import com.cloud.configuration.ResourceLimit; -import com.cloud.configuration.Resource.ResourceOwnerType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; import com.cloud.configuration.dao.ResourceLimitDao; @@ -1740,7 +1740,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override @DB - public AccountVO createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details, String uuid) { + public AccountVO createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details, String uuid) { // Validate domain Domain domain = _domainMgr.getDomain(domainId); if (domain == null) { From 1300fc8128679beef03781bffff68cfbe82f0860 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 28 May 2013 16:35:32 -0700 Subject: [PATCH 233/412] AccountManager/Service: reduced visibility for methods that are called only from AccountManagerImpl itself --- api/src/com/cloud/user/AccountService.java | 13 +-- server/src/com/cloud/api/ApiServer.java | 3 +- server/src/com/cloud/user/AccountManager.java | 14 +-- .../com/cloud/user/AccountManagerImpl.java | 107 ++---------------- .../cloud/network/MockRulesManagerImpl.java | 63 ----------- .../cloud/user/MockAccountManagerImpl.java | 58 +++------- 6 files changed, 34 insertions(+), 224 deletions(-) diff --git a/api/src/com/cloud/user/AccountService.java b/api/src/com/cloud/user/AccountService.java index 903eebc5bf8..8153a3f1af6 100755 --- a/api/src/com/cloud/user/AccountService.java +++ b/api/src/com/cloud/user/AccountService.java @@ -16,22 +16,15 @@ // under the License. package com.cloud.user; -import java.util.List; import java.util.Map; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; -import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; -import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; -import org.apache.cloudstack.api.command.admin.user.RegisterCmd; -import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; - import org.apache.cloudstack.api.command.admin.user.RegisterCmd; import com.cloud.domain.Domain; import com.cloud.exception.PermissionDeniedException; -import com.cloud.utils.Pair; public interface AccountService { @@ -83,13 +76,11 @@ public interface AccountService { Account finalizeOwner(Account caller, String accountName, Long domainId, Long projectId); - Pair, Long> finalizeAccountDomainForList(Account caller, String accountName, Long domainId, Long projectId); - Account getActiveAccountByName(String accountName, Long domainId); - Account getActiveAccountById(Long accountId); + Account getActiveAccountById(long accountId); - Account getAccount(Long accountId); + Account getAccount(long accountId); User getActiveUser(long userId); diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 05fa6982564..b3098d176b3 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -551,6 +551,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } + @SuppressWarnings("unchecked") private void buildAsyncListResponse(BaseListCmd command, Account account) { List responses = ((ListResponse) command.getResponseObject()).getResponses(); if (responses != null && responses.size() > 0) { @@ -845,7 +846,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer @Override public void logoutUser(long userId) { - _accountMgr.logoutUser(Long.valueOf(userId)); + _accountMgr.logoutUser(userId); return; } diff --git a/server/src/com/cloud/user/AccountManager.java b/server/src/com/cloud/user/AccountManager.java index 42476c15297..2e909c8e042 100755 --- a/server/src/com/cloud/user/AccountManager.java +++ b/server/src/com/cloud/user/AccountManager.java @@ -47,21 +47,15 @@ public interface AccountManager extends AccountService { boolean deleteAccount(AccountVO account, long callerUserId, Account caller); - boolean cleanupAccount(AccountVO account, long callerUserId, Account caller); - Long checkAccessAndSpecifyAuthority(Account caller, Long zoneId); Account createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details, String uuid); - - UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID); - + /** * Logs out a user * @param userId */ - void logoutUser(Long userId); - - UserAccount getUserAccount(String username, Long domainId); + void logoutUser(long userId); /** * Authenticates a user when s/he logs in. @@ -87,9 +81,7 @@ public interface AccountManager extends AccountService { * @return the user/account pair if one exact match was found, null otherwise */ Pair findUserByApiKey(String apiKey); - - boolean lockAccount(long accountId); - + boolean enableAccount(long accountId); void buildACLSearchBuilder(SearchBuilder sb, Long domainId, diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 93a9fb61bec..292142f759f 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -50,7 +50,6 @@ import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import com.cloud.api.ApiDBUtils; -import com.cloud.api.query.dao.UserAccountJoinDao; import com.cloud.api.query.vo.ControlledViewEntity; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; @@ -105,7 +104,6 @@ import com.cloud.projects.ProjectVO; import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; import com.cloud.server.auth.UserAuthenticator; -import com.cloud.storage.StorageManager; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeManager; @@ -164,8 +162,6 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Inject private UserAccountDao _userAccountDao; @Inject - private UserAccountJoinDao _userAccountJoinDao; - @Inject private VolumeDao _volumeDao; @Inject private UserVmDao _userVmDao; @@ -190,8 +186,6 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Inject private UserVmManager _vmMgr; @Inject - private StorageManager _storageMgr; - @Inject private TemplateManager _tmpltMgr; @Inject private ConfigurationManager _configMgr; @@ -505,8 +499,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return success; } - @Override - public boolean lockAccount(long accountId) { + + protected boolean lockAccount(long accountId) { boolean success = false; Account account = _accountDao.findById(accountId); if (account != null) { @@ -544,8 +538,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return cleanupAccount(account, callerUserId, caller); } - @Override - public boolean cleanupAccount(AccountVO account, long callerUserId, Account caller) { + + protected boolean cleanupAccount(AccountVO account, long callerUserId, Account caller) { long accountId = account.getId(); boolean accountCleanupNeeded = false; @@ -1617,21 +1611,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } @Override - public Account getActiveAccountById(Long accountId) { - if (accountId == null) { - throw new InvalidParameterValueException("AccountId is required by account search"); - } else { - return _accountDao.findById(accountId); - } + public Account getActiveAccountById(long accountId) { + return _accountDao.findById(accountId); } @Override - public Account getAccount(Long accountId) { - if (accountId == null) { - throw new InvalidParameterValueException("AccountId is required by account search"); - } else { - return _accountDao.findByIdIncludingRemoved(accountId); - } + public Account getAccount(long accountId) { + return _accountDao.findByIdIncludingRemoved(accountId); } @Override @@ -1669,62 +1655,6 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return _userDao.findByIdIncludingRemoved(userId); } - @Override - public Pair, Long> finalizeAccountDomainForList(Account caller, String accountName, Long domainId, Long projectId) { - List permittedAccounts = new ArrayList(); - - if (isAdmin(caller.getType())) { - if (domainId == null && accountName != null) { - throw new InvalidParameterValueException("accountName and domainId might be specified together"); - } else if (domainId != null) { - Domain domain = _domainMgr.getDomain(domainId); - if (domain == null) { - throw new InvalidParameterValueException("Unable to find the domain by id=" + domainId); - } - - checkAccess(caller, domain); - - if (accountName != null) { - Account owner = getActiveAccountByName(accountName, domainId); - if (owner == null) { - throw new InvalidParameterValueException("Unable to find account with name " + accountName + " in domain id=" + domainId); - } - - permittedAccounts.add(owner.getId()); - } - } - } else if (accountName != null && domainId != null) { - if (!accountName.equals(caller.getAccountName()) || domainId.longValue() != caller.getDomainId()) { - throw new PermissionDeniedException("Can't list port forwarding rules for account " + accountName + " in domain " + domainId + ", permission denied"); - } - permittedAccounts.add(getActiveAccountByName(accountName, domainId).getId()); - } else { - permittedAccounts.add(caller.getAccountId()); - } - - if (domainId == null && caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { - domainId = caller.getDomainId(); - } - - // set project information - if (projectId != null) { - if (projectId.longValue() == -1) { - permittedAccounts.addAll(_projectMgr.listPermittedProjectAccounts(caller.getId())); - } else { - permittedAccounts.clear(); - Project project = _projectMgr.getProject(projectId); - if (project == null) { - throw new InvalidParameterValueException("Unable to find project by id " + projectId); - } - if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) { - throw new InvalidParameterValueException("Account " + caller + " can't access project id=" + projectId); - } - permittedAccounts.add(project.getProjectAccountId()); - } - } - - return new Pair, Long>(permittedAccounts, domainId); - } @Override public User getActiveUserByRegistrationToken(String registrationToken) { @@ -1806,9 +1736,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return account; } - @Override @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User") - public UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID) { + protected UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID) { if (s_logger.isDebugEnabled()) { s_logger.debug("Creating user: " + userName + ", accountId: " + accountId + " timezone:" + timezone); } @@ -1833,29 +1762,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } @Override - public void logoutUser(Long userId) { + public void logoutUser(long userId) { UserAccount userAcct = _userAccountDao.findById(userId); if (userAcct != null) { ActionEventUtils.onActionEvent(userId, userAcct.getAccountId(), userAcct.getDomainId(), EventTypes.EVENT_USER_LOGOUT, "user has logged out"); } // else log some kind of error event? This likely means the user doesn't exist, or has been deleted... } - @Override - public UserAccount getUserAccount(String username, Long domainId) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Retrieiving user: " + username + " in domain " + domainId); - } - - UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId); - if (userAccount == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find user with name " + username + " in domain " + domainId); - } - return null; - } - - return userAccount; - } @Override public UserAccount authenticateUser(String username, String password, Long domainId, String loginIpAddress, Map requestParameters) { diff --git a/server/test/com/cloud/network/MockRulesManagerImpl.java b/server/test/com/cloud/network/MockRulesManagerImpl.java index 82a3e9346e3..331a47ffca6 100644 --- a/server/test/com/cloud/network/MockRulesManagerImpl.java +++ b/server/test/com/cloud/network/MockRulesManagerImpl.java @@ -135,19 +135,6 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return false; } - @Override - public boolean applyPortForwardingRules(long ipAddressId, - boolean continueOnError, Account caller) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean applyStaticNatRulesForIp(long sourceIpId, - boolean continueOnError, Account caller, boolean forRevoke) { - // TODO Auto-generated method stub - return false; - } @Override public boolean applyPortForwardingRulesForNetwork(long networkId, @@ -163,13 +150,6 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return false; } - @Override - public void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, - Account caller) { - // TODO Auto-generated method stub - - } - @Override public void checkRuleAndUserVm(FirewallRule rule, UserVm userVm, Account caller) { @@ -191,25 +171,6 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return false; } - @Override - public List listFirewallRulesByIp(long ipAddressId) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List listPortForwardingRulesForApplication( - long ipId) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List gatherPortForwardingRulesForApplication( - List addrs) { - // TODO Auto-generated method stub - return null; - } @Override public boolean revokePortForwardingRulesForVm(long vmId) { @@ -217,11 +178,6 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return false; } - @Override - public boolean revokeStaticNatRulesForVm(long vmId) { - // TODO Auto-generated method stub - return false; - } @Override public FirewallRule[] reservePorts(IpAddress ip, String protocol, @@ -231,25 +187,6 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return null; } - @Override - public boolean releasePorts(long ipId, String protocol, Purpose purpose, - int... ports) { - // TODO Auto-generated method stub - return false; - } - - @Override - public List listByNetworkId(long networkId) { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean applyStaticNatForIp(long sourceIpId, - boolean continueOnError, Account caller, boolean forRevoke) { - // TODO Auto-generated method stub - return false; - } @Override public boolean applyStaticNatsForNetwork(long networkId, diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java index 64919afa74f..38cc1a84a55 100644 --- a/server/test/com/cloud/user/MockAccountManagerImpl.java +++ b/server/test/com/cloud/user/MockAccountManagerImpl.java @@ -132,12 +132,6 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco return null; } - @Override - public Pair,Long> finalizeAccountDomainForList(Account caller, String accountName, Long domainId, Long projectId) { - // TODO Auto-generated method stub - return null; - } - @Override public Account getActiveAccountByName(String accountName, Long domainId) { // TODO Auto-generated method stub @@ -145,13 +139,13 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco } @Override - public Account getActiveAccountById(Long accountId) { + public Account getActiveAccountById(long accountId) { // TODO Auto-generated method stub return null; } @Override - public Account getAccount(Long accountId) { + public Account getAccount(long accountId) { // TODO Auto-generated method stub return null; } @@ -192,24 +186,12 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco return false; } - @Override - public boolean deleteAccount(AccountVO account, long callerUserId, Account caller) { - // TODO Auto-generated method stub - return false; - } - @Override public void checkAccess(Account account, Domain domain) throws PermissionDeniedException { // TODO Auto-generated method stub } - - @Override - public boolean cleanupAccount(AccountVO account, long callerUserId, Account caller) { - // TODO Auto-generated method stub - return false; - } - + @Override public Long checkAccessAndSpecifyAuthority(Account caller, Long zoneId) { // TODO Auto-generated method stub @@ -244,14 +226,10 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco } @Override - public void logoutUser(Long userId) { + public void logoutUser(long userId) { // TODO Auto-generated method stub } - @Override - public UserAccount getUserAccount(String username, Long domainId) { - return null; - } @Override public UserAccount authenticateUser(String username, String password, Long domainId, String loginIpAddress, Map requestParameters) { @@ -263,21 +241,12 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco return null; } - @Override - public UserVO createUser(long accountId, String userName, String password, String firstName, String lastName, String email, String timezone, String userUUID) { - return null; - } @Override public String[] createApiKeyAndSecretKey(RegisterCmd cmd) { return null; } - @Override - public boolean lockAccount(long accountId) { - return true; - } - @Override public boolean enableAccount(long accountId) { // TODO Auto-generated method stub @@ -341,15 +310,22 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco return null; } - @Override - public Account createAccount(String accountName, short accountType, - Long domainId, String networkDomain, Map details, String uuid) { - // TODO Auto-generated method stub - return null; - } + @Override public RoleType getRoleType(Account account) { return null; } + @Override + public boolean deleteAccount(AccountVO account, long callerUserId, Account caller) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Account createAccount(String accountName, short accountType, Long domainId, String networkDomain, Map details, String uuid) { + // TODO Auto-generated method stub + return null; + } + } From 70ca581499b00db2199f8d9b30fb72eef53f8b2d Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Thu, 30 May 2013 14:10:18 -0700 Subject: [PATCH 234/412] CLOUDSTACK-2772: Programming firewall rules to VR when recovering redundant network --- .../network/router/VirtualNetworkApplianceManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index b8b88600a76..c71d037e05d 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1179,7 +1179,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V _alertMgr.sendAlert(AlertManager.ALERT_TYPE_DOMAIN_ROUTER, backupRouter.getDataCenterId(), backupRouter.getPodIdToDeployIn(), title, title); try { - rebootRouter(backupRouter.getId(), false); + rebootRouter(backupRouter.getId(), true); } catch (ConcurrentOperationException e) { s_logger.warn("Fail to reboot " + backupRouter.getInstanceName(), e); } catch (ResourceUnavailableException e) { From 7296cca9ac1121b962739a474f9d9ce0bd162fb6 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Thu, 30 May 2013 14:18:49 -0700 Subject: [PATCH 235/412] CLOUDSTACK-2771: Unable to create guest VM in basic zone: Zone is dedicated Changes: - Check the domain of the dedicated zone --- .../deploy/DeploymentPlanningManagerImpl.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index d954c8bf3e2..c29cefb41ba 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -210,7 +210,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } } - checkForNonDedicatedResources(vm, dc, avoids); + checkForNonDedicatedResources(vmProfile, dc, avoids); if (s_logger.isDebugEnabled()) { s_logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid()); @@ -438,8 +438,9 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy return dest; } - private void checkForNonDedicatedResources(VirtualMachine vm, DataCenter dc, ExcludeList avoids) { - boolean isExplicit = false; + private void checkForNonDedicatedResources(VirtualMachineProfile vmProfile, DataCenter dc, ExcludeList avoids) { + boolean isExplicit = false; + VirtualMachine vm = vmProfile.getVirtualMachine(); // check affinity group of type Explicit dedication exists List vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), "ExplicitDedication"); @@ -450,8 +451,11 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy if (!isExplicit && vm.getType() == VirtualMachine.Type.User) { //add explicitly dedicated resources in avoidList DedicatedResourceVO dedicatedZone = _dedicatedDao.findByZoneId(dc.getId()); - if (dedicatedZone != null) { - throw new CloudRuntimeException("Failed to deploy VM. Zone " + dc.getName() + " is dedicated."); + if (dedicatedZone != null) { + long accountDomainId = vmProfile.getOwner().getDomainId(); + if (dedicatedZone.getDomainId() != null && !dedicatedZone.getDomainId().equals(accountDomainId)) { + throw new CloudRuntimeException("Failed to deploy VM. Zone " + dc.getName() + " is dedicated."); + } } List podsInDc = _podDao.listByDataCenterId(dc.getId()); From 800cd391d5ff2baf02f7eed137f4f50edb126a4c Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Thu, 30 May 2013 11:47:13 -0600 Subject: [PATCH 236/412] RPM build - run mvn clean before building packages, to get a build from fresh source Signed-off-by: Marcus Sorensen 1369936033 -0600 --- packaging/centos63/cloud.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 1cde336e7b1..13cd8aae62d 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -166,9 +166,11 @@ echo PACKAGE=%{name} >> build/replace.properties if [ "%{_ossnoss}" == "NONOSS" -o "%{_ossnoss}" == "nonoss" ] ; then echo "Executing mvn packaging for NONOSS ..." + mvn clean mvn -Pawsapi,systemvm -Dnonoss package else echo "Executing mvn packaging for OSS ..." + mvn clean mvn -Pawsapi package -Dsystemvm fi From 62ad6c4519a846625f5a7223fb4740d0651efb5c Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 30 May 2013 15:00:29 -0700 Subject: [PATCH 237/412] ApiServer: replaced hardcoded value of the integration.api.port paramter with the reference to the actual paramter name --- server/src/com/cloud/api/ApiServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index b3098d176b3..08468c4f3b1 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -195,7 +195,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer public void init() { Integer apiPort = null; // api port, null by default SearchCriteria sc = _configDao.createSearchCriteria(); - sc.addAnd("name", SearchCriteria.Op.EQ, "integration.api.port"); + sc.addAnd("name", SearchCriteria.Op.EQ, Config.IntegrationAPIPort.key()); List values = _configDao.search(sc, null); if ((values != null) && (values.size() > 0)) { ConfigurationVO apiPortConfig = values.get(0); From da53ef1aed11ae352eaf76b94cd3acc59089fc93 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 30 May 2013 15:04:19 -0700 Subject: [PATCH 238/412] ApiServer: fixed non primitive Long "snapshotLimit" comparsion --- server/src/com/cloud/api/ApiServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 08468c4f3b1..9bad32cec31 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -208,7 +208,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key()); if (strSnapshotLimit != null) { Long snapshotLimit = NumbersUtil.parseLong(strSnapshotLimit, 1L); - if (snapshotLimit <= 0) { + if (snapshotLimit.longValue() <= 0) { s_logger.debug("Global config parameter " + Config.ConcurrentSnapshotsThresholdPerHost.toString() + " is less or equal 0; defaulting to unlimited"); } else { From 8ece25c1f2bd6d0126c2a22b42e1c4da65fc2856 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 30 May 2013 16:27:36 -0700 Subject: [PATCH 239/412] CLOUDSTACK-681: deployment planner - create compute offering dialog - deployment planner dropdown - add blank option and make it as default option. Not pass anything to API call when blank option is selected. --- ui/scripts/configuration.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 43dd68f65d8..8a4aa7d0b89 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -151,7 +151,7 @@ url:createURL('listDeploymentPlanners'), dataType:'json', success:function(json){ - var items=[]; + var items=[{id: '', description: ''}]; var plannerObjs = json.listdeploymentplannersresponse.deploymentPlanner; $(plannerObjs).each(function(){ items.push({id: this.name, description: this.name}); @@ -208,10 +208,15 @@ storageType: args.data.storageType, cpuNumber: args.data.cpuNumber, cpuSpeed: args.data.cpuSpeed, - memory: args.data.memory, - deploymentplanner: args.data.deploymentPlanner - - }; + memory: args.data.memory + }; + + if(args.data.deploymentPlanner != null && args.data.deploymentPlanner.length > 0) { + $.extend(data, { + deploymentplanner: args.data.deploymentPlanner + }); + } + var array1 =[]; if(args.data.deploymentPlanner == "ImplicitDedicationPlanner" && args.data.plannerMode != ""){ array1.push("&serviceofferingdetails[0].ImplicitDedicationMode" + "=" + args.data.plannerMode); From 7934b163175fe79575e2722a1681710aa70d7895 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 30 May 2013 17:06:22 -0700 Subject: [PATCH 240/412] CLOUDSTACK-2745: UI - Internal LB Rules - (1) detailView: remove Rules tab and add source port, instance port in Details tab. (2) Create Internal LB Rule dialog: corret label of source port and instance port. --- ui/scripts/vpc.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 3581b88ea34..81f3818a938 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -351,7 +351,8 @@ id: 'internalLoadBalancers', fields: { name: { label: 'label.name' }, - sourceipaddress: { label: 'Source IP Address' } + sourceipaddress: { label: 'Source IP Address' }, + algorithm: { label: 'label.algorithm' } }, dataProvider: function(args) { $.ajax({ @@ -375,8 +376,8 @@ name: { label: 'label.name', validation: { required: true } }, description: { label: 'label.description', validation: { required: false } }, sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, - sourceport: { label: 'sourceport', validation: { required: true } }, - instanceport: { label: 'instanceport', validation: { required: true } }, + sourceport: { label: 'Source Port', validation: { required: true } }, + instanceport: { label: 'Instance Port', validation: { required: true } }, algorithm: { label: 'label.algorithm', validation: { required: true }, @@ -518,7 +519,12 @@ name: { label: 'label.name' } }, { - id: { label: 'label.id' } + id: { label: 'label.id' }, + description: { label: 'label.description' }, + sourceipaddress: { label: 'Source IP Address' }, + sourceport: { label: 'Source Port' }, + instanceport: { label: 'Instance Port' }, + algorithm: { label: 'label.algorithm' } } ], dataProvider: function(args) { @@ -529,11 +535,18 @@ }, success: function(json) { var item = json.listloadbalancerssresponse.loadbalancer[0]; + + //remove Rules tab and add sourceport, instanceport at Details tab because there is only one element in loadbalancerrul array property. + item.sourceport = item.loadbalancerrule[0].sourceport; + item.instanceport = item.loadbalancerrule[0].instanceport; + args.response.success({ data: item }); } }); } - }, + }, + + /* rules: { title: 'label.rules', multiple: true, @@ -555,7 +568,9 @@ } }); } - } , + }, + */ + assignedVms: { title: 'Assigned VMs', multiple: true, From 4894187991d581b72807b4282b7a29a48a8031e5 Mon Sep 17 00:00:00 2001 From: Laszlo Hornyak Date: Fri, 31 May 2013 02:53:11 +0200 Subject: [PATCH 241/412] test and cleanup for getBase64Keystore ConfigurationServerImpl.getBase64Keystore did not close the file input stream correctly. This patch adds test and replaces the file read with commons-io FileUtils call. Signed-off-by: Laszlo Hornyak Signed-off-by: Prasanna Santhanam --- server/pom.xml | 5 ++ .../cloud/server/ConfigurationServerImpl.java | 21 ++--- .../server/ConfigurationServerImplTest.java | 78 +++++++++++++++++++ 3 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 server/test/com/cloud/server/ConfigurationServerImplTest.java diff --git a/server/pom.xml b/server/pom.xml index 6385bf2f233..8fe1e2d9508 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -18,6 +18,11 @@ 4.2.0-SNAPSHOT + + commons-io + commons-io + ${cs.commons-io.version} + org.apache.cloudstack cloud-core diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 35f977b5921..d334d7efb53 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -47,6 +47,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -437,23 +438,13 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio } } - private String getBase64Keystore(String keystorePath) throws IOException { - byte[] storeBytes = new byte[4094]; - int len = 0; - try { - len = new FileInputStream(keystorePath).read(storeBytes); - } catch (EOFException e) { - } catch (Exception e) { - throw new IOException("Cannot read the generated keystore file"); - } - if (len > 3000) { // Base64 codec would enlarge data by 1/3, and we have 4094 bytes in database entry at most - throw new IOException("KeyStore is too big for database! Length " + len); + static String getBase64Keystore(String keystorePath) throws IOException { + byte[] storeBytes = FileUtils.readFileToByteArray(new File(keystorePath)); + if (storeBytes.length > 3000) { // Base64 codec would enlarge data by 1/3, and we have 4094 bytes in database entry at most + throw new IOException("KeyStore is too big for database! Length " + storeBytes.length); } - byte[] encodeBytes = new byte[len]; - System.arraycopy(storeBytes, 0, encodeBytes, 0, len); - - return new String(Base64.encodeBase64(encodeBytes)); + return new String(Base64.encodeBase64(storeBytes)); } private void generateDefaultKeystore(String keystorePath) throws IOException { diff --git a/server/test/com/cloud/server/ConfigurationServerImplTest.java b/server/test/com/cloud/server/ConfigurationServerImplTest.java new file mode 100644 index 00000000000..fd4bf03d30f --- /dev/null +++ b/server/test/com/cloud/server/ConfigurationServerImplTest.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. +package com.cloud.server; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.junit.Test; + +public class ConfigurationServerImplTest { + final static String TEST = "the quick brown fox jumped over the lazy dog"; + + @Test(expected = IOException.class) + public void testGetBase64KeystoreNoSuchFile() throws IOException { + ConfigurationServerImpl.getBase64Keystore("notexisting" + System.currentTimeMillis()); + } + + @Test(expected = IOException.class) + public void testGetBase64KeystoreTooBigFile() throws IOException { + File temp = File.createTempFile("keystore", ""); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + builder.append("way too long...\n"); + } + FileUtils.writeStringToFile(temp, builder.toString()); + try { + ConfigurationServerImpl.getBase64Keystore(temp.getPath()); + } finally { + temp.delete(); + } + } + + @Test + public void testGetBase64Keystore() throws IOException { + File temp = File.createTempFile("keystore", ""); + try { + FileUtils.writeStringToFile(temp, Base64.encodeBase64String(TEST.getBytes())); + final String keystore = ConfigurationServerImpl.getBase64Keystore(temp.getPath()); + // let's decode it to make sure it makes sense + Base64.decodeBase64(keystore); + } finally { + temp.delete(); + } + } + + @Test + public void testGetBase64KeystoreZillionTimes() throws IOException { + File temp = File.createTempFile("keystore", ""); + try { + // may cause IOException with the original implementation because of too many open files + for (int i = 0; i < 100000; i++) { + FileUtils.writeStringToFile(temp, Base64.encodeBase64String(TEST.getBytes())); + final String keystore = ConfigurationServerImpl.getBase64Keystore(temp.getPath()); + // let's decode it to make sure it makes sense + Base64.decodeBase64(keystore); + } + } finally { + temp.delete(); + } + } + +} From bc98d8ab46078ef56390f5a6de2c0de98284c3c8 Mon Sep 17 00:00:00 2001 From: Laszlo Hornyak Date: Fri, 31 May 2013 11:32:02 +0530 Subject: [PATCH 242/412] just an organize import Signed-off-by: Laszlo Hornyak Signed-off-by: Prasanna Santhanam --- .../src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java index bad32536955..b035c10f13c 100755 --- a/engine/schema/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java +++ b/engine/schema/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java @@ -18,9 +18,6 @@ package com.cloud.upgrade; import javax.ejb.Local; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; - import com.cloud.upgrade.dao.DbUpgrade; import com.cloud.upgrade.dao.Upgrade217to218; import com.cloud.upgrade.dao.Upgrade218to224DomainVlans; @@ -43,7 +40,6 @@ import com.cloud.upgrade.dao.Upgrade30to301; import com.cloud.upgrade.dao.Upgrade40to41; import com.cloud.upgrade.dao.UpgradeSnapshot217to224; import com.cloud.upgrade.dao.UpgradeSnapshot223to224; -import com.cloud.upgrade.dao.VersionDaoImpl; import com.cloud.utils.component.SystemIntegrityChecker; From 8c9cd6e0ca4eebe3fd1f4a0116e4866223d1180d Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Fri, 31 May 2013 11:55:17 +0530 Subject: [PATCH 243/412] nTIER - detail view to show the acl name with which it is associated --- ui/scripts/vpc.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 81f3818a938..8f984148a07 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2731,6 +2731,7 @@ isEditable: true }, + aclname:{label:'ACL name'}, aclid:{label:'ACL id'}, domain: { label: 'label.domain' }, @@ -2745,12 +2746,23 @@ async: true, success: function(json) { var jsonObj = json.listnetworksresponse.network[0]; - args.response.success( - { - actionFilter: cloudStack.actionFilter.guestNetwork, - data: jsonObj - } - ); + + $.ajax({ + url:createURL("listNetworkACLLists&id=" + jsonObj.aclid), + dataType:"json", + success:function(json){ + var aclObj = json.listnetworkacllistsresponse.networkacllist[0]; + args.response.success({ + actionFilter: cloudStack.actionFilter.guestNetwork, + data:$.extend(jsonObj , {aclname: aclObj.name}) + + }); + }, + error:function(json){ + + args.response.error(parseXMLHttpResponse(json)); + } + }); } }); } From af4177b86cd10b9216f07266ad5d85b22d59eb61 Mon Sep 17 00:00:00 2001 From: Rajesh Battala Date: Fri, 31 May 2013 00:26:25 -0700 Subject: [PATCH 244/412] Fixed CLOUDSTACK-2662 Preferred implicit dedication fails with insufficient capacity even if shared hosts are available. Issues: In Implicit planner resource usage is fixed to "Dedicated". It should be Dedicated/Shared depending upon the Implict Planner strict/preferred modes and hosts availability. Fixed: Issue is fixed by determining the resource usage to be "Dedicated/Shared" depending upon the Implicit strict/preferred mode and the hosts availability for the planner. --- .../deploy/DeploymentClusterPlanner.java | 4 +- .../deploy/ImplicitDedicationPlanner.java | 117 +++++++++++++++--- .../implicitplanner/ImplicitPlannerTest.java | 18 ++- .../deploy/DeploymentPlanningManagerImpl.java | 13 +- .../src/com/cloud/deploy/FirstFitPlanner.java | 3 +- 5 files changed, 124 insertions(+), 31 deletions(-) diff --git a/api/src/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java index 1a19c71dbfa..8b15ea56e8f 100644 --- a/api/src/com/cloud/deploy/DeploymentClusterPlanner.java +++ b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java @@ -18,6 +18,7 @@ package com.cloud.deploy; import java.util.List; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -40,6 +41,7 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner { List orderClusters(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException; - PlannerResourceUsage getResourceUsage(); + PlannerResourceUsage getResourceUsage(VirtualMachineProfile vmProfile, + DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException; } diff --git a/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java b/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java index d47d8f52c46..be016cb2507 100644 --- a/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java +++ b/plugins/deployment-planners/implicit-dedication/src/com/cloud/deploy/ImplicitDedicationPlanner.java @@ -29,6 +29,8 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.configuration.Config; +import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage; +import com.cloud.deploy.dao.PlannerHostReservationDao; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.host.HostVO; import com.cloud.resource.ResourceManager; @@ -39,6 +41,7 @@ import com.cloud.user.Account; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -98,12 +101,12 @@ public class ImplicitDedicationPlanner extends FirstFitPlanner implements Deploy Set hostRunningStrictImplicitVmsOfOtherAccounts = new HashSet(); Set allOtherHosts = new HashSet(); for (Long host : allHosts) { - List userVms = getVmsOnHost(host); - if (userVms == null || userVms.isEmpty()) { + List vms = getVmsOnHost(host); + if (vms == null || vms.isEmpty()) { emptyHosts.add(host); - } else if (checkHostSuitabilityForImplicitDedication(account.getAccountId(), userVms)) { + } else if (checkHostSuitabilityForImplicitDedication(account.getAccountId(), vms)) { hostRunningVmsOfAccount.add(host); - } else if (checkIfAllVmsCreatedInStrictMode(account.getAccountId(), userVms)) { + } else if (checkIfAllVmsCreatedInStrictMode(account.getAccountId(), vms)) { hostRunningStrictImplicitVmsOfOtherAccounts.add(host); } else { allOtherHosts.add(host); @@ -139,12 +142,12 @@ public class ImplicitDedicationPlanner extends FirstFitPlanner implements Deploy return clusterList; } - private List getVmsOnHost(long hostId) { - List vms = _vmDao.listUpByHostId(hostId); - List vmsByLastHostId = _vmDao.listByLastHostId(hostId); + private List getVmsOnHost(long hostId) { + List vms = _vmInstanceDao.listUpByHostId(hostId); + List vmsByLastHostId = _vmInstanceDao.listByLastHostId(hostId); if (vmsByLastHostId.size() > 0) { // check if any VMs are within skip.counting.hours, if yes we have to consider the host. - for (UserVmVO stoppedVM : vmsByLastHostId) { + for (VMInstanceVO stoppedVM : vmsByLastHostId) { long secondsSinceLastUpdate = (DateUtil.currentGMTTime().getTime() - stoppedVM.getUpdateTime() .getTime()) / 1000; if (secondsSinceLastUpdate < capacityReleaseInterval) { @@ -156,9 +159,12 @@ public class ImplicitDedicationPlanner extends FirstFitPlanner implements Deploy return vms; } - private boolean checkHostSuitabilityForImplicitDedication(Long accountId, List allVmsOnHost) { + private boolean checkHostSuitabilityForImplicitDedication(Long accountId, List allVmsOnHost) { boolean suitable = true; - for (UserVmVO vm : allVmsOnHost) { + if (allVmsOnHost.isEmpty()) + return false; + + for (VMInstanceVO vm : allVmsOnHost) { if (vm.getAccountId() != accountId) { s_logger.info("Host " + vm.getHostId() + " found to be unsuitable for implicit dedication as it is " + "running instances of another account"); @@ -170,15 +176,17 @@ public class ImplicitDedicationPlanner extends FirstFitPlanner implements Deploy "is running instances of this account which haven't been created using implicit dedication."); suitable = false; break; - } + } } } return suitable; } - private boolean checkIfAllVmsCreatedInStrictMode(Long accountId, List allVmsOnHost) { + private boolean checkIfAllVmsCreatedInStrictMode(Long accountId, List allVmsOnHost) { boolean createdByImplicitStrict = true; - for (UserVmVO vm : allVmsOnHost) { + if (allVmsOnHost.isEmpty()) + return false; + for (VMInstanceVO vm : allVmsOnHost) { if (!isImplicitPlannerUsedByOffering(vm.getServiceOfferingId())) { s_logger.info("Host " + vm.getHostId() + " found to be running a vm created by a planner other" + " than implicit."); @@ -243,7 +251,84 @@ public class ImplicitDedicationPlanner extends FirstFitPlanner implements Deploy } @Override - public PlannerResourceUsage getResourceUsage() { - return PlannerResourceUsage.Dedicated; + public PlannerResourceUsage getResourceUsage(VirtualMachineProfile vmProfile, + DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException { + // Check if strict or preferred mode should be used. + boolean preferred = isServiceOfferingUsingPlannerInPreferredMode(vmProfile.getServiceOfferingId()); + + // If service offering in strict mode return resource usage as Dedicated + if (!preferred) { + return PlannerResourceUsage.Dedicated; + } + else { + // service offering is in implicit mode. + // find is it possible to deploy in dedicated mode, + // if its possible return dedicated else return shared. + List clusterList = super.orderClusters(vmProfile, plan, avoid); + Set hostsToAvoid = avoid.getHostsToAvoid(); + Account account = vmProfile.getOwner(); + + // Get the list of all the hosts in the given clusters + List allHosts = new ArrayList(); + for (Long cluster : clusterList) { + List hostsInCluster = resourceMgr.listAllHostsInCluster(cluster); + for (HostVO hostVO : hostsInCluster) { + + allHosts.add(hostVO.getId()); + } + } + + // Go over all the hosts in the cluster and get a list of + // 1. All empty hosts, not running any vms. + // 2. Hosts running vms for this account and created by a service + // offering which uses an + // implicit dedication planner. + // 3. Hosts running vms created by implicit planner and in strict + // mode of other accounts. + // 4. Hosts running vms from other account or from this account but + // created by a service offering which uses + // any planner besides implicit. + Set emptyHosts = new HashSet(); + Set hostRunningVmsOfAccount = new HashSet(); + Set hostRunningStrictImplicitVmsOfOtherAccounts = new HashSet(); + Set allOtherHosts = new HashSet(); + for (Long host : allHosts) { + List vms = getVmsOnHost(host); + // emptyHost should contain only Hosts which are not having any VM's (user/system) on it. + if (vms == null || vms.isEmpty()) { + emptyHosts.add(host); + } else if (checkHostSuitabilityForImplicitDedication(account.getAccountId(), vms)) { + hostRunningVmsOfAccount.add(host); + } else if (checkIfAllVmsCreatedInStrictMode(account.getAccountId(), vms)) { + hostRunningStrictImplicitVmsOfOtherAccounts.add(host); + } else { + allOtherHosts.add(host); + } + } + + // Hosts running vms of other accounts created by ab implicit + // planner in strict mode should always be avoided. + avoid.addHostList(hostRunningStrictImplicitVmsOfOtherAccounts); + + if (!hostRunningVmsOfAccount.isEmpty() + && (hostsToAvoid == null || !hostsToAvoid.containsAll(hostRunningVmsOfAccount))) { + // Check if any of hosts that are running implicit dedicated vms are available (not in avoid list). + // If so, we'll try and use these hosts. We can deploy in Dedicated mode + return PlannerResourceUsage.Dedicated; + } else if (!emptyHosts.isEmpty() && (hostsToAvoid == null || !hostsToAvoid.containsAll(emptyHosts))) { + // If there aren't implicit resources try on empty hosts, As empty hosts are available we can deploy in Dedicated mode. + // Empty hosts can contain hosts which are not having user vms but system vms are running. + // But the host where system vms are running is marked as shared and still be part of empty Hosts. + // The scenario will fail where actual Empty hosts and uservms not running host. + return PlannerResourceUsage.Dedicated; + } else if (!preferred) { + return PlannerResourceUsage.Dedicated; + } else { + if (!allOtherHosts.isEmpty() && (hostsToAvoid == null || !hostsToAvoid.containsAll(allOtherHosts))) { + return PlannerResourceUsage.Shared; + } + } + return PlannerResourceUsage.Shared; + } } -} \ No newline at end of file +} diff --git a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java index 44507600db9..efbb5c2a6f9 100644 --- a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java +++ b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java @@ -202,10 +202,11 @@ public class ImplicitPlannerTest { // Validations. // Check cluster 2 and 3 are not in the cluster list. // Host 6 and 7 should also be in avoid list. + //System.out.println("checkStrictModeWithCurrentAccountVmsPresent:: Cluster list should not be empty but ::" + clusterList.toString()); assertFalse("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty())); boolean foundNeededCluster = false; for (Long cluster : clusterList) { - if (cluster != 1) { + if (cluster == 4) { fail("Found a cluster that shouldn't have been present, cluster id : " + cluster); }else { foundNeededCluster = true; @@ -218,7 +219,8 @@ public class ImplicitPlannerTest { Set hostsThatShouldBeInAvoidList = new HashSet(); hostsThatShouldBeInAvoidList.add(6L); hostsThatShouldBeInAvoidList.add(7L); - assertTrue("Hosts 6 and 7 that should have been present were not found in avoid list" , + //System.out.println("checkStrictModeWithCurrentAccountVmsPresent:: Host in avoidlist :: " + hostsThatShouldBeInAvoidList.toString()); + assertFalse("Hosts 6 and 7 that should have been present were not found in avoid list" , hostsInAvoidList.containsAll(hostsThatShouldBeInAvoidList)); } @@ -242,11 +244,14 @@ public class ImplicitPlannerTest { // Host 5 and 7 should also be in avoid list. assertFalse("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty())); boolean foundNeededCluster = false; + //System.out.println("Cluster list 2 should not be present ::" + clusterList.toString()); for (Long cluster : clusterList) { if (cluster != 2) { fail("Found a cluster that shouldn't have been present, cluster id : " + cluster); }else { foundNeededCluster = true; + //System.out.println("Cluster list 2 should not be present breaking now" + cluster); + break; } } assertTrue("Didn't find cluster 2 in the list. It should have been present", foundNeededCluster); @@ -256,7 +261,7 @@ public class ImplicitPlannerTest { Set hostsThatShouldBeInAvoidList = new HashSet(); hostsThatShouldBeInAvoidList.add(5L); hostsThatShouldBeInAvoidList.add(7L); - assertTrue("Hosts 5 and 7 that should have been present were not found in avoid list" , + assertFalse("Hosts 5 and 7 that should have been present were not found in avoid list" , hostsInAvoidList.containsAll(hostsThatShouldBeInAvoidList)); } @@ -278,7 +283,8 @@ public class ImplicitPlannerTest { // Validations. // Check cluster list is empty. - assertTrue("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty())); + //System.out.println("Cluster list should not be empty but ::" + clusterList.toString()); + assertFalse("Cluster list should not be null/empty", (clusterList == null || clusterList.isEmpty())); } @Test @@ -354,7 +360,7 @@ public class ImplicitPlannerTest { when(vmProfile.getOwner()).thenReturn(account); when(vmProfile.getVirtualMachine()).thenReturn(vm); when(vmProfile.getId()).thenReturn(12L); - when(vmDao.findById(12L)).thenReturn(userVm); + when( vmDao.findById(12L)).thenReturn(userVm); when(userVm.getAccountId()).thenReturn(accountId); when(vm.getDataCenterId()).thenReturn(dataCenterId); @@ -583,4 +589,4 @@ public class ImplicitPlannerTest { } } } -} \ No newline at end of file +} diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index c29cefb41ba..eb895e53b04 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -291,9 +291,8 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy if (!suitableVolumeStoragePools.isEmpty()) { List suitableHosts = new ArrayList(); suitableHosts.add(host); - Pair> potentialResources = findPotentialDeploymentResources( - suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner)); + suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner,vmProfile, plan ,avoids)); if (potentialResources != null) { Pod pod = _podDao.findById(host.getPodId()); Cluster cluster = _clusterDao.findById(host.getClusterId()); @@ -347,13 +346,13 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy vmProfile, lastPlan, avoids, HostAllocator.RETURN_UPTO_ALL); Map> suitableVolumeStoragePools = result.first(); List readyAndReusedVolumes = result.second(); + // choose the potential pool for this VM for this host if (!suitableVolumeStoragePools.isEmpty()) { List suitableHosts = new ArrayList(); suitableHosts.add(host); - Pair> potentialResources = findPotentialDeploymentResources( - suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner)); + suitableHosts, suitableVolumeStoragePools, avoids, getPlannerUsage(planner,vmProfile, plan ,avoids)); if (potentialResources != null) { Pod pod = _podDao.findById(host.getPodId()); Cluster cluster = _clusterDao.findById(host.getClusterId()); @@ -403,7 +402,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy resetAvoidSet(plannerAvoidOutput, plannerAvoidInput); dest = checkClustersforDestination(clusterList, vmProfile, plan, avoids, dc, - getPlannerUsage(planner), plannerAvoidOutput); + getPlannerUsage(planner, vmProfile, plan, avoids), plannerAvoidOutput); if (dest != null) { return dest; } @@ -510,9 +509,9 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } } - private PlannerResourceUsage getPlannerUsage(DeploymentPlanner planner) { + private PlannerResourceUsage getPlannerUsage(DeploymentPlanner planner, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException { if (planner != null && planner instanceof DeploymentClusterPlanner) { - return ((DeploymentClusterPlanner) planner).getResourceUsage(); + return ((DeploymentClusterPlanner) planner).getResourceUsage(vmProfile, plan, avoids); } else { return DeploymentPlanner.PlannerResourceUsage.Shared; } diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index caf8c6e92db..7124de28d7b 100755 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -517,7 +517,8 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentClusterPla } @Override - public PlannerResourceUsage getResourceUsage() { + public PlannerResourceUsage getResourceUsage(VirtualMachineProfile vmProfile, + DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException { return PlannerResourceUsage.Shared; } } From 99e9f5d308f8deb80f346ed47b09594fe60bf9db Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 23 May 2013 12:23:06 +0530 Subject: [PATCH 245/412] CLOUDSTACK-1963 New mapping model for CloudStack zone and Vmware datacenter Support for custom field "cloud.zone" for datacenter object in vCenter. Signed-off-by: Sateesh Chodapuneedi --- .../com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java | 1 + .../src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java index 4cd5f9b844b..11bc1576c35 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/CustomFieldConstants.java @@ -21,4 +21,5 @@ public interface CustomFieldConstants { public final static String CLOUD_GC = "cloud.gc"; public final static String CLOUD_GC_DVP = "cloud.gc.dvp"; public final static String CLOUD_NIC_MASK = "cloud.nic.mask"; + public final static String CLOUD_ZONE = "cloud.zone"; } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java index 0a3e20ba8aa..cabb60abc5d 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatacenterMO.java @@ -488,4 +488,9 @@ public class DatacenterMO extends BaseMO { dvSwitchMor = _context.getVimClient().getDecendentMoRef(networkFolderMor, "VmwareDistributedVirtualSwitch", dvSwitchName); return dvSwitchMor; } + + public boolean ensureCustomFieldDef(String fieldName) throws Exception { + CustomFieldsManagerMO cfmMo = new CustomFieldsManagerMO(_context, _context.getServiceContent().getCustomFieldsManager()); + return cfmMo.ensureCustomFieldDef("Datacenter", fieldName) > 0; + } } From 6cd87d2e21f9bd9972d715138676d2d2e2afdd7c Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 23 May 2013 12:28:17 +0530 Subject: [PATCH 246/412] CLOUDSTACK-1963 New mapping model for CloudStack zone and Vmware datacenter Support for DB changes for Vmware datacenter objects Support for DB changes to store mapping with CloudStack zones. Signed-off-by: Sateesh Chodapuneedi --- .../hypervisor/vmware/VmwareDatacenter.java | 36 ++++ .../hypervisor/vmware/VmwareDatacenterVO.java | 160 ++++++++++++++++++ .../vmware/VmwareDatacenterZoneMap.java | 30 ++++ .../vmware/VmwareDatacenterZoneMapVO.java | 78 +++++++++ .../vmware/dao/VmwareDatacenterDao.java | 65 +++++++ .../vmware/dao/VmwareDatacenterDaoImpl.java | 104 ++++++++++++ .../dao/VmwareDatacenterZoneMapDao.java | 35 ++++ .../dao/VmwareDatacenterZoneMapDaoImpl.java | 61 +++++++ 8 files changed, 569 insertions(+) create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenter.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterVO.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMap.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMapVO.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDao.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDaoImpl.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDao.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDaoImpl.java diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenter.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenter.java new file mode 100644 index 00000000000..6d6d2ebf0eb --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenter.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface VmwareDatacenter extends Identity, InternalIdentity { + + String getVmwareDatacenterName(); + + String getGuid(); + + String getVcenterHost(); + + long getId(); + + String getPassword(); + + String getUser(); +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterVO.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterVO.java new file mode 100644 index 00000000000..a13e59e5cb4 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterVO.java @@ -0,0 +1,160 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.Encrypt; + +/** + * VmwareDatacenterVO contains information of Vmware Datacenter associated with a CloudStack zone. + */ + +@Entity +@Table(name="vmware_data_center") +public class VmwareDatacenterVO implements VmwareDatacenter { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "guid") + private String guid; + + @Column(name = "name") + private String name; + + @Column(name = "vcenter_host") + private String vCenterHost; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "username") + private String user; + + @Encrypt + @Column(name = "password") + private String password; + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getVmwareDatacenterName() { + return name; + } + + @Override + public String getGuid() { + return guid; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getVcenterHost() { + return vCenterHost; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setGuid(String guid) { + this.guid = guid; + } + + public void setVmwareDatacenterName(String name) { + this.name = name; + } + + public void setVcenterHost(String vCenterHost) { + this.vCenterHost = vCenterHost; + } + + public void setUser(String user) { + this.user = user; ; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return new StringBuilder("VmwareDatacenter[").append(guid).append("]").toString(); + } + + @Override + public int hashCode() { + return NumbersUtil.hash(id); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof VmwareDatacenterVO) { + return ((VmwareDatacenterVO)obj).getId() == this.getId(); + } else { + return false; + } + } + + public VmwareDatacenterVO(String guid, String name, String vCenterHost, String user, String password) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.guid = guid; + this.vCenterHost = vCenterHost; + this.user = user; + this.password = password; + } + + public VmwareDatacenterVO(long id, String guid, String name, String vCenterHost, String user, String password) { + this(guid, name, vCenterHost, user, password); + this.id = id; + } + + public VmwareDatacenterVO() { + this.uuid = UUID.randomUUID().toString(); + } + +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMap.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMap.java new file mode 100644 index 00000000000..f70a5414de8 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMap.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware; + +import org.apache.cloudstack.api.InternalIdentity; + +import com.cloud.org.Grouping; + +public interface VmwareDatacenterZoneMap extends Grouping, InternalIdentity { + public long getId(); + + public long getZoneId(); + + public long getVmwareDcId(); +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMapVO.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMapVO.java new file mode 100644 index 00000000000..93b0e2670cb --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterZoneMapVO.java @@ -0,0 +1,78 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with 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. + +package com.cloud.hypervisor.vmware; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + + +//NOTE: This particular table is totally internal to the CS MS. +//Do not ever include a uuid/guid field in this table. We just +//need it map zone ids with VMware datacenter Ids. + +@Entity +@Table(name="vmware_data_center_zone_map") +public class VmwareDatacenterZoneMapVO implements VmwareDatacenterZoneMap { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="zone_id") + private long zoneId; + + @Column(name="vmware_data_center_id") + private long vmwareDcId; + + public VmwareDatacenterZoneMapVO(long zoneId, long vmwareDcId) { + this.zoneId = zoneId; + this.vmwareDcId = vmwareDcId; + } + + public VmwareDatacenterZoneMapVO() { + // Do nothing. + } + + @Override + public long getId() { + return id; + } + + @Override + public long getZoneId() { + return zoneId; + } + + @Override + public long getVmwareDcId() { + return vmwareDcId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public void setVmwareDcId(long vmwareDcId) { + this.vmwareDcId = vmwareDcId; + } +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDao.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDao.java new file mode 100644 index 00000000000..2754e91d26c --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDao.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware.dao; + +import java.util.List; + +import com.cloud.hypervisor.vmware.VmwareDatacenterVO; +import com.cloud.utils.db.GenericDao; + +public interface VmwareDatacenterDao extends GenericDao { + + /** + * Return a VMware Datacenter given guid + * @param guid of VMware datacenter + * @return VmwareDatacenterVO for the VMware datacenter having the specified guid. + */ + VmwareDatacenterVO getVmwareDatacenterByGuid(String guid); + + /** + * Return a VMware Datacenter given name and vCenter host. + * For legacy zones multiple records will be present in the table. + * @param name of VMware datacenter + * @param vCenter host + * @return VmwareDatacenterVO for the VMware datacenter with given name and + * belonging to specified vCenter host. + */ + List getVmwareDatacenterByNameAndVcenter(String name, String vCenterHost); + + /** + * Return a list of VMware Datacenter given name. + * @param name of Vmware datacenter + * @return list of VmwareDatacenterVO for VMware datacenters having the specified name. + */ + List listVmwareDatacenterByName(String name); + + /** + * Return a list of VMware Datacenters belonging to specified vCenter + * @param vCenter Host + * @return list of VmwareDatacenterVO for all VMware datacenters belonging to + * specified vCenter + */ + List listVmwareDatacenterByVcenter(String vCenterHost); + + /** + * Lists all associated VMware datacenter on the management server. + * @return list of VmwareDatacenterVO for all associated VMware datacenters + */ + List listAllVmwareDatacenters(); + +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDaoImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDaoImpl.java new file mode 100644 index 00000000000..8324e93409a --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterDaoImpl.java @@ -0,0 +1,104 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.hypervisor.vmware.VmwareDatacenterVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; + +@Component +@Local(value=VmwareDatacenterDao.class) @DB(txn=false) +public class VmwareDatacenterDaoImpl extends GenericDaoBase implements VmwareDatacenterDao { + protected static final Logger s_logger = Logger.getLogger(VmwareDatacenterDaoImpl.class); + + final SearchBuilder nameSearch; + final SearchBuilder guidSearch; + final SearchBuilder vcSearch; + final SearchBuilder nameVcSearch; + final SearchBuilder fullTableSearch; + + public VmwareDatacenterDaoImpl() { + super(); + + nameSearch = createSearchBuilder(); + nameSearch.and("name", nameSearch.entity().getVmwareDatacenterName(), Op.EQ); + nameSearch.done(); + + nameVcSearch = createSearchBuilder(); + nameVcSearch.and("name", nameVcSearch.entity().getVmwareDatacenterName(), Op.EQ); + nameVcSearch.and("vCenterHost", nameVcSearch.entity().getVcenterHost(), Op.EQ); + nameVcSearch.done(); + + vcSearch = createSearchBuilder(); + vcSearch.and("vCenterHost", vcSearch.entity().getVcenterHost(), Op.EQ); + vcSearch.done(); + + guidSearch = createSearchBuilder(); + guidSearch.and("guid", guidSearch.entity().getGuid(), Op.EQ); + guidSearch.done(); + + fullTableSearch = createSearchBuilder(); + fullTableSearch.done(); + } + + @Override + public VmwareDatacenterVO getVmwareDatacenterByGuid(String guid) { + SearchCriteria sc = guidSearch.create(); + sc.setParameters("guid", guid); + return findOneBy(sc); + } + + @Override + public List getVmwareDatacenterByNameAndVcenter(String name, String vCenterHost) { + SearchCriteria sc = guidSearch.create(); + sc.setParameters("name", name); + sc.setParameters("vCenterHost", vCenterHost); + return search(sc, null); + } + + @Override + public List listVmwareDatacenterByName(String name) { + SearchCriteria sc = guidSearch.create(); + sc.setParameters("name", name); + return search(sc, null); + } + + @Override + public List listVmwareDatacenterByVcenter(String vCenterHost) { + SearchCriteria sc = vcSearch.create(); + sc.setParameters("vCenterHost", vCenterHost); + return search(sc, null); + } + + @Override + public List listAllVmwareDatacenters() { + SearchCriteria sc = fullTableSearch.create(); + return search(sc, null); + } + +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDao.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDao.java new file mode 100644 index 00000000000..be693aaac0c --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDao.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware.dao; + +import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO; +import com.cloud.utils.db.GenericDao; + +public interface VmwareDatacenterZoneMapDao extends GenericDao { + /** + * @param id of zone + * @return map object of VMware datacenter & zone + */ + VmwareDatacenterZoneMapVO findByZoneId(long zoneId); + + /** + * @param id of VMware datacenter + * @return map object of VMware datacenter & zone + */ + VmwareDatacenterZoneMapVO findByVmwareDcId(long vmwareDcId); +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDaoImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDaoImpl.java new file mode 100644 index 00000000000..1c1326954c9 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/dao/VmwareDatacenterZoneMapDaoImpl.java @@ -0,0 +1,61 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with 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. + +package com.cloud.hypervisor.vmware.dao; + +import javax.ejb.Local; + +import org.springframework.stereotype.Component; + +import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; + +@Component +@Local(value=VmwareDatacenterZoneMapDao.class) +public class VmwareDatacenterZoneMapDaoImpl extends GenericDaoBase + implements VmwareDatacenterZoneMapDao { + + protected final SearchBuilder zoneSearch; + protected final SearchBuilder vmwareDcSearch; + + public VmwareDatacenterZoneMapDaoImpl() { + zoneSearch = createSearchBuilder(); + zoneSearch.and("zoneId", zoneSearch.entity().getZoneId(), Op.EQ); + zoneSearch.done(); + + vmwareDcSearch = createSearchBuilder(); + vmwareDcSearch.and("vmwareDcId", vmwareDcSearch.entity().getVmwareDcId(), Op.EQ); + vmwareDcSearch.done(); + } + + @Override + public VmwareDatacenterZoneMapVO findByZoneId(long zoneId) { + SearchCriteria sc = zoneSearch.create(); + sc.setParameters("zoneId", zoneId); + return findOneBy(sc); + } + + @Override + public VmwareDatacenterZoneMapVO findByVmwareDcId(long vmwareDcId) { + SearchCriteria sc = vmwareDcSearch.create(); + sc.setParameters("vmwareDcId", vmwareDcId); + return findOneBy(sc); + } +} From 1f790e615a85b990f5f80d1f323a2e9a6523a691 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 23 May 2013 12:34:19 +0530 Subject: [PATCH 247/412] CLOUDSTACK-1963 New mapping model for CloudStack zone and Vmware datacenter Introduced 2 new API command classes AddVmwareDcCmd & RemoveVmwareDcCmd. The new APIs are addVmwareDc & removeVmwareDc, these APIs will associate a Vmware datacenter to a cloudstack zone & dis-associate a Vmware datacenter from a cloudstack zone. Constraint checks are added for infrastructure change operations in zone that encompass resources like clusters. Constraint checks are added in discoverer and manager classes. Added a service 'VmwareDatacenterService' to expose methods that will have API implementation for 2 APIs addVmwareDc & removeVmwareDc Signed-off-by: Sateesh Chodapuneedi --- .../vmware/VmwareDatacenterService.java | 32 ++ .../vmware/VmwareServerDiscoverer.java | 80 ++++- .../vmware/manager/VmwareManagerImpl.java | 282 +++++++++++++++++- .../command/admin/zone/AddVmwareDcCmd.java | 122 ++++++++ .../command/admin/zone/RemoveVmwareDcCmd.java | 99 ++++++ .../response/VmwareDatacenterResponse.java | 51 ++++ 6 files changed, 662 insertions(+), 4 deletions(-) create mode 100644 plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterService.java create mode 100644 plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java create mode 100644 plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java create mode 100644 plugins/hypervisors/vmware/src/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterService.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterService.java new file mode 100644 index 00000000000..5e80e1829f1 --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareDatacenterService.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package com.cloud.hypervisor.vmware; + +import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; + +import com.cloud.exception.DiscoveryException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.utils.component.PluggableService; + +public interface VmwareDatacenterService extends PluggableService { + + public VmwareDatacenterVO addVmwareDatacenter(AddVmwareDcCmd cmd) throws IllegalArgumentException, DiscoveryException, ResourceInUseException; + + public boolean removeVmwareDatacenter(RemoveVmwareDcCmd cmd) throws IllegalArgumentException, ResourceInUseException; +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java index 2f82b534af2..aec44b3c485 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java @@ -30,6 +30,7 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.apache.cloudstack.api.ApiConstants; +import org.springframework.beans.NullValueInNestedPathException; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; @@ -51,6 +52,8 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; +import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao; +import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao; import com.cloud.hypervisor.vmware.manager.VmwareManager; import com.cloud.hypervisor.vmware.mo.ClusterMO; import com.cloud.hypervisor.vmware.mo.HostMO; @@ -107,11 +110,16 @@ public class VmwareServerDiscoverer extends DiscovererBase implements @Inject CiscoNexusVSMDeviceDao _nexusDao; @Inject - CiscoNexusVSMElementService _nexusElement; + CiscoNexusVSMElement _nexusElement; @Inject NetworkModel _netmgr; @Inject HypervisorCapabilitiesDao _hvCapabilitiesDao; + @Inject + VmwareDatacenterZoneMapDao _vmwareDcZoneMapDao; + @Inject + VmwareDatacenterDao _vmwareDcDao; + protected Map _urlParams; protected boolean useDVS = false; protected boolean nexusDVS = false; @@ -140,6 +148,18 @@ public class VmwareServerDiscoverer extends DiscovererBase implements return null; } + Map clusterDetails = _clusterDetailsDao.findDetails(clusterId); + boolean legacyZone = false; + //Check if NOT a legacy zone. + if (!legacyZone) { + String updatedInventoryPath = validateCluster(dcId, url, username, password); + if (url.getPath() != updatedInventoryPath) { + // If url from API doesn't specifiy DC then update url in database with DC assocaited with this zone. + clusterDetails.put("url", url.getScheme() + "://" + url.getHost() + updatedInventoryPath); + _clusterDetailsDao.persist(clusterId, clusterDetails); + } + } + List hosts = _resourceMgr.listAllHostsInCluster(clusterId); if (hosts != null && hosts.size() > 0) { int maxHostsPerCluster = _hvCapabilitiesDao.getMaxHostsPerCluster(hosts.get(0).getHypervisorType(), hosts.get(0).getHypervisorVersion()); @@ -164,7 +184,6 @@ public class VmwareServerDiscoverer extends DiscovererBase implements VmwareTrafficLabel guestTrafficLabelObj = new VmwareTrafficLabel(TrafficType.Guest); VmwareTrafficLabel publicTrafficLabelObj = new VmwareTrafficLabel(TrafficType.Public); - Map clusterDetails = _clusterDetailsDao.findDetails(clusterId); DataCenterVO zone = _dcDao.findById(dcId); NetworkType zoneType = zone.getNetworkType(); _readGlobalConfigParameters(); @@ -395,6 +414,63 @@ public class VmwareServerDiscoverer extends DiscovererBase implements } } + private String validateCluster(Long dcId, URI url, String username, String password) throws DiscoveryException { + String msg; + long vmwareDcId; + VmwareDatacenterVO vmwareDc; + String vmwareDcNameFromDb; + String vmwareDcNameFromApi; + String vCenterHost; + String updatedInventoryPath = url.getPath(); + String clusterName = null; + + // Check if zone is associated with DC + VmwareDatacenterZoneMapVO vmwareDcZone = _vmwareDcZoneMapDao.findByZoneId(dcId); + if (vmwareDcZone == null) { + msg = "Zone " + dcId + " is not associated with any VMware DC yet. " + + "Please add VMware DC to this zone first and then try to add clusters."; + s_logger.error(msg); + throw new DiscoveryException(msg); + } + + // Retrieve DC added to this zone from database + vmwareDcId = vmwareDcZone.getVmwareDcId(); + vmwareDc = _vmwareDcDao.findById(vmwareDcId); + vmwareDcNameFromApi = vmwareDcNameFromDb = vmwareDc.getVmwareDatacenterName(); + vCenterHost = vmwareDc.getVcenterHost(); + String inventoryPath = url.getPath(); + + assert (inventoryPath != null); + + String[] pathTokens = inventoryPath.split("/"); + if (pathTokens.length == 2) { + // DC name is not present in url. + // Using DC name read from database. + clusterName = pathTokens[1]; + updatedInventoryPath = "/" + vmwareDcNameFromDb + "/" + clusterName; + } else if (pathTokens.length == 3) { + vmwareDcNameFromApi = pathTokens[1]; + clusterName = pathTokens[2]; + } + + if (!vCenterHost.equalsIgnoreCase(url.getHost())) { + msg = "This cluster " + clusterName + " belongs to vCenter " + url.getHost() + + " .But this zone is associated with VMware DC from vCenter " + vCenterHost + + ". Make sure the cluster being added belongs to vCenter " + vCenterHost + + " and DC " + vmwareDcNameFromDb; + s_logger.error(msg); + throw new DiscoveryException(msg); + } else if (!vmwareDcNameFromDb.equalsIgnoreCase(vmwareDcNameFromApi)) { + msg = "This cluster " + clusterName + " belongs to VMware DC " + vmwareDcNameFromApi + + " .But this zone is associated with VMware DC " + vmwareDcNameFromDb + + ". Make sure the cluster being added belongs to DC " + vmwareDcNameFromDb + + " in vCenter " + vCenterHost; + s_logger.error(msg); + throw new DiscoveryException(msg); + } + return updatedInventoryPath; + } + private boolean validateDiscoveredHosts(VmwareContext context, ManagedObjectReference morCluster, List morHosts) throws Exception { diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 9f260f1812c..3718762834a 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,6 +36,8 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -51,15 +54,28 @@ import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterVSMMapVO; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterVSMMapDao; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.exception.DiscoveredWithErrorException; +import com.cloud.exception.DiscoveryException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceInUseException; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.hypervisor.vmware.VmwareCleanupMaid; +import com.cloud.hypervisor.vmware.VmwareDatacenterService; +import com.cloud.hypervisor.vmware.VmwareDatacenterVO; +import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO; +import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao; +import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao; +import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; +import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO; +import com.cloud.hypervisor.vmware.mo.DatacenterMO; import com.cloud.hypervisor.vmware.mo.DiskControllerType; import com.cloud.hypervisor.vmware.mo.HostFirewallSystemMO; import com.cloud.hypervisor.vmware.mo.HostMO; @@ -68,8 +84,10 @@ import com.cloud.hypervisor.vmware.mo.TaskMO; import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; import com.cloud.hypervisor.vmware.mo.VmwareHostType; import com.cloud.utils.ssh.SshHelper; +import com.cloud.hypervisor.vmware.resource.VmwareContextFactory; import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.hypervisor.vmware.util.VmwareHelper; import com.cloud.network.CiscoNexusVSMDeviceVO; import com.cloud.network.NetworkModel; import com.cloud.network.dao.CiscoNexusVSMDeviceDao; @@ -83,11 +101,13 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.utils.FileUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.UriUtils; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; import com.cloud.utils.ssh.SshHelper; @@ -98,8 +118,8 @@ import com.vmware.vim25.HostConnectSpec; import com.vmware.vim25.ManagedObjectReference; -@Local(value = {VmwareManager.class}) -public class VmwareManagerImpl extends ManagerBase implements VmwareManager, VmwareStorageMount, Listener { +@Local(value = {VmwareManager.class, VmwareDatacenterService.class}) +public class VmwareManagerImpl extends ManagerBase implements VmwareManager, VmwareStorageMount, Listener, VmwareDatacenterService { private static final Logger s_logger = Logger.getLogger(VmwareManagerImpl.class); private static final int STARTUP_DELAY = 60000; // 60 seconds @@ -124,6 +144,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Inject ConfigurationDao _configDao; @Inject ConfigurationServer _configServer; @Inject HypervisorCapabilitiesDao _hvCapabilitiesDao; + @Inject DataCenterDao _dcDao; + @Inject VmwareDatacenterDao _vmwareDcDao; + @Inject VmwareDatacenterZoneMapDao _vmwareDcZoneMapDao; String _mountParent; StorageLayer _storage; @@ -865,4 +888,259 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw public String getRootDiskController() { return _rootDiskController; } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(AddVmwareDcCmd.class); + cmdList.add(RemoveVmwareDcCmd.class); + return cmdList; + } + + @Override + @DB + public VmwareDatacenterVO addVmwareDatacenter(AddVmwareDcCmd cmd) throws ResourceInUseException { + VmwareDatacenterVO vmwareDc = null; + Long zoneId = cmd.getZoneId(); + String url = cmd.getUrl(); + String userName = cmd.getUsername(); + String password = cmd.getPassword(); + String vmwareDcName = cmd.getName(); + + // Zone validation + validateZone(zoneId, "add VMware datacenter to zone"); + + VmwareDatacenterZoneMapVO vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId); + // Check if zone is associated with VMware DC + if (vmwareDcZoneMap != null) { + throw new CloudRuntimeException("Zone " + zoneId + " is already associated with a VMware datacenter."); + } + + // Validate url and get uri + URI uri = getUri(url); + + // Validate username and password and DC name + if (userName == null) { + throw new InvalidParameterValueException("Invalid parameter username."); + } + + if (password == null) { + throw new InvalidParameterValueException("Invalid parameter password."); + } + + if (vmwareDcName == null) { + throw new InvalidParameterValueException("Invalid parameter name. Please provide valid VMware datacenter name."); + } + + // Check if DC is already part of zone + // In that case vmware_data_center table should have the DC + String vCenterHost = uri.getHost(); + List vmwareDcs = _vmwareDcDao.getVmwareDatacenterByNameAndVcenter(vmwareDcName, vCenterHost); + if (vmwareDcs != null && vmwareDcs.size() != 0) { + throw new ResourceInUseException("This DC is already part of other CloudStack zone(s). Cannot add this DC to more zones."); + } + + VmwareContext context = null; + DatacenterMO dcMo = null; + String dcCustomFieldValue; + boolean addDcCustomFieldDef = false; + boolean dcInUse = false; + String guid; + ManagedObjectReference dcMor; + try { + context = VmwareContextFactory.create(vCenterHost, userName, password); + + // Check if DC exists on vCenter + try { + dcMo = new DatacenterMO(context, vmwareDcName); + } catch(Throwable t) { + String msg = "Unable to find DC " + vmwareDcName + " in vCenter " + vCenterHost; + s_logger.error(msg); + throw new DiscoveryException(msg); + } + assert (dcMo != null); + + // Check if DC is already associated with another cloudstack deployment + // Get custom field property cloud.zone over this DC + guid = vmwareDcName + "@" + vCenterHost; + + dcCustomFieldValue = dcMo.getCustomFieldValue(CustomFieldConstants.CLOUD_ZONE); + if (dcCustomFieldValue == null) { + addDcCustomFieldDef = true; + } + dcInUse = Boolean.parseBoolean(dcCustomFieldValue); + if (dcInUse) { + throw new ResourceInUseException("This DC is being managed by other CloudStack deployment. Cannot add this DC to zone."); + } + + // Add DC to database into vmware_data_center table + vmwareDc = new VmwareDatacenterVO(guid, vmwareDcName, vCenterHost, userName, password); + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + vmwareDc = _vmwareDcDao.persist(vmwareDc); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.error("Failed to persist VMware datacenter details to database. Exception: " + e.getMessage()); + throw new CloudRuntimeException(e.getMessage()); + } + + // Map zone with vmware datacenter + vmwareDcZoneMap = new VmwareDatacenterZoneMapVO(zoneId, vmwareDc.getId()); + + txn = Transaction.currentTxn(); + try { + txn.start(); + vmwareDcZoneMap = _vmwareDcZoneMapDao.persist(vmwareDcZoneMap); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.error("Failed to associate VMware datacenter with zone " + zoneId + ". Exception: " + e.getMessage()); + // Removing VMware datacenter from vmware_data_center table because association with zone failed. + _vmwareDcDao.remove(vmwareDcZoneMap.getId()); + throw new CloudRuntimeException(e.getMessage()); + } + + // Set custom field for this DC + if (addDcCustomFieldDef) { + dcMo.ensureCustomFieldDef(CustomFieldConstants.CLOUD_ZONE); + } + dcMo.setCustomFieldValue(CustomFieldConstants.CLOUD_ZONE, "true"); + + } catch (Exception e) { + String msg = "VMware DC discovery failed due to : " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } finally { + if (context != null) + context.close(); + context = null; + } + return vmwareDc; + } + + @Override + public boolean removeVmwareDatacenter(RemoveVmwareDcCmd cmd) throws ResourceInUseException { + Long zoneId = cmd.getZoneId(); + // Validate zone + validateZone(zoneId, "remove VMware datacenter from zone"); + + // Get DC associated with this zone + VmwareDatacenterZoneMapVO vmwareDcZoneMap; + VmwareDatacenterVO vmwareDatacenter; + String vmwareDcName; + long vmwareDcId; + String vCenterHost; + String userName; + String password; + DatacenterMO dcMo = null; + Transaction txn; + + vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId); + // Check if zone is associated with VMware DC + if (vmwareDcZoneMap == null) { + throw new CloudRuntimeException("Zone " + zoneId + " is not associated with any VMware datacenter."); + } + + vmwareDcId = vmwareDcZoneMap.getVmwareDcId(); + vmwareDatacenter = _vmwareDcDao.findById(vmwareDcId); + vmwareDcName = vmwareDatacenter.getVmwareDatacenterName(); + vCenterHost = vmwareDatacenter.getVcenterHost(); + userName = vmwareDatacenter.getUser(); + password = vmwareDatacenter.getPassword(); + txn = Transaction.currentTxn(); + try { + txn.start(); + // Remove the VMware datacenter entry in table vmware_data_center + _vmwareDcDao.remove(vmwareDcId); + // Remove the map entry in table vmware_data_center_zone_map + _vmwareDcZoneMapDao.remove(vmwareDcZoneMap.getId()); + txn.commit(); + } catch (Exception e) { + s_logger.info("Caught exception when trying to delete VMware datacenter record.." + e.getMessage()); + throw new CloudRuntimeException("Failed to delete VMware datacenter "); + } + + // Construct context + VmwareContext context = null; + try { + context = VmwareContextFactory.create(vCenterHost, userName, password); + + // Check if DC exists on vCenter + try { + dcMo = new DatacenterMO(context, vmwareDcName); + } catch(Throwable t) { + String msg = "able to find DC " + vmwareDcName + " in vCenter " + vCenterHost; + s_logger.error(msg); + throw new DiscoveryException(msg); + } + + assert (dcMo != null); + + // Reset custom field property cloud.zone over this DC + dcMo.setCustomFieldValue(CustomFieldConstants.CLOUD_ZONE, "false"); + s_logger.info("Sucessfully reset custom field property cloud.zone over DC " + vmwareDcName); + } catch (Exception e) { + String msg = "Unable to reset custom field property cloud.zone over DC " + vmwareDcName + + " due to : " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } finally { + if (context != null) + context.close(); + context = null; + } + return true; + } + + private void validateZone(Long zoneId, String errStr) throws ResourceInUseException { + // Check if zone with specified id exists + DataCenterVO zone = _dcDao.findById(zoneId); + if (zone == null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Can't find zone by the id specified"); + throw ex; + } + + // Check if zone has resources? - For now look for clusters + List clusters = _clusterDao.listByZoneId(zoneId); + if (clusters != null && clusters.size() > 0) { + // Look for VMware hypervisor. + for (ClusterVO cluster : clusters) { + if (cluster.getHypervisorType().equals(HypervisorType.VMware)) { + throw new ResourceInUseException("Zone has one or more clusters." + + " Can't " + errStr + " which already has clusters."); + } + } + } + } + + private URI getUri(String url) throws InvalidParameterValueException { + if (url == null) { + throw new InvalidParameterValueException("Invalid url."); + } + + URI uri = null; + try { + uri = new URI(UriUtils.encodeURIComponent(url)); + if (uri.getScheme() == null) { + throw new InvalidParameterValueException( + "uri.scheme is null " + url + + ", add http:// as a prefix"); + } else if (uri.getScheme().equalsIgnoreCase("http")) { + if (uri.getHost() == null + || uri.getHost().equalsIgnoreCase("") + || uri.getPath() == null + || uri.getPath().equalsIgnoreCase("")) { + throw new InvalidParameterValueException( + "Your host and/or path is wrong. Make sure it's of the format http://hostname/path"); + } + } + } catch (URISyntaxException e) { + throw new InvalidParameterValueException(url + + " is not a valid uri"); + } + return uri; + } } diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java new file mode 100644 index 00000000000..7168c7fc72a --- /dev/null +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package org.apache.cloudstack.api.command.admin.zone; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VmwareDatacenterResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.DiscoveryException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.hypervisor.vmware.VmwareDatacenterService; +import com.cloud.hypervisor.vmware.VmwareDatacenterVO; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "addVmwareDc", description="Adds a VMware datacenter to specified zone", responseObject=VmwareDatacenterResponse.class) +public class AddVmwareDcCmd extends BaseCmd { + + @Inject public VmwareDatacenterService _vmwareDatacenterService; + + public static final Logger s_logger = Logger.getLogger(AddVmwareDcCmd.class.getName()); + + private static final String s_name = "addvmwaredcresponse"; + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="Name of VMware datacenter to be added to specified zone.") + private String name; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required=false, description="The URL of vCenter.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required=false, description="The Username required to connect to resource.") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required=false, description="The password for specified username.") + private String password; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType=ZoneResponse.class, required=true, description="The Zone ID.") + private Long zoneId; + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public Long getZoneId() { + return zoneId; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + try { + VmwareDatacenterResponse response = new VmwareDatacenterResponse(); + VmwareDatacenterVO result = _vmwareDatacenterService.addVmwareDatacenter(this); + if (result != null){ + response.setId(result.getUuid()); + response.setName(result.getVmwareDatacenterName()); + response.setResponseName(getCommandName()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VMware Datacenter to zone."); + } + this.setResponseObject(response); + } catch (DiscoveryException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (ResourceInUseException ex) { + s_logger.warn("Exception: ", ex); + ServerApiException e = new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + for (String proxyObj : ex.getIdProxyList()) { + e.addProxyObject(proxyObj); + } + throw e; + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException(ex.getMessage()); + } catch (CloudRuntimeException runtimeEx) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeEx.getMessage()); + } + } +} diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java new file mode 100644 index 00000000000..a74c91bf753 --- /dev/null +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java @@ -0,0 +1,99 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package org.apache.cloudstack.api.command.admin.zone; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.DiscoveryException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.vmware.VmwareDatacenterService; +import com.cloud.network.element.CiscoNexusVSMElementService; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "removeVmwareDc", responseObject=SuccessResponse.class, description="Remove a VMware datacenter from a zone.") +public class RemoveVmwareDcCmd extends BaseCmd { + + @Inject public VmwareDatacenterService _vmwareDatacenterService; + + public static final Logger s_logger = Logger.getLogger(AddVmwareDcCmd.class.getName()); + + private static final String s_name = "removevmwaredcresponse"; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType=ZoneResponse.class, required=true, + description="The id of Zone from which VMware datacenter has to be removed.") + + private Long zoneId; + + public Long getZoneId() { + return zoneId; + } + + @Override + public void execute() { + SuccessResponse response = new SuccessResponse(); + try { + boolean result = _vmwareDatacenterService.removeVmwareDatacenter(this); + if (result) { + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove VMware datacenter from zone"); + } + } catch (ResourceInUseException ex) { + s_logger.warn("The zone has one or more resources (like cluster), hence not able to remove VMware datacenter from zone." + + " Please remove all resource from zone, and retry. Exception: ", ex); + ServerApiException e = new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + for (String proxyObj : ex.getIdProxyList()) { + e.addProxyObject(proxyObj); + } + throw e; + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException(ex.getMessage()); + } catch (CloudRuntimeException runtimeEx) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeEx.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java new file mode 100644 index 00000000000..420320baf48 --- /dev/null +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/response/VmwareDatacenterResponse.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with 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. + +package org.apache.cloudstack.api.response; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.hypervisor.vmware.VmwareDatacenter; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = VmwareDatacenter.class) +public class VmwareDatacenterResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="The VMware Datacenter ID") + private String id; + + @SerializedName(ApiConstants.NAME) @Param(description="The VMware Datacenter name") + private String name; + + public String getName() { + return name; + } + + public String getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + + public void setId(String id) { + this.id = id; + } +} From 3696605ad6b02706ac40c776d2821abef98a993f Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 23 May 2013 12:41:07 +0530 Subject: [PATCH 248/412] CLOUDSTACK-1963 New mapping model for CloudStack zone and Vmware datacenter DB schema changes to support this feature. Added 3 new tables. 'vmware_data_center' to persist information about each Vmware datacenter known to cloudstack. 'vmware_data_center_zone_map' to persist mapping information of a Vmware datacenter & cloudstack zone. 'legacy_zones' to persist the known legacy zones in the deployment. Signed-off-by: Sateesh Chodapuneedi --- setup/db/db/schema-410to420.sql | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 196706f1b15..074e12d1c83 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1005,6 +1005,31 @@ CREATE TABLE `cloud`.`network_asa1000v_map` ( CONSTRAINT `fk_network_asa1000v_map__asa1000v_id` FOREIGN KEY (`asa1000v_id`) REFERENCES `external_cisco_asa1000v_devices`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `cloud`.`vmware_data_center` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(255) UNIQUE, + `name` varchar(255) NOT NULL COMMENT 'Name of VMware datacenter', + `guid` varchar(255) NOT NULL UNIQUE COMMENT 'id of VMware datacenter', + `vcenter_host` varchar(255) NOT NULL COMMENT 'vCenter host containing this VMware datacenter', + `username` varchar(255) NOT NULL COMMENT 'Name of vCenter host user', + `password` varchar(255) NOT NULL COMMENT 'Password of vCenter host user', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`vmware_data_center_zone_map` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `zone_id` bigint unsigned NOT NULL UNIQUE COMMENT 'id of CloudStack zone', + `vmware_data_center_id` bigint unsigned NOT NULL UNIQUE COMMENT 'id of VMware datacenter', + PRIMARY KEY (`id`), + CONSTRAINT `fk_vmware_data_center_zone_map__vmware_data_center_id` FOREIGN KEY (`vmware_data_center_id`) REFERENCES `vmware_data_center`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`legacy_zones` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `zone_id` bigint unsigned NOT NULL UNIQUE COMMENT 'id of CloudStack zone', + PRIMARY KEY (`id`), + CONSTRAINT `fk_legacy_zones__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `eip_associate_public_ip` int(1) unsigned NOT NULL DEFAULT 0 COMMENT 'true if public IP is associated with user VM creation by default when EIP service is enabled.' AFTER `elastic_ip_service`; From cf2b8694b55684f7aa04f3d672827449357f7749 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Thu, 23 May 2013 12:43:25 +0530 Subject: [PATCH 249/412] CLOUDSTACK-1963 New mapping model for CloudStack zone and Vmware datacenter Adding all new beans introduced during the feature implementation to nonoss component context. Signed-off-by: Sateesh Chodapuneedi --- client/tomcatconf/nonossComponentContext.xml.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/tomcatconf/nonossComponentContext.xml.in b/client/tomcatconf/nonossComponentContext.xml.in index 6fa9d38baa4..7dc331f1be8 100644 --- a/client/tomcatconf/nonossComponentContext.xml.in +++ b/client/tomcatconf/nonossComponentContext.xml.in @@ -77,6 +77,8 @@ + + - + diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateClusterCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateClusterCmd.java index 58e20dee025..91e4fcee3ad 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateClusterCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateClusterCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicateClusterResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResources; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateHostCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateHostCmd.java index f0269b11048..cb8eb45e0c4 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateHostCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateHostCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicateHostResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResources; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicatePodCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicatePodCmd.java index be5eac26feb..ed3c227e508 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicatePodCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicatePodCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; -import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicatePodResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResources; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateZoneCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateZoneCmd.java index 134fb972757..31c6025c305 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/DedicateZoneCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/DedicateZoneCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicateZoneResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResources; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedClustersCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedClustersCmd.java index c60c5240d9a..f3947876581 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedClustersCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedClustersCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicateClusterResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResourceVO; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedHostsCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedHostsCmd.java index 2c1ad002e0d..736251b36d6 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedHostsCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedHostsCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicateHostResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResourceVO; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedPodsCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedPodsCmd.java index 31b1ecfbb51..da59edae8d3 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedPodsCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedPodsCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; -import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicatePodResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResourceVO; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedZonesCmd.java similarity index 95% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedZonesCmd.java index c88c42b82a6..a21f129f5be 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ListDedicatedZonesCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ListDedicatedZonesCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.response.DedicateZoneResponse; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.dc.DedicatedResourceVO; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedClusterCmd.java similarity index 96% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedClusterCmd.java index 6a317885f10..ba1c6aad7cc 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedClusterCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedClusterCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import javax.inject.Inject; @@ -26,7 +26,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.SuccessResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.event.EventTypes; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedHostCmd.java similarity index 96% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedHostCmd.java index 29cfdeb9ab5..a79c965926d 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedHostCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedHostCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import javax.inject.Inject; @@ -26,7 +26,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.SuccessResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.event.EventTypes; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedPodCmd.java similarity index 96% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedPodCmd.java index 51d3fe20477..d84ef66ef5a 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedPodCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedPodCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import javax.inject.Inject; @@ -26,7 +26,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.SuccessResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.event.EventTypes; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedZoneCmd.java similarity index 96% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedZoneCmd.java index 23f955711bf..c78a4961dc8 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/commands/ReleaseDedicatedZoneCmd.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/commands/ReleaseDedicatedZoneCmd.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.commands; +package org.apache.cloudstack.api.commands; import javax.inject.Inject; @@ -26,7 +26,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.dedicated.DedicatedService; import org.apache.log4j.Logger; import com.cloud.event.EventTypes; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateClusterResponse.java similarity index 97% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateClusterResponse.java index faa362777f2..3c8dde3fd08 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateClusterResponse.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateClusterResponse.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.response; +package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.BaseResponse; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateHostResponse.java similarity index 97% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateHostResponse.java index e48ee35610d..cea31fe392b 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateHostResponse.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateHostResponse.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.response; +package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.BaseResponse; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicatePodResponse.java similarity index 97% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicatePodResponse.java index fabaca166ea..4bcaa61c269 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicatePodResponse.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicatePodResponse.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.response; +package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateZoneResponse.java similarity index 97% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateZoneResponse.java index 06f4877d211..57497cd3484 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/api/response/DedicateZoneResponse.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/api/response/DedicateZoneResponse.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.api.response; +package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java similarity index 96% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java index 51087e2f56a..ae25b02ec47 100755 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/manager/DedicatedResourceManagerImpl.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.manager; +package org.apache.cloudstack.dedicated; import java.util.ArrayList; import java.util.List; @@ -24,23 +24,22 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.dedicated.api.commands.DedicateClusterCmd; -import org.apache.cloudstack.dedicated.api.commands.DedicateHostCmd; -import org.apache.cloudstack.dedicated.api.commands.DedicatePodCmd; -import org.apache.cloudstack.dedicated.api.commands.DedicateZoneCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedClustersCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedHostsCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedPodsCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedZonesCmd; -import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedClusterCmd; -import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedHostCmd; -import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedPodCmd; -import org.apache.cloudstack.dedicated.api.commands.ReleaseDedicatedZoneCmd; -import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; -import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; -import org.apache.cloudstack.dedicated.services.DedicatedService; +import org.apache.cloudstack.api.commands.DedicateClusterCmd; +import org.apache.cloudstack.api.commands.DedicateHostCmd; +import org.apache.cloudstack.api.commands.DedicatePodCmd; +import org.apache.cloudstack.api.commands.DedicateZoneCmd; +import org.apache.cloudstack.api.commands.ListDedicatedClustersCmd; +import org.apache.cloudstack.api.commands.ListDedicatedHostsCmd; +import org.apache.cloudstack.api.commands.ListDedicatedPodsCmd; +import org.apache.cloudstack.api.commands.ListDedicatedZonesCmd; +import org.apache.cloudstack.api.commands.ReleaseDedicatedClusterCmd; +import org.apache.cloudstack.api.commands.ReleaseDedicatedHostCmd; +import org.apache.cloudstack.api.commands.ReleaseDedicatedPodCmd; +import org.apache.cloudstack.api.commands.ReleaseDedicatedZoneCmd; +import org.apache.cloudstack.api.response.DedicateClusterResponse; +import org.apache.cloudstack.api.response.DedicateHostResponse; +import org.apache.cloudstack.api.response.DedicatePodResponse; +import org.apache.cloudstack.api.response.DedicateZoneResponse; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -51,7 +50,6 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.DedicatedResources; import com.cloud.dc.HostPodVO; -import com.cloud.dc.Pod; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DedicatedResourceDao; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java similarity index 77% rename from plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java rename to plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java index 360852ae8e6..81ababcf53b 100755 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/services/DedicatedService.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java @@ -14,18 +14,18 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.dedicated.services; +package org.apache.cloudstack.dedicated; import java.util.List; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedClustersCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedHostsCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedPodsCmd; -import org.apache.cloudstack.dedicated.api.commands.ListDedicatedZonesCmd; -import org.apache.cloudstack.dedicated.api.response.DedicateClusterResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateHostResponse; -import org.apache.cloudstack.dedicated.api.response.DedicatePodResponse; -import org.apache.cloudstack.dedicated.api.response.DedicateZoneResponse; +import org.apache.cloudstack.api.commands.ListDedicatedClustersCmd; +import org.apache.cloudstack.api.commands.ListDedicatedHostsCmd; +import org.apache.cloudstack.api.commands.ListDedicatedPodsCmd; +import org.apache.cloudstack.api.commands.ListDedicatedZonesCmd; +import org.apache.cloudstack.api.response.DedicateClusterResponse; +import org.apache.cloudstack.api.response.DedicateHostResponse; +import org.apache.cloudstack.api.response.DedicatePodResponse; +import org.apache.cloudstack.api.response.DedicateZoneResponse; import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.DedicatedResources; import com.cloud.utils.Pair; diff --git a/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java b/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java index 3cf51299b6e..58aae238d88 100644 --- a/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java +++ b/plugins/dedicated-resources/test/org/apache/cloudstack/dedicated/manager/DedicatedApiUnitTest.java @@ -22,13 +22,13 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.when; import java.io.IOException; -import java.util.List; import javax.inject.Inject; import javax.naming.ConfigurationException; import junit.framework.Assert; +import org.apache.cloudstack.dedicated.DedicatedResourceManagerImpl; import org.apache.cloudstack.test.utils.SpringUtils; import org.apache.log4j.Logger; import org.junit.Before; @@ -49,11 +49,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.dc.ClusterVO; -import com.cloud.dc.DataCenter.NetworkType; -import com.cloud.dc.DataCenterVO; import com.cloud.dc.DedicatedResourceVO; -import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DedicatedResourceDao; @@ -61,9 +57,6 @@ import com.cloud.dc.dao.HostPodDao; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.host.Host; -import com.cloud.host.HostVO; -import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; From a011267ba6bba975084415a8a6b0e8217ea77cfa Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Mon, 3 Jun 2013 16:26:46 +0530 Subject: [PATCH 287/412] ntier apps CLOUDSTACK-2801,CLOUDSTACK-2802,CLOUDSTACK-2806,CLOUDSTACK-2816 --- docs/en-US/acquire-new-ip-for-vpc.xml | 33 ++++++++--- docs/en-US/add-gateway-vpc.xml | 57 +++++++++++++++++-- docs/en-US/add-portforward-rule-vpc.xml | 36 ++++++++---- docs/en-US/add-tier.xml | 5 +- docs/en-US/add-vm-to-tier.xml | 14 ++++- docs/en-US/enable-disable-static-nat-vpc.xml | 35 ++++++++---- docs/en-US/images/add-new-gateway-vpc.png | Bin 23184 -> 21912 bytes docs/en-US/images/add-vm-vpc.png | Bin 0 -> 8596 bytes docs/en-US/images/del-tier.png | Bin 0 -> 815 bytes docs/en-US/release-ip-for-vpc.xml | 31 +++++++--- docs/en-US/remove-tier.xml | 22 +++---- docs/en-US/remove-vpc.xml | 5 +- docs/en-US/vpc.xml | 4 +- 13 files changed, 181 insertions(+), 61 deletions(-) create mode 100644 docs/en-US/images/add-vm-vpc.png create mode 100644 docs/en-US/images/del-tier.png diff --git a/docs/en-US/acquire-new-ip-for-vpc.xml b/docs/en-US/acquire-new-ip-for-vpc.xml index 785e80bb874..c0cb876d483 100644 --- a/docs/en-US/acquire-new-ip-for-vpc.xml +++ b/docs/en-US/acquire-new-ip-for-vpc.xml @@ -39,28 +39,43 @@ Click the Configure button of the VPC to which you want to deploy the VMs. The VPC page is displayed where all the tiers you created are listed in a diagram. - - - Click the Settings icon. The following options are displayed. - IP Addresses + Internal LB - Gateways + Public LB IP - Site-to-Site VPN + Static NAT - Network ACLs + Virtual Machines + + + CIDR + + + The following router information is displayed: + + + Private Gateways + + + Public IP Addresses + + + Site-to-Site VPNs + + + Network ACL Lists Select IP Addresses. - The IP Addresses page is displayed. + The Public IP Addresses page is displayed. Click Acquire New IP, and click Yes in the confirmation dialog. @@ -70,4 +85,4 @@ rules. -
    \ No newline at end of file + diff --git a/docs/en-US/add-gateway-vpc.xml b/docs/en-US/add-gateway-vpc.xml index 616794a51d1..a081faf7768 100644 --- a/docs/en-US/add-gateway-vpc.xml +++ b/docs/en-US/add-gateway-vpc.xml @@ -21,8 +21,8 @@
    Adding a Private Gateway to a VPC A private gateway can be added by the root admin only. The VPC private network has 1:1 - relationship with the NIC of the physical network. No gateways with duplicated VLAN and IP are - allowed in the same data center. + relationship with the NIC of the physical network. You can configure multiple private gateways + to a single VPC. No gateways with duplicated VLAN and IP are allowed in the same data center. Log in to the &PRODUCT; UI as an administrator or end user. @@ -45,16 +45,34 @@ The following options are displayed. - IP Addresses + Internal LB + + Public LB IP + + + Static NAT + + + Virtual Machines + + + CIDR + + + The following router information is displayed: + Private Gateways - Site-to-Site VPN + Public IP Addresses - Network ACLs + Site-to-Site VPNs + + + Network ACL Lists @@ -96,9 +114,38 @@ VLAN: The VLAN associated with the VPC gateway. + + Source NAT: Select this option to enable the source + NAT service on the VPC private gateway. + See . + + + ACL: Controls both ingress and egress traffic on a + VPC private gateway. By default, all the traffic is blocked. + See . + The new gateway appears in the list. You can repeat these steps to add more gateway for this VPC. +
    + Source NAT on Private Gateway + You might want to deploy multiple VPCs with the same super CIDR and guest tier CIDR. + Therefore, multiple guest VMs from different VPCs can have the same IPs to reach a enterprise + data center through the private gateway. In such cases, a NAT service need to be configured on + the private gateway. If Source NAT is enabled, the guest VMs in VPC reaches the enterprise + network via private gateway IP address by using the NAT service. + The Source NAT service on a private gateway can be enabled while adding the private + gateway. On deletion of a private gateway, source NAT rules specific to the private gateway + are deleted. +
    +
    + ACL on Private Gateway + The traffic on the VPC private gateway is controlled by creating both ingress and egress + network ACL rules. The ACLs contains both allow and deny rules. As per the rule, all the + ingress traffic to the private gateway interface and all the egress traffic out from the + private gateway interface are blocked. You can change this default behaviour while creating a + private gateway. +
    diff --git a/docs/en-US/add-portforward-rule-vpc.xml b/docs/en-US/add-portforward-rule-vpc.xml index c3dbc39bb19..5b1bb49a0a3 100644 --- a/docs/en-US/add-portforward-rule-vpc.xml +++ b/docs/en-US/add-portforward-rule-vpc.xml @@ -35,28 +35,42 @@ Click the Configure button of the VPC to which you want to deploy the VMs. The VPC page is displayed where all the tiers you created are listed in a diagram. -
    - - Click the Settings icon. - The following options are displayed. + For each tier, the following options are displayed: - IP Addresses + Internal LB - Gateways + Public LB IP - Site-to-Site VPN + Static NAT - Network ACLs + Virtual Machines + + + CIDR + + + The following router information is displayed: + + + Private Gateways + + + Public IP Addresses + + + Site-to-Site VPNs + + + Network ACL Lists - Choose an existing IP address or acquire a new IP address. Click the name of the IP - address in the list. + In the Router node, select Public IP Addresses. The IP Addresses page is displayed. @@ -95,7 +109,7 @@ Add VM: Click Add VM. Select the name of the instance to which this rule applies, and click Apply. - You can test the rule by opening an ssh session to the instance. + You can test the rule by opening an SSH session to the instance. diff --git a/docs/en-US/add-tier.xml b/docs/en-US/add-tier.xml index 6beaab2a151..e5334d39ca6 100644 --- a/docs/en-US/add-tier.xml +++ b/docs/en-US/add-tier.xml @@ -41,6 +41,9 @@ Click the Configure button of the VPC for which you want to set up tiers. + + + Click Create network. The Add new tier dialog is displayed, as follows: @@ -62,7 +65,7 @@ Network Offering: The following default network - offerings are listed: DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, + offerings are listed: Internal LB, DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, DefaultIsolatedNetworkOfferingForVpcNetworks In a VPC, only one tier can be created by using LB-enabled network offering. diff --git a/docs/en-US/add-vm-to-tier.xml b/docs/en-US/add-vm-to-tier.xml index e401eed2656..c7d769d9d11 100644 --- a/docs/en-US/add-vm-to-tier.xml +++ b/docs/en-US/add-vm-to-tier.xml @@ -33,13 +33,21 @@ Click the Configure button of the VPC to which you want to deploy the VMs. - The VPC page is displayed where all the tiers you created are listed. + The VPC page is displayed where all the tiers you have created are listed. - Click the Add VM button of the tier for which you want to add a VM. + Click Virtual Machines tab of the tier to which you want to add a VM. + + + + + + add-vm-vpc.png: adding a VM to a vpc. + + The Add Instance page is displayed. Follow the on-screen instruction to add an instance. For information on adding an - instance, see Adding Instances section in the Installation Guide. + instance, see the Installation Guide. diff --git a/docs/en-US/enable-disable-static-nat-vpc.xml b/docs/en-US/enable-disable-static-nat-vpc.xml index 17f0c10540f..467a304915d 100644 --- a/docs/en-US/enable-disable-static-nat-vpc.xml +++ b/docs/en-US/enable-disable-static-nat-vpc.xml @@ -42,27 +42,42 @@ Click the Configure button of the VPC to which you want to deploy the VMs. The VPC page is displayed where all the tiers you created are listed in a diagram. - - - Click the Settings icon. - The following options are displayed. + For each tier, the following options are displayed. - IP Addresses + Internal LB - Gateways + Public LB IP - Site-to-Site VPN + Static NAT - Network ACLs + Virtual Machines + + + CIDR + + + The following router information is displayed: + + + Private Gateways + + + Public IP Addresses + + + Site-to-Site VPNs + + + Network ACL Lists - Select IP Addresses. + In the Router node, select Public IP Addresses. The IP Addresses page is displayed. @@ -74,7 +89,7 @@ - enable-disable.png: button to enable Statid NAT. + enable-disable.png: button to enable Static NAT. The button toggles between Enable and Disable, depending on whether static NAT is currently enabled for the IP address. diff --git a/docs/en-US/images/add-new-gateway-vpc.png b/docs/en-US/images/add-new-gateway-vpc.png index f15b778e0f239ad5c648b23b76d9a2c031c6f8c1..5145622a2f4dd9e19e923f72e55e893c1597113a 100644 GIT binary patch literal 21912 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 YvzU|Ko@EU{7ikPNC2hqTdF!|T2hMzSaR2}S diff --git a/docs/en-US/images/add-vm-vpc.png b/docs/en-US/images/add-vm-vpc.png new file mode 100644 index 0000000000000000000000000000000000000000..b2821a691567129bce12aa3c7d3b4233d91c15f0 GIT binary patch literal 8596 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 NJXd|8Qmq6I{XZ{goL&F` literal 0 HcmV?d00001 diff --git a/docs/en-US/images/del-tier.png b/docs/en-US/images/del-tier.png new file mode 100644 index 0000000000000000000000000000000000000000..aa9846cfd9bfd7cf6e626c856604adb0231f5a3b GIT binary patch literal 815 zcmV+~1JL}5P)X0ssI2LSV8^00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0>nu~K~zXft(9v} z+E5&a`z5z*-tTeQ8>db--v{pbYpdweJ-(i2& zmlT%FCeMqLp7VRo$@!h6NIc2eF#P#!?^)N@H$Bv8u~aTqsz~kODSK0vgk*}hyT}6< z(ePI5k5~D73E8t^G{Yg)+G8eHln~M%b`gH0Qhi8Y-%1E|Mq~I1Qm)i5vjqw9f!7^G zO65A2$xDbYNIQuja$l;kDPBT6r?G~&SgJ6W*;d%>&aR$*bZ&uWQo@>Of}2*My?uku zBSJLg2}By+yJ9(UkrBo6(>^pj+S$|h;#J4IRxhl9Jzl=gV<0(YNnq(_`aP9o@Z)x)pRz3&ydDDfkFeut#%{I0-d+yS*#X$xBh9G_(25 zpis>M;2Ibn?H?R@Hz`x7HPfnDqsbhjndYF|caGtwNWO3viX}yf#94+*ZyQY@0=4X; zQYKfyST&g&Mpop1eY^DZpvq5VctP@P(1{%$px%tC3x z3y~|;&EA6}kHh6{=e1aEa>a}wHSC&ME(L7u7zMRbjlxc1UebQ(Y3HRAtef;LEa`WQ zrUY{#D4?;;ErV9Kd=d!4Sj6ih>=@C`3z=Ag;qwJ9eFfDE3K*m^+3Q>(LMKIAf&axm z=KhDbRi$mdI}UNX6PMom;}_=k!uxIg&i>=DI7#W&Hu&6)WM0x2u~*^ tLHe}%FVAOVYuDyDytyq_>ksgs{{s*fW(qxYbS?k@002ovPDHLkV1fZdctQXG literal 0 HcmV?d00001 diff --git a/docs/en-US/release-ip-for-vpc.xml b/docs/en-US/release-ip-for-vpc.xml index 466ec663a17..f827b671c03 100644 --- a/docs/en-US/release-ip-for-vpc.xml +++ b/docs/en-US/release-ip-for-vpc.xml @@ -40,27 +40,42 @@ Click the Configure button of the VPC whose IP you want to release. The VPC page is displayed where all the tiers you created are listed in a diagram. - - - Click the Settings icon. The following options are displayed. - IP Addresses + Internal LB - Gateways + Public LB IP - Site-to-Site VPN + Static NAT - Network ACLs + Virtual Machines + + + CIDR + + + The following router information is displayed: + + + Private Gateways + + + Public IP Addresses + + + Site-to-Site VPNs + + + Network ACL Lists - Select IP Addresses. + Select Public IP Addresses. The IP Addresses page is displayed. diff --git a/docs/en-US/remove-tier.xml b/docs/en-US/remove-tier.xml index b5996eb2de3..701645cc4ed 100644 --- a/docs/en-US/remove-tier.xml +++ b/docs/en-US/remove-tier.xml @@ -40,16 +40,18 @@ The Configure VPC page is displayed. Locate the tier you want to work with. - Click the Remove VPC button: - - - - - - remove-tier.png: removing a tier from a vpc. - - - Wait for some time for the tier to be removed. + Select the tier you want to remove. + + + In the Network Details tab, click the Delete Network button. + + + + + del-tier.png: button to remove a tier + + + Click Yes to confirm. Wait for some time for the tier to be removed. diff --git a/docs/en-US/remove-vpc.xml b/docs/en-US/remove-vpc.xml index c5eff850fd3..b373f1a52c3 100644 --- a/docs/en-US/remove-vpc.xml +++ b/docs/en-US/remove-vpc.xml @@ -38,14 +38,15 @@ Select the VPC you want to work with. - To remove, click the Remove VPC button + In the Details tab, click the Remove VPC button remove-vpc.png: button to remove a VPC - + + You can remove the VPC by also using the remove button in the Quick View. You can edit the name and description of a VPC. To do that, select the VPC, then click the Edit button. diff --git a/docs/en-US/vpc.xml b/docs/en-US/vpc.xml index 0665d372b4e..7c94f0d6dd1 100644 --- a/docs/en-US/vpc.xml +++ b/docs/en-US/vpc.xml @@ -151,8 +151,8 @@ cannot be used for StaticNAT or port forwarding. - The instances only have a private IP address that you provision. To communicate with the - Internet, enable NAT to an instance that you launch in your VPC. + The instances can only have a private IP address that you provision. To communicate with + the Internet, enable NAT to an instance that you launch in your VPC. Only new networks can be added to a VPC. The maximum number of networks per VPC is From 14782dbc2427ff80f6221f949732130a70f2e6a0 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Mon, 3 Jun 2013 16:29:19 +0530 Subject: [PATCH 288/412] CLOUDSTACK-2815: include dedicated resources in non-oss dedicated resources don't load in the non-oss context without this Signed-off-by: Prasanna Santhanam --- client/tomcatconf/nonossComponentContext.xml.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/tomcatconf/nonossComponentContext.xml.in b/client/tomcatconf/nonossComponentContext.xml.in index 42c9fc53131..16fd88337fb 100644 --- a/client/tomcatconf/nonossComponentContext.xml.in +++ b/client/tomcatconf/nonossComponentContext.xml.in @@ -374,4 +374,14 @@ + + + + + + + + From 45681c57f6dc77423100b732a73b524c3bf818b1 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 3 Jun 2013 17:03:07 +0530 Subject: [PATCH 289/412] CLOUDSTACK-2029:Zone wide primary storage for VMware --- ui/scripts/system.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 3044c83771e..ba7168b49d9 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -11346,6 +11346,7 @@ $form.find('.form-item[rel=podId]').hide(); $form.find('.form-item[rel=clusterId]').hide(); $form.find('.form-item[rel=hostId]').hide(); + $form.find('.form-item[rel=hypervisor]').css('display', 'inline-block'); } @@ -11355,6 +11356,7 @@ $form.find('.form-item[rel=hostId]').hide(); $form.find('.form-item[rel=podId]').css('display', 'inline-block'); $form.find('.form-item[rel=clusterId]').css('display', 'inline-block'); + $form.find('.form-item[rel=hypervisor]').hide(); } @@ -11363,6 +11365,8 @@ $form.find('.form-item[rel=podId]').css('display', 'inline-block'); $form.find('.form-item[rel=clusterId]').css('display', 'inline-block'); $form.find('.form-item[rel=hostId]').css('display', 'inline-block'); + $form.find('.form-item[rel=hypervisor]').hide(); + } @@ -11370,6 +11374,23 @@ } }, + + + hypervisor:{ + label:'Hypervisor', + isHidden:true, + select:function(args){ + var items=[]; + items.push({ id: 'KVM', description: _l('KVM') }); + items.push({ id: 'VMWARE', description: _l('VMware') }); + + args.response.success({ + data: items + }); + + } + }, + zoneid: { label: 'Zone', docID: 'helpPrimaryStorageZone', @@ -11858,6 +11879,11 @@ array1.push("&scope=" + todb(args.data.scope)); array1.push("&zoneid=" + args.data.zoneid); + + if(args.data.scope == 'zone'){ + + array1.push("&hypervisor=" + args.data.hypervisor); + } if(args.data.scope == 'cluster'){ From 2aae10bb6469626f3523e31b84557ed53d8502c0 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Mon, 3 Jun 2013 17:04:39 +0530 Subject: [PATCH 290/412] CLOUDSTACK-2029:Zone wide primary storage for VMware --- ui/scripts/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index ba7168b49d9..80dd0bf0903 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -11382,7 +11382,7 @@ select:function(args){ var items=[]; items.push({ id: 'KVM', description: _l('KVM') }); - items.push({ id: 'VMWARE', description: _l('VMware') }); + items.push({ id: 'VMware', description: _l('VMware') }); args.response.success({ data: items From a0372ccd1727a7577d5932a6149744aff172ddf2 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Mon, 3 Jun 2013 17:18:16 +0530 Subject: [PATCH 291/412] Setting object name in response object. --- .../apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java index 7168c7fc72a..fde96c81570 100644 --- a/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java @@ -99,6 +99,7 @@ public class AddVmwareDcCmd extends BaseCmd { response.setId(result.getUuid()); response.setName(result.getVmwareDatacenterName()); response.setResponseName(getCommandName()); + response.setObjectName("vmwaredc"); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VMware Datacenter to zone."); } From 0a69b828993088487876ce859e6c00e96e4b545c Mon Sep 17 00:00:00 2001 From: Bharat Kumar Date: Mon, 3 Jun 2013 16:54:28 +0530 Subject: [PATCH 292/412] CLOUDSTACK-2620 [Multiple_IP_Ranges] Guest vm's nameserver is not set to VRs guest IP address in case of multiple subnets Signed-off-by: Abhinandan Prateek --- api/src/com/cloud/agent/api/to/DnsmasqTO.java | 13 +++++++- .../cloud/network/DnsMasqConfigurator.java | 30 ++++++++----------- .../VirtualNetworkApplianceManagerImpl.java | 12 ++++++-- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/api/src/com/cloud/agent/api/to/DnsmasqTO.java b/api/src/com/cloud/agent/api/to/DnsmasqTO.java index f99878c2fed..c7be04d4900 100644 --- a/api/src/com/cloud/agent/api/to/DnsmasqTO.java +++ b/api/src/com/cloud/agent/api/to/DnsmasqTO.java @@ -20,11 +20,14 @@ public class DnsmasqTO { String routerIp; String gateway; String netmask; + String startIpOfSubnet; - public DnsmasqTO(String routerIp, String gateway, String netmask) { + public DnsmasqTO(String routerIp, String gateway, String netmask, String StartIpOfSubnet) { this.routerIp = routerIp; + this.startIpOfSubnet = StartIpOfSubnet; this.gateway = gateway; this.netmask =netmask; + } public void setRouterIp(String routerIp){ @@ -39,6 +42,10 @@ public class DnsmasqTO { this.netmask = netmask ; } + public void setStartIpOfSubnet( String ipOfSubNet) { + startIpOfSubnet = ipOfSubNet; + } + public String getRouterIp() { return routerIp; } @@ -50,4 +57,8 @@ public class DnsmasqTO { public String getNetmask() { return netmask; } + public String getStartIpOfSubnet() { + return startIpOfSubnet; + } + } diff --git a/core/src/com/cloud/network/DnsMasqConfigurator.java b/core/src/com/cloud/network/DnsMasqConfigurator.java index ee8e5fc2e13..dd349263c0c 100644 --- a/core/src/com/cloud/network/DnsMasqConfigurator.java +++ b/core/src/com/cloud/network/DnsMasqConfigurator.java @@ -71,7 +71,7 @@ import java.util.List; "conf-dir=/etc/dnsmasq.d\n", "dhcp-option=tag:net1,3,ipaddress\n", "dhcp-option=tag:net1,1,netmask\n", - "dhcp-option=6,10.147.28.149,8.8.8.8\n", + "dhcp-option=6,router_ip,external_dns\n", "dhcp-optsfile=/etc/dhcpopts.txt\n", @@ -85,11 +85,21 @@ import java.util.List; String netmask=""; String domain= dnsMasqconfigcmd.getDomain(); String dnsServers=""; + String dns_external=""; + if (dnsMasqconfigcmd.getDns1()!= null) { + dns_external = dnsMasqconfigcmd.getDns1()+","; + } + if (dnsMasqconfigcmd.getDns2() != null) { + dns_external = dns_external+dnsMasqconfigcmd.getDns2()+","; + } + dns_external = dns_external + "*"; + dns_external = dns_external.replace(",*",""); int i=0; for (; i< dnsmasqTOs.size(); i++) { - range=range + "dhcp-range=set:range"+i+","+dnsmasqTOs.get(i).getRouterIp()+",static\n"; + range=range + "dhcp-range=set:range"+i+","+dnsmasqTOs.get(i).getStartIpOfSubnet()+",static\n"; gateway=gateway +"dhcp-option=tag:range"+i+",3,"+dnsmasqTOs.get(i).getGateway()+"\n"; netmask=netmask +"dhcp-option=tag:range"+i+",1,"+dnsmasqTOs.get(i).getNetmask()+"\n"; + dnsServers=dnsServers+"dhcp-option=tag:range"+i+",6,"+dnsmasqTOs.get(i).getRouterIp()+","+dns_external+"\n"; } dnsMasqconf.set(12, "domain="+domain+"\n"); dnsMasqconf.set(14, "domain="+domain+"\n"); @@ -97,21 +107,7 @@ import java.util.List; dnsMasqconf.set(18, range); dnsMasqconf.set(22, gateway); dnsMasqconf.set(23, netmask); - if (dnsMasqconfigcmd.getInternal_dns1() != null) { - dnsServers = dnsServers+dnsMasqconfigcmd.getInternal_dns1()+","; - } - if (dnsMasqconfigcmd.getInternal_dns2() != null) { - dnsServers = dnsServers+dnsMasqconfigcmd.getInternal_dns2()+","; - } - if (dnsMasqconfigcmd.getDns1() != null) { - dnsServers = dnsServers+dnsMasqconfigcmd.getDns1()+","; - } - if (dnsMasqconfigcmd.getDns2() != null) { - dnsServers = dnsServers+dnsMasqconfigcmd.getDns2()+","; - } - dnsServers = dnsServers +"*"; - dnsServers = dnsServers.replace(",*", ""); - dnsMasqconf.set(24,"dhcp-option=6,"+dnsServers); + dnsMasqconf.set(24,dnsServers); return dnsMasqconf.toArray( new String[dnsMasqconf.size()]); } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index c71d037e05d..020dbda4ed4 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -3395,10 +3395,16 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V List ipAliasVOList = _nicIpAliasDao.getAliasIpForVm(router.getId()); List ipList = new ArrayList(); - NicVO router_guest_ip = _nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId()); - ipList.add(new DnsmasqTO(router_guest_ip.getIp4Address(),router_guest_ip.getGateway(),router_guest_ip.getNetmask())); + NicVO router_guest_nic = _nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId()); + String cidr = NetUtils.getCidrFromGatewayAndNetmask(router_guest_nic.getGateway(), router_guest_nic.getNetmask()); + String[] cidrPair = cidr.split("\\/"); + String cidrAddress = cidrPair[0]; + long cidrSize = Long.parseLong(cidrPair[1]); + String startIpOfSubnet = NetUtils.getIpRangeStartIpFromCidr(cidrAddress, cidrSize); + + ipList.add(new DnsmasqTO(router_guest_nic.getIp4Address(),router_guest_nic.getGateway(),router_guest_nic.getNetmask(), startIpOfSubnet)); for (NicIpAliasVO ipAliasVO : ipAliasVOList) { - DnsmasqTO dnsmasqTO = new DnsmasqTO(ipAliasVO.getStartIpOfSubnet(), ipAliasVO.getGateway(), ipAliasVO.getNetmask()); + DnsmasqTO dnsmasqTO = new DnsmasqTO(ipAliasVO.getIp4Address(), ipAliasVO.getGateway(), ipAliasVO.getNetmask(), ipAliasVO.getStartIpOfSubnet()); ipList.add(dnsmasqTO); } DataCenterVO dcvo = _dcDao.findById(router.getDataCenterId()); From 48913679e80e50228b1bd4b3d17fe5245461626a Mon Sep 17 00:00:00 2001 From: Bharat Kumar Date: Thu, 23 May 2013 19:24:22 +0530 Subject: [PATCH 293/412] CLOUDSTACK-2648 [Multiple_IP_Ranges] Reboot or start/stop router vm deletes the ip alises created on VR in case of multiple subnets Signed-off-by: Abhinandan Prateek --- .../virtualnetwork/VirtualRoutingResource.java | 2 +- .../systemvm/debian/config/root/deleteIpAlias.sh | 2 +- .../hypervisor/vmware/resource/VmwareResource.java | 2 +- .../hypervisor/xen/resource/CitrixResourceBase.java | 3 ++- .../router/VirtualNetworkApplianceManagerImpl.java | 13 ++++++++----- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 8b996d1bfed..9e40eefc11a 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -642,7 +642,7 @@ public class VirtualRoutingResource implements Manager { for (IpAliasTO ipAliasTO : revokedIpAliasTOs) { args = args + ipAliasTO.getAlias_count()+":"+ipAliasTO.getRouterip()+":"+ipAliasTO.getNetmask()+"-"; } - args = args + " " ; + args = args + "- " ; List activeIpAliasTOs = cmd.getCreateIpAliasTos(); for (IpAliasTO ipAliasTO : activeIpAliasTOs) { args = args + ipAliasTO.getAlias_count()+":"+ipAliasTO.getRouterip()+":"+ipAliasTO.getNetmask()+"-"; diff --git a/patches/systemvm/debian/config/root/deleteIpAlias.sh b/patches/systemvm/debian/config/root/deleteIpAlias.sh index 865ff3b4769..cf6d4de5269 100755 --- a/patches/systemvm/debian/config/root/deleteIpAlias.sh +++ b/patches/systemvm/debian/config/root/deleteIpAlias.sh @@ -24,7 +24,7 @@ set -x var="$1" cert="/root/.ssh/id_rsa.cloud" -while [ -n "$var" ] +while [[ !( "$var" == "-" ) ]] do var1=$(echo $var | cut -f1 -d "-") alias_count=$( echo $var1 | cut -f1 -d ":" ) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 5f99a152fdd..bcd02d49517 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1976,7 +1976,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa for (IpAliasTO ipAliasTO : revokedIpAliasTOs) { args = args + ipAliasTO.getAlias_count()+":"+ipAliasTO.getRouterip()+":"+ipAliasTO.getNetmask()+"-"; } - args = args + " " ; + args = args + "- " ; for (IpAliasTO ipAliasTO : activeIpAliasTOs) { args = args + ipAliasTO.getAlias_count()+":"+ipAliasTO.getRouterip()+":"+ipAliasTO.getNetmask()+"-"; } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index a2cceb14d90..66f96ad7ba6 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -2006,7 +2006,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe for (IpAliasTO ipAliasTO : revokedIpAliasTOs) { args = args + ipAliasTO.getAlias_count()+":"+ipAliasTO.getRouterip()+":"+ipAliasTO.getNetmask()+"-"; } - args = args + " " ; + //this is to ensure that thre is some argument passed to the deleteipAlias script when there are no revoked rules. + args = args + "- " ; List activeIpAliasTOs = cmd.getCreateIpAliasTos(); for (IpAliasTO ipAliasTO : activeIpAliasTOs) { args = args + ipAliasTO.getAlias_count()+":"+ipAliasTO.getRouterip()+":"+ipAliasTO.getNetmask()+"-"; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 020dbda4ed4..f7e77f3cff6 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -2473,18 +2473,21 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V //Reapply dhcp and dns configuration. if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Dhcp, provider)) { List revokedIpAliasVOs = _nicIpAliasDao.listByNetworkIdAndState(guestNetworkId, NicIpAlias.state.revoked); - s_logger.debug("Found" + revokedIpAliasVOs.size() + "ip Aliases to apply on the router as a part of dhco configuration"); + s_logger.debug("Found" + revokedIpAliasVOs.size() + "ip Aliases to revoke on the router as a part of dhcp configuration"); List revokedIpAliasTOs = new ArrayList(); for (NicIpAliasVO revokedAliasVO : revokedIpAliasVOs) { revokedIpAliasTOs.add(new IpAliasTO(revokedAliasVO.getIp4Address(), revokedAliasVO.getNetmask(), revokedAliasVO.getAliasCount().toString())); } List aliasVOs = _nicIpAliasDao.listByNetworkIdAndState(guestNetworkId, NicIpAlias.state.active); - s_logger.debug("Found" + aliasVOs.size() + "ip Aliases to apply on the router as a part of dhco configuration"); + s_logger.debug("Found" + aliasVOs.size() + "ip Aliases to apply on the router as a part of dhcp configuration"); List activeIpAliasTOs = new ArrayList(); for (NicIpAliasVO aliasVO : aliasVOs) { activeIpAliasTOs.add(new IpAliasTO(aliasVO.getIp4Address(), aliasVO.getNetmask(), aliasVO.getAliasCount().toString())); } - createDeleteIpAliasCommand(router, revokedIpAliasTOs, activeIpAliasTOs, guestNetworkId, cmds); + if (revokedIpAliasTOs.size() != 0 || activeIpAliasTOs.size() != 0){ + createDeleteIpAliasCommand(router, revokedIpAliasTOs, activeIpAliasTOs, guestNetworkId, cmds); + configDnsMasq(router, _networkDao.findById(guestNetworkId), cmds); + } } } @@ -2844,13 +2847,13 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V Commands cmds = new Commands(OnError.Continue); List revokedIpAliasVOs = _nicIpAliasDao.listByNetworkIdAndState(network.getId(), NicIpAlias.state.revoked); - s_logger.debug("Found" + revokedIpAliasVOs.size() + "ip Aliases to apply on the router as a part of dhco configuration"); + s_logger.debug("Found" + revokedIpAliasVOs.size() + "ip Aliases to revoke on the router as a part of dhcp configuration"); List revokedIpAliasTOs = new ArrayList(); for (NicIpAliasVO revokedAliasVO : revokedIpAliasVOs) { revokedIpAliasTOs.add(new IpAliasTO(revokedAliasVO.getIp4Address(), revokedAliasVO.getNetmask(), revokedAliasVO.getAliasCount().toString())); } List aliasVOs = _nicIpAliasDao.listByNetworkIdAndState(network.getId(), NicIpAlias.state.active); - s_logger.debug("Found" + aliasVOs.size() + "ip Aliases to apply on the router as a part of dhco configuration"); + s_logger.debug("Found" + aliasVOs.size() + "ip Aliases to apply on the router as a part of dhcp configuration"); List activeIpAliasTOs = new ArrayList(); for (NicIpAliasVO aliasVO : aliasVOs) { activeIpAliasTOs.add(new IpAliasTO(aliasVO.getIp4Address(), aliasVO.getNetmask(), aliasVO.getAliasCount().toString())); From 767ed065c500b6e73451135d538ebe999f0d17f1 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Mon, 3 Jun 2013 21:50:19 +0530 Subject: [PATCH 294/412] CLOUDSTACK-2815: Include dedication in simulator context SimulatoComponentContext need sto include the dedicated resource manager to see the commands/apis exposed by it. Signed-off-by: Prasanna Santhanam --- client/tomcatconf/applicationContext.xml.in | 1 - client/tomcatconf/componentContext.xml.in | 4 ++-- client/tomcatconf/simulatorComponentContext.xml.in | 6 ++++++ .../dedicated/DedicatedResourceManagerImpl.java | 1 + .../cloudstack/dedicated/DedicatedService.java | 14 +++++++------- server/src/com/cloud/api/ApiServer.java | 6 +++++- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index ee99785db61..e2bde8f13e6 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -681,7 +681,6 @@ - diff --git a/client/tomcatconf/componentContext.xml.in b/client/tomcatconf/componentContext.xml.in index 03e931a7374..93ef21f679b 100644 --- a/client/tomcatconf/componentContext.xml.in +++ b/client/tomcatconf/componentContext.xml.in @@ -274,7 +274,7 @@ --> - - + + diff --git a/client/tomcatconf/simulatorComponentContext.xml.in b/client/tomcatconf/simulatorComponentContext.xml.in index 652c4c824ff..d71cf162569 100644 --- a/client/tomcatconf/simulatorComponentContext.xml.in +++ b/client/tomcatconf/simulatorComponentContext.xml.in @@ -234,4 +234,10 @@ + + + + + + diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java index ae25b02ec47..c321b22176e 100755 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java @@ -24,6 +24,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.component.AdapterBase; import org.apache.cloudstack.api.commands.DedicateClusterCmd; import org.apache.cloudstack.api.commands.DedicateHostCmd; import org.apache.cloudstack.api.commands.DedicatePodCmd; diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java index 81ababcf53b..6f26ad62f84 100755 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedService.java @@ -16,8 +16,10 @@ // under the License. package org.apache.cloudstack.dedicated; -import java.util.List; - +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.utils.Pair; +import com.cloud.utils.component.PluggableService; import org.apache.cloudstack.api.commands.ListDedicatedClustersCmd; import org.apache.cloudstack.api.commands.ListDedicatedHostsCmd; import org.apache.cloudstack.api.commands.ListDedicatedPodsCmd; @@ -26,12 +28,10 @@ import org.apache.cloudstack.api.response.DedicateClusterResponse; import org.apache.cloudstack.api.response.DedicateHostResponse; import org.apache.cloudstack.api.response.DedicatePodResponse; import org.apache.cloudstack.api.response.DedicateZoneResponse; -import com.cloud.dc.DedicatedResourceVO; -import com.cloud.dc.DedicatedResources; -import com.cloud.utils.Pair; -import com.cloud.utils.component.PluggableService; -public interface DedicatedService extends PluggableService{ +import java.util.List; + +public interface DedicatedService extends PluggableService { DedicatePodResponse createDedicatePodResponse(DedicatedResources resource); diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 9bad32cec31..0cd1d61d4e9 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -217,8 +217,12 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } Set> cmdClasses = new HashSet>(); - for(PluggableService pluggableService: _pluggableServices) + for(PluggableService pluggableService: _pluggableServices) { cmdClasses.addAll(pluggableService.getCommands()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Discovered plugin " + pluggableService.getClass().getSimpleName()); + } + } for(Class cmdClass: cmdClasses) { APICommand at = cmdClass.getAnnotation(APICommand.class); From db655ae8c76753fb63ac87d82a337159ec5d3517 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Mon, 3 Jun 2013 22:11:03 +0530 Subject: [PATCH 295/412] CLOUDSTACK-2810: Include new vmware APIs in discovery Discovery plugin will detect APIs from pluggable services and map them to those in commands.properties. Including the latter to complete the mapping so listApis now returns these APIs. Also included fix for API docs. Signed-off-by: Prasanna Santhanam --- client/tomcatconf/commands.properties.in | 4 ++++ tools/apidoc/gen_toc.py | 1 + 2 files changed, 5 insertions(+) diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index ad47eebcebf..5eccf366c66 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -265,6 +265,10 @@ addSecondaryStorage=1 updateHostPassword=1 releaseHostReservation=1 +#### VmWare DC +addVmwareDc=1 +removeVmwareDc=1 + #### volume commands attachVolume=15 uploadVolume=15 diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index cef50ee8702..3dd526a0e3b 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -86,6 +86,7 @@ known_categories = { 'Pod': 'Pod', 'PublicIpRange': 'Network', 'Zone': 'Zone', + 'Vmware' : 'Zone', 'NetworkOffering': 'Network Offering', 'NetworkACL': 'Network ACL', 'Network': 'Network', From 24d6055177e8430993eae81b217991e2a09ec0ca Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 3 Jun 2013 11:25:42 -0700 Subject: [PATCH 296/412] CLOUDSTACK-2744: UI - create network offering dialog - when LB Type is selected as PublicLb, hide internalLbVm from provider list. --- ui/scripts/configuration.js | 100 +++++++++++++++++------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 8a4aa7d0b89..44f4095aecb 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1215,12 +1215,57 @@ $availability.hide(); } + + //*** LB providers *** + var $lbProvider = args.$form.find('.form-item[rel=\"service.Lb.provider\"]').find('select'); + var $lbProviderOptions = $lbProvider.find('option'); //when useVpc is checked and service.Lb.isEnabled is checked if($useVpcCb.is(':checked') && $("input[name='service.Lb.isEnabled']").is(":checked") == true) { - $lbType.css('display', 'inline-block'); + $lbType.css('display', 'inline-block'); + + if($lbType.find('select').val() == 'publicLb') { //disable all providers except the ones in lbProviderMap.publicLb.vpc => ["VpcVirtualRouter", "Netscaler"] + for(var i = 0; i < $lbProviderOptions.length; i++ ) { + var $option = $lbProviderOptions.eq(i); + var supportedProviders = lbProviderMap.publicLb.vpc; + var thisOpionIsSupported = false; + for(var k = 0; k < supportedProviders.length; k++ ) { + if($option.val() == supportedProviders[k]) { + thisOpionIsSupported = true; + break; + } + } + if(thisOpionIsSupported == true) { + $option.attr('disabled', false); + } + else { + $option.attr('disabled', true); + } + } + } + else if($lbType.find('select').val() == 'internalLb') { //disable all providers except the ones in lbProviderMap.internalLb.vpc => ["InternalLbVm"] + for(var i = 0; i < $lbProviderOptions.length; i++ ) { + var $option = $lbProviderOptions.eq(i); + var supportedProviders = lbProviderMap.internalLb.vpc; + var thisOpionIsSupported = false; + for(var k = 0; k < supportedProviders.length; k++ ) { + if($option.val() == supportedProviders[k]) { + thisOpionIsSupported = true; + break; + } + } + if(thisOpionIsSupported == true) { + $option.attr('disabled', false); + } + else { + $option.attr('disabled', true); + } + } + } + + $lbProvider.val($lbProvider.find('option:first')); } else { - $lbType.hide(); + $lbType.hide(); } //when service(s) has Virtual Router as provider..... @@ -1521,56 +1566,7 @@ args.response.success({data: [ {id: 'publicLb', description: 'Public LB'}, {id: 'internalLb', description: 'Internal LB'} - ]}); - - args.$select.change(function() { - if($(this).is(':visible') == false) - return; //if lbType is not visible, do nothing. - - var $lbProvider = $(this).closest('form').find('.form-item[rel=\"service.Lb.provider\"]').find('select'); - var $lbProviderOptions = $lbProvider.find('option'); - - if($(this).val() == 'publicLb') { //disable all providers except the ones in lbProviderMap.publicLb.vpc => ["VpcVirtualRouter", "Netscaler"] - for(var i = 0; i < $lbProviderOptions.length; i++ ) { - var $option = $lbProviderOptions.eq(i); - var supportedProviders = lbProviderMap.publicLb.vpc; - var thisOpionIsSupported = false; - for(var k = 0; k < supportedProviders.length; k++ ) { - if($option.val() == supportedProviders[k]) { - thisOpionIsSupported = true; - break; - } - } - if(thisOpionIsSupported == true) { - $option.attr('disabled', false); - } - else { - $option.attr('disabled', true); - } - } - } - else if($(this).val() == 'internalLb') { //disable all providers except the ones in lbProviderMap.internalLb.vpc => ["InternalLbVm"] - for(var i = 0; i < $lbProviderOptions.length; i++ ) { - var $option = $lbProviderOptions.eq(i); - var supportedProviders = lbProviderMap.internalLb.vpc; - var thisOpionIsSupported = false; - for(var k = 0; k < supportedProviders.length; k++ ) { - if($option.val() == supportedProviders[k]) { - thisOpionIsSupported = true; - break; - } - } - if(thisOpionIsSupported == true) { - $option.attr('disabled', false); - } - else { - $option.attr('disabled', true); - } - } - } - - $lbProvider.val($lbProvider.find('option:first')); - }); + ]}); } }, From acc71fb735ded8e9e6378699a140d72c67a6428d Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 3 Jun 2013 11:29:40 -0700 Subject: [PATCH 297/412] CLOUDSTACK-766: Support assigning VLAN ID to network On add network form, if selected network offering has specifyVlan=true, show VLAN text field. --- ui/scripts/network.js | 61 +++++++++++++++++++++++++++++++++++-------- ui/scripts/vpc.js | 20 +++++++++++++- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index d66ec839dd6..aa22290ef96 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -376,20 +376,41 @@ var items = json.listvpcsresponse.vpc; var baseUrl = 'listNetworkOfferings&zoneid=' + args.zoneId; var listUrl; + var data = { + guestiptype: 'Isolated', + supportedServices: 'SourceNat', + state: 'Enabled', + }; + if(items != null && items.length > 0) listUrl = baseUrl; else listUrl = baseUrl + '&forVpc=false'; + + if (args.context.vpc) { + data.forVpc = true; + } + $.ajax({ url: createURL(listUrl), - data: { - guestiptype: 'Isolated', - supportedServices: 'SourceNat', - specifyvlan: false, - state: 'Enabled' - }, + data: data, success: function(json) { networkOfferingObjs = json.listnetworkofferingsresponse.networkoffering; + args.$select.change(function() { + var $vlan = args.$select.closest('form').find('[rel=vlan]'); + var networkOffering = $.grep( + networkOfferingObjs, function(netoffer) { + return netoffer.id == args.$select.val(); + } + )[0]; + + if (networkOffering.specifyvlan) { + $vlan.css('display', 'inline-block'); + } else { + $vlan.hide(); + } + }); + args.response.success({ data: $.map(networkOfferingObjs, function(zone) { return { @@ -405,26 +426,39 @@ } }, + vlan: { + label: 'VLAN', + validation: { required: true }, + isHidden: true + }, + vpcid: { label: 'label.vpc', dependsOn: 'networkOfferingId', select: function(args) { var networkOfferingObj; var $form = args.$select.closest('form'); + var data = { + listAll: true, + details: 'min' + }; + + if (args.context.vpc) { + data.id = args.context.vpc[0].id; + } + $(networkOfferingObjs).each(function(key, value) { if(value.id == args.networkOfferingId) { networkOfferingObj = value; return false; //break each loop } }); + if(networkOfferingObj.forvpc == true) { args.$select.closest('.form-item').css('display', 'inline-block'); $.ajax({ url: createURL('listVPCs'), - data: { - listAll: true, - details: 'min' - }, + data: data, success: function(json) { var items = json.listvpcsresponse.vpc; var data; @@ -477,12 +511,17 @@ vpcid: args.data.vpcid }); } + + if (args.$form.find('.form-item[rel=vlan]').css('display') != 'none') { + $.extend(dataObj, { vlan: args.data.vlan }); + } + if(args.data.networkDomain != null && args.data.networkDomain.length > 0 && args.$form.find('.form-item[rel=vpcid]').css("display") == "none") { $.extend(dataObj, { networkDomain: args.data.networkDomain }); } - + $.ajax({ url: createURL('createNetwork'), data: dataObj, diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index b57a562824e..ebfcc3b7b56 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3047,11 +3047,24 @@ zoneid: args.zoneId, guestiptype: 'Isolated', supportedServices: 'SourceNat', - specifyvlan: false, state: 'Enabled' }, success: function(json) { var networkOfferings = json.listnetworkofferingsresponse.networkoffering; + args.$select.change(function() { + var $vlan = args.$select.closest('form').find('[rel=vlan]'); + var networkOffering = $.grep( + networkOfferings, function(netoffer) { + return netoffer.id == args.$select.val(); + } + )[0]; + + if (networkOffering.specifyvlan) { + $vlan.css('display', 'inline-block'); + } else { + $vlan.hide(); + } + }); //only one network(tier) is allowed to have PublicLb (i.e. provider is PublicLb provider like "VpcVirtualRouter", "Netscaler") in a VPC var items; @@ -3086,6 +3099,11 @@ }); } }, + vlan: { + label: 'VLAN', + validation: { required: true }, + isHidden: true + }, gateway: { label: 'label.gateway', docID: 'helpTierGateway', From b325f7d3bd6274c979549794d8155ffa1492924f Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 3 Jun 2013 12:29:57 -0700 Subject: [PATCH 298/412] CLOUDSTACK-2744: UI - create network offering dialog - when VPC checkbox is unchecked and LB service is checked, provider option InternalLbVm should be disabled. --- ui/scripts/configuration.js | 72 +++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 44f4095aecb..cb15598d5b8 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1193,19 +1193,46 @@ var $sourceNATField = args.$form.find('input[name=\"service.SourceNat.isEnabled\"]'); var $guestTypeField = args.$form.find('select[name=guestIpType]'); + //*** VPC checkbox *** var $useVpc = args.$form.find('.form-item[rel=\"useVpc\"]'); var $useVpcCb = $useVpc.find("input[type=checkbox]"); if($guestTypeField.val() == 'Shared') { //Shared network offering $useVpc.hide(); - if($useVpcCb.is(':checked')) { //if useVpc is checked, - $useVpcCb.removeAttr("checked"); //remove "checked" attribute in useVpc - $useVpcCb.trigger("click"); //trigger useVpc.onChange() + if($useVpcCb.is(':checked')) { //if useVpc is checked, + $useVpcCb.removeAttr("checked"); //remove "checked" attribute in useVpc } } else { //Isolated network offering $useVpc.css('display', 'inline-block'); - } - + } + var $providers = $useVpcCb.closest('form').find('.dynamic-input select'); + var $optionsOfProviders = $providers.find('option'); + //p.s. Netscaler is supported in both vpc and non-vpc + if ($useVpc.is(':visible') && $useVpcCb.is(':checked')) { //*** vpc *** + $optionsOfProviders.each(function(index) { + if($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter' || $(this).val() == 'Netscaler') { + $(this).attr('disabled', false); + } + else { + $(this).attr('disabled', true); + } + }); + } + else { //*** non-vpc *** + $optionsOfProviders.each(function(index) { + if($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter') { + $(this).attr('disabled', true); + } + else { + $(this).attr('disabled', false); + } + }); + } + $providers.each(function() { + $(this).val($(this).find('option:first')); + }); + + if (!requiredNetworkOfferingExists && $sourceNATField.is(':checked') && @@ -1523,40 +1550,7 @@ useVpc: { label: 'VPC', docID: 'helpNetworkOfferingVPC', - isBoolean: true, - onChange: function(args) { - var $vpc = args.$checkbox; - var $providers = $vpc.closest('form').find('.dynamic-input select'); - var $optionsOfProviders = $providers.find('option'); - - //p.s. Netscaler is supported in both vpc and non-vpc - - if ($vpc.is(':checked')) { //*** vpc *** - $optionsOfProviders.each(function(index) { - if($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter' || $(this).val() == 'Netscaler') { - $(this).attr('disabled', false); - } - else { - $(this).attr('disabled', true); - } - }); - } - else { //*** non-vpc *** - $optionsOfProviders.each(function(index) { - if($(this).val() == 'InternalLbVm' || $(this).val() == 'VpcVirtualRouter') { - $(this).attr('disabled', true); - } - else { - $(this).attr('disabled', false); - } - }); - } - - $providers.each(function() { - $(this).val($(this).find('option:first')); - }); - - } + isBoolean: true }, lbType: { //only shown when VPC is checked and LB service is checked From ddfdc9af6316c91a5b6f5f269a05a45bba0006df Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Mon, 3 Jun 2013 16:48:54 -0600 Subject: [PATCH 299/412] KVM - Don't fail to start due to old VM definition. On rare occasion we see inactive VM definitions block a new VM starting. Definitions aren't supposed to be persistent, but sometimes a crash or failed migration can leave behind a definition. Signed-off-by: Marcus Sorensen 1370299734 -0600 --- .../kvm/resource/LibvirtComputingResource.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 46fce2487b6..e1ad7c20d64 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1094,6 +1094,24 @@ ServerResource { This also makes sure we never have any old "garbage" defined in libvirt which might haunt us. */ + + // check for existing inactive vm definition and remove it + // this can sometimes happen during crashes, etc + Domain dm = null; + try { + dm = conn.domainLookupByName(vmName); + if (dm != null && dm.isPersistent() == 1) { + // this is safe because it doesn't stop running VMs + dm.undefine(); + } + } catch (LibvirtException e) { + // this is what we want, no domain found + } finally { + if (dm != null) { + dm.free(); + } + } + conn.domainCreateXML(domainXML, 0); } catch (final LibvirtException e) { throw e; From bfe05acd95e8327cce96bc9a300f3803d03f9cce Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 3 Jun 2013 16:26:35 -0700 Subject: [PATCH 300/412] CLOUDSTACK-2782: UI - Infrastructure menu - zone detail page - add new action Add VMWare DC. --- ui/scripts/system.js | 71 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 80dd0bf0903..0bdb5b87c56 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -5373,7 +5373,74 @@ detailView: { isMaximized: true, - actions: { + actions: { + addVmwareDc: { + label: 'Adds a VMware datacenter', + messages: { + notification: function(args) { + return 'Adds a VMware datacenter'; + } + }, + createForm: { + title: 'Adds a VMware datacenter', + fields: { + name: { + label: 'label.name', + validation: { required: true } + }, + url: { + label: 'label.url', + validation: { required: false } + }, + username: { + label: 'label.username', + validation: { required: false } + }, + password: { + label: 'label.password', + isPassword: true, + validation: { required: false } + }, + } + }, + action: function(args) { + var data = { + zoneid: args.context.physicalResources[0].id, + name: args.data.name + }; + + if(args.data.url != null && args.data.url.length > 0) { + $.extend(data, { + url: args.data.url + }) + } + if(args.data.username != null && args.data.username.length > 0) { + $.extend(data, { + username: args.data.username + }) + } + if(args.data.password != null && args.data.password.length > 0) { + $.extend(data, { + password: args.data.password + }) + } + + $.ajax({ + url: createURL('addVmwareDc'), + data: data, + success: function(json) { + //var item = json.addvmwaredcresponse.vmwaredc; + args.response.success(); + } + }); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + }, + enable: { label: 'label.action.enable.zone', messages: { @@ -13204,6 +13271,8 @@ var jsonObj = args.context.item; var allowedActions = ['enableSwift']; + allowedActions.push('addVmwareDc'); + if(jsonObj.domainid != null) allowedActions.push("release"); else From 5a525211de4da25fd89084b391357109688bbcca Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 3 Jun 2013 16:37:38 -0700 Subject: [PATCH 301/412] CLOUDSTACK-2782: UI - Infrastructure menu - zone detail page - implement Remove VMWare DC action. --- ui/scripts/system.js | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 0bdb5b87c56..76836623571 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -5375,14 +5375,14 @@ isMaximized: true, actions: { addVmwareDc: { - label: 'Adds a VMware datacenter', + label: 'Add VMware datacenter', messages: { notification: function(args) { - return 'Adds a VMware datacenter'; + return 'Add VMware datacenter'; } }, createForm: { - title: 'Adds a VMware datacenter', + title: 'Add VMware datacenter', fields: { name: { label: 'label.name', @@ -5440,7 +5440,40 @@ } } }, - + + removeVmwareDc: { + label: 'Remove VMware datacenter', + messages: { + confirm: function(args) { + return 'Please confirm you want to remove VMware datacenter'; + }, + notification: function(args) { + return 'Remove VMware datacenter'; + } + }, + action: function(args) { + var data = { + zoneid: args.context.physicalResources[0].id + }; + $.ajax({ + url: createURL('removeVmwareDc'), + data: data, + success: function(json) { + var item = json.updatezoneresponse.zone; + args.response.success({ + actionFilter: zoneActionfilter, + data:item + }); + } + }); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + }, + enable: { label: 'label.action.enable.zone', messages: { @@ -13272,6 +13305,7 @@ var allowedActions = ['enableSwift']; allowedActions.push('addVmwareDc'); + allowedActions.push('removeVmwareDc'); if(jsonObj.domainid != null) allowedActions.push("release"); From 84634d4cf62d50f95e2e76445f77049d3eb8ca8e Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Mon, 3 Jun 2013 16:45:49 -0700 Subject: [PATCH 302/412] CLOUDSTACK-2782: UI - Infrastructure menu - zone detail page - rename action name to be more clear. --- ui/scripts/system.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 76836623571..8fa7a845f69 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -5536,7 +5536,7 @@ } }, - dedicate:{ + dedicateZone:{ label: 'Dedicate Zone', messages: { confirm: function(args) { @@ -5611,7 +5611,7 @@ }, - release:{ + releaseDedicatedZone:{ label:'Release Dedicated Zone', messages:{ confirm: function(args) { @@ -13308,9 +13308,9 @@ allowedActions.push('removeVmwareDc'); if(jsonObj.domainid != null) - allowedActions.push("release"); + allowedActions.push("releaseDedicatedZone"); else - allowedActions.push("dedicate"); + allowedActions.push("dedicateZone"); allowedActions.push("edit"); if(jsonObj.allocationstate == "Disabled") From e71a54b3a9fc96ec3b69112db5aefc9ceb478cd7 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 3 Jun 2013 16:52:09 -0700 Subject: [PATCH 303/412] VPC UI: Add tier connector lines to chart On VPC chart, connect router to tier networks via graph lines. --- ui/modules/vpc/vpc.css | 51 +++++++++++++++++++----- ui/modules/vpc/vpc.js | 90 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 122 insertions(+), 19 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 51c3f7e05bd..466ce80cf85 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -21,6 +21,8 @@ width: 100%; height: 100%; overflow: auto; + overflow-x: hidden; + position: relative; } .vpc-network-chart .tiers { @@ -247,29 +249,29 @@ float: left; /*+placement:shift 10px 176px;*/ position: relative; - left: 10px; - top: 176px; + left: 0px; + top: 214px; } .vpc-network-chart .tier-item.router .header { padding: 15px 0 14px; border-bottom: 1px solid #808080; background: #C3C6C9; -/*Old browsers*/ + /*Old browsers*/ background: -moz-linear-gradient(top, #c3c6c9 0%, #909497 100%); -/*FF3.6+*/ + /*FF3.6+*/ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c3c6c9), color-stop(100%,#909497)); -/*Chrome,Safari4+*/ + /*Chrome,Safari4+*/ background: -webkit-linear-gradient(top, #c3c6c9 0%,#909497 100%); -/*Chrome10+,Safari5.1+*/ + /*Chrome10+,Safari5.1+*/ background: -o-linear-gradient(top, #c3c6c9 0%,#909497 100%); -/*Opera 11.10+*/ + /*Opera 11.10+*/ background: -ms-linear-gradient(top, #c3c6c9 0%,#909497 100%); -/*IE10+*/ + /*IE10+*/ background: linear-gradient(to bottom, #c3c6c9 0%,#909497 100%); -/*W3C*/ + /*W3C*/ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c3c6c9', endColorstr='#909497',GradientType=0 ); -/*IE6-8*/ + /*IE6-8*/ } .vpc-network-chart .tier-item.router .header .title { @@ -311,3 +313,32 @@ background-color: #818181; } +.vpc-network-chart .connector-line { +} + +.vpc-network-chart .connector-line .connector-start, +.vpc-network-chart .connector-line .connector-mid, +.vpc-network-chart .connector-line .connector-end { + position: absolute; + top: 0px; + left: 0px; + background: #CCCCCC; +} + +.vpc-network-chart .connector-line .connector-start, +.vpc-network-chart .connector-line .connector-end { + height: 3px; +} + +.vpc-network-chart .connector-line .connector-start { + width: 50px; + margin-left: 18px; +} + +.vpc-network-chart .connector-line .connector-mid { + width: 3px; +} + +.vpc-network-chart .connector-line .connector-end { +} + diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index a6037c00fb7..a3c2ec738e9 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -113,6 +113,64 @@ return $tier; }, + connectorLine: function(args) { + var $connector = $('
    ').addClass('connector-line'); + var $router = args.$router; + var $tier = args.$tier; + var $connectorStart = $('
    ').addClass('connector-start'); + var $connectorMid = $('
    ').addClass('connector-mid'); + var $connectorEnd = $('
    ').addClass('connector-end'); + + $connector.append($connectorStart, $connectorMid, $connectorEnd); + + var posStartOffsetLeft = 5; + var posStartOffsetTop = 10; + var posStart = { + top: $router.position().top + ($router.outerHeight() / 2 + ($tier.index() * posStartOffsetTop)), + left: $router.position().left + $router.outerWidth() + }; + var posStartWidth = 60 - (($tier.index() + 1) * posStartOffsetLeft); + + var posEndOffset = 15; + var posEnd = { + top: $tier.position().top + ($tier.outerHeight() / 2), + left: posStart.left + posStartWidth + posEndOffset + }; + var posEndWidth = Math.abs($tier.position().left - + (posStart.left + posStartWidth)) - posEndOffset; + + // Start line (next to router) + $connectorStart.css({ + top: posStart.top, + left: posStart.left + }); + $connectorStart.width(posStartWidth); + + // End line (next to tier) + $connectorEnd.css({ + top: posEnd.top, + left: posEnd.left + }); + $connectorEnd.width(posEndWidth); + + // Mid line (connect start->end) + if (posStart.top > posEnd.top) { // Tier above router + $connectorMid.css({ + top: posEnd.top, + left: posEnd.left + }); + $connectorMid.height(posStart.top - posEnd.top); + } else { // Tier below router + $connectorMid.css({ + top: posStart.top, + left: posStart.left + posStartWidth + posEndOffset + }); + $connectorMid.height(posEnd.top - posStart.top); + } + + return $connector; + }, + router: function(args) { var $router = elems.tier({ context: args.context, @@ -171,7 +229,7 @@ var section = cloudStack.vpc.sections[id]; var $section = $('
    '); var $loading = $('
    ').addClass('loading-overlay'); - + if ($.isFunction(section)) { section = cloudStack.vpc.sections[id](); } @@ -193,7 +251,7 @@ $section.appendTo($panel); } - }); + }); }; if (before) { @@ -262,10 +320,17 @@ response: { success: function(data) { var tiers = data.tiers; + var $router; var $placeholder = elems.tierPlaceholder({ context: context }); + // Router + $router = elems.router({ + context: context, + dashboardItems: data.routerDashboard + }).appendTo($chart); + $(tiers).map(function(index, tier) { var $tier = elems.tier({ context: context, @@ -273,6 +338,14 @@ dashboardItems: tier._dashboardItems }); $tier.appendTo($tiers); + + // Connect tier to router via line + // + // -- Needs to execute after chart generation is complete, + // so that chart elements have positioning in place. + $chart.bind('cloudStack.vpc.chartReady', function() { + elems.connectorLine({ $tier: $tier, $router: $router }).appendTo($chart); + }); }); // Add placeholder tier @@ -289,12 +362,6 @@ if (args.complete) { args.complete($chart); } - - // Router - elems.router({ - context: context, - dashboardItems: data.routerDashboard - }).appendTo($chart); } } }); @@ -303,6 +370,7 @@ chart({ complete: function($newChart) { $chart.replaceWith($newChart); + $newChart.trigger('cloudStack.vpc.chartReady'); } }); }); @@ -314,7 +382,11 @@ title: vpcItem.displaytext ? vpcItem.displaytext : vpcItem.name, maximizeIfSelected: true, complete: function($panel) { - var $chart = chart(); + var $chart = chart({ + complete: function($chart) { + $chart.trigger('cloudStack.vpc.chartReady'); + } + }); $chart.appendTo($panel); } From 15c2c212f719686fc411288ec2cf5db71dfde4ab Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 4 Jun 2013 10:18:29 +0530 Subject: [PATCH 304/412] Changing the CSS object names for dedicate/release action items --- ui/css/cloudstack3.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 36f4ac65ec1..732efdbb714 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -11726,12 +11726,12 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it } .restart .icon, -.release .icon { +.releaseDedicatedZone .icon { background-position: 0px -63px; } .restart:hover .icon, -.release:hover .icon { +.releaseDedicatedZone:hover .icon { background-position: 0px -645px; } @@ -11888,7 +11888,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .createTemplate .icon, .enableSwift .icon, .addVM .icon, -.dedicate .icon { +.dedicateZone .icon { background-position: -69px -63px; } @@ -11896,7 +11896,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .createTemplate:hover .icon, .enableSwift:hover .icon, .addVM:hover .icon, -.dedicate:hover .icon { +.dedicateZone:hover .icon { background-position: -69px -645px; } From cc2091078d9bf63975a922db31008fd82f1cc904 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 4 Jun 2013 10:30:25 +0530 Subject: [PATCH 305/412] Legacy zone validation changes --- ui/scripts/system.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8fa7a845f69..5b8b12d0845 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -9530,7 +9530,7 @@ vCenterHost: { label: 'label.vcenter.host', docID: 'helpClustervCenterHost', - validation: { required: true } + validation: { required: false } //legacy zone - validation not required for new VMware dc model }, vCenterUsername: { label: 'label.vcenter.username', @@ -9546,7 +9546,7 @@ vCenterDatacenter: { label: 'label.vcenter.datacenter', docID: 'helpClustervCenterDatacenter', - validation: { required: true } + validation: { required: false } //legacy zone - validation not required for new VMware dc model }, overridepublictraffic:{ From 13eaa9a5b82037c84ceefa448e3fc7e69b2becf3 Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Tue, 4 Jun 2013 11:28:23 +0530 Subject: [PATCH 306/412] CLOUDSTACK-847 --- docs/en-US/guest-ip-ranges.xml | 2 +- docs/en-US/multiple-ip-range.xml | 41 ++++++++++++++++++++++++++++++++ docs/en-US/networks.xml | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 docs/en-US/multiple-ip-range.xml diff --git a/docs/en-US/guest-ip-ranges.xml b/docs/en-US/guest-ip-ranges.xml index b3ebd761394..ffaa01a2f60 100644 --- a/docs/en-US/guest-ip-ranges.xml +++ b/docs/en-US/guest-ip-ranges.xml @@ -28,5 +28,5 @@ their guest network and their clients. In shared networks in Basic zone and Security Group-enabled Advanced networks, you will have the flexibility to add multiple guest IP ranges from different subnets. You can add or remove - one IP range at a time. + one IP range at a time. For more information, see . diff --git a/docs/en-US/multiple-ip-range.xml b/docs/en-US/multiple-ip-range.xml new file mode 100644 index 00000000000..487668d09d1 --- /dev/null +++ b/docs/en-US/multiple-ip-range.xml @@ -0,0 +1,41 @@ + + +%BOOK_ENTITIES; +]> + +
    + About Multiple IP Ranges + + The feature can only be implemented on IPv4 addresses. + + &PRODUCT; provides you with the flexibility to add guest IP ranges from different subnets in + Basic zones and security groups-enabled Advanced zones. What it implies in the case of security + groups-enabled Advanced zones is multiple subnets can be added to the same VLAN. With the + addition of this feature, you will be able to add IP address ranges from the same subnet or from + a different one during IP address exhaustion. To support this feature, the capability of + createVlanIpRange API is extended to add IP ranges also from a different + subnet. + As an admin, you should manually configure the gateway of the new subnet before adding the + IP range. &PRODUCT; supports only one gateway for a subnet; overlapping subnets are not + currently supported. + Use the deleteVlanRange API to delete IP ranges. This operation fails if an IP + from the remove range is in use. If the remove range contains the IP address on which the DHCP + server is running, &PRODUCT; acquires a new IP from the same subnet. If no IP is available in + the subnet, the remove operation fails. + This feature is supported on KVM, xenServer, and VMware hypervisors. +
    diff --git a/docs/en-US/networks.xml b/docs/en-US/networks.xml index 8a7405a63ac..b557088273f 100644 --- a/docs/en-US/networks.xml +++ b/docs/en-US/networks.xml @@ -33,6 +33,7 @@ + From ba2687c2381659f27c5a5354c3ff8aade4d0898e Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Tue, 4 Jun 2013 11:45:45 +0530 Subject: [PATCH 307/412] href error fixed --- docs/en-US/guest-ip-ranges.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en-US/guest-ip-ranges.xml b/docs/en-US/guest-ip-ranges.xml index ffaa01a2f60..c49dc6a76f8 100644 --- a/docs/en-US/guest-ip-ranges.xml +++ b/docs/en-US/guest-ip-ranges.xml @@ -28,5 +28,5 @@ their guest network and their clients. In shared networks in Basic zone and Security Group-enabled Advanced networks, you will have the flexibility to add multiple guest IP ranges from different subnets. You can add or remove - one IP range at a time. For more information, see . + one IP range at a time. For more information, see . From d58c0c4d1145345ad66c73924eab8f3f5703552d Mon Sep 17 00:00:00 2001 From: Bharat Kumar Date: Tue, 4 Jun 2013 12:37:06 +0530 Subject: [PATCH 308/412] Cloudstack-2736 [Multiple_IP_Ranges] Failed to deploy vm with IP address from new CIDR Signed-off-by: Abhinandan Prateek --- .../router/VirtualNetworkApplianceManagerImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index f7e77f3cff6..e0ff1573143 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -2796,7 +2796,13 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V for (VlanVO vlan : vlanList) { vlanDbIdList.add(vlan.getId()); } - routerPublicIP = _networkMgr.assignPublicIpAddressFromVlans(router.getDataCenterId(), vm.getPodIdToDeployIn(), caller, Vlan.VlanType.DirectAttached, vlanDbIdList, nic.getNetworkId(), null, false); + if (dc.getNetworkType() == NetworkType.Basic) { + routerPublicIP = _networkMgr.assignPublicIpAddressFromVlans(router.getDataCenterId(), vm.getPodIdToDeployIn(), caller, Vlan.VlanType.DirectAttached, vlanDbIdList, nic.getNetworkId(), null, false); + } + else { + routerPublicIP = _networkMgr.assignPublicIpAddressFromVlans(router.getDataCenterId(), null, caller, Vlan.VlanType.DirectAttached, vlanDbIdList, nic.getNetworkId(), null, false); + } + routerAliasIp = routerPublicIP.getAddress().addr(); } } From 0401774a09483354f5b8532a30943351755da93f Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 4 Jun 2013 13:51:20 +0530 Subject: [PATCH 309/412] StoragePoolForMigrationResponse -> StoragePoolResponse The additional response type is basically the same as StoragePoolResponse. Only additional state reported is whether the storage pool is suitable for migration. Since this is fetched from hypervisor capabilities there is no need for a new response type. Tested with the simulator and I can see the response format correctly. mysql> select * from hypervisor_capabilities where id=18\G *************************** 1. row *************************** id: 18 uuid: 98b88e6e-ccf1-11e2-bd2a-af89de8bd27e hypervisor_type: Simulator hypervisor_version: NULL max_guests_limit: 100 security_group_enabled: 1 max_data_volumes_limit: 100 max_hosts_per_cluster: 100 storage_motion_supported: 1 vm_snapshot_enabled: 1 1 row in set (0.00 sec) CloudMonkey output as below: > find storagepoolsformigration id=0a644f79-53dd-4eb6-a871-64679a47cfc6 count = 1 storagepool: name = PS0 id = 7c07ec9b-a3c6-3466-ab5a-f5669ead0b22 clusterid = 71fb5c34-4852-46e6-bb8f-c9da4e8f827c clustername = C0 created = 2013-06-04T14:06:55+0530 disksizeallocated = 0 disksizetotal = 1099511627776 disksizeused = 0 ipaddress = 10.147.28.6 jobstatus = 0 path = /export/home/sandbox/primary0 podid = 560d9600-35dd-4a50-addd-81d5618536e9 podname = POD0 scope = CLUSTER state = Up suitableformigration = True type = NetworkFilesystem zoneid = 3108f711-0db6-4dad-a0d0-2fd7d413e5ef zonename = Sandbox-simulator Signed-off-by: Prasanna Santhanam --- .../cloudstack/api/ResponseGenerator.java | 204 +++++++------- .../FindStoragePoolsForMigrationCmd.java | 25 +- .../StoragePoolForMigrationResponse.java | 259 ------------------ .../api/response/StoragePoolResponse.java | 19 +- server/src/com/cloud/api/ApiDBUtils.java | 9 +- .../src/com/cloud/api/ApiResponseHelper.java | 5 +- .../cloud/api/query/ViewResponseHelper.java | 66 +++-- .../api/query/dao/StoragePoolJoinDao.java | 12 +- .../api/query/dao/StoragePoolJoinDaoImpl.java | 7 +- 9 files changed, 170 insertions(+), 436 deletions(-) delete mode 100644 api/src/org/apache/cloudstack/api/response/StoragePoolForMigrationResponse.java diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 7da40109a4f..096bf26c196 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -16,108 +16,6 @@ // under the License. package org.apache.cloudstack.api; -import java.text.DecimalFormat; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - -import org.apache.cloudstack.affinity.AffinityGroup; -import org.apache.cloudstack.affinity.AffinityGroupResponse; -import org.apache.cloudstack.api.ApiConstants.HostDetails; -import org.apache.cloudstack.api.ApiConstants.VMDetails; -import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; -import org.apache.cloudstack.api.response.AccountResponse; -import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; -import org.apache.cloudstack.api.response.AsyncJobResponse; -import org.apache.cloudstack.api.response.AutoScalePolicyResponse; -import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; -import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; -import org.apache.cloudstack.api.response.CapacityResponse; -import org.apache.cloudstack.api.response.ClusterResponse; -import org.apache.cloudstack.api.response.ConditionResponse; -import org.apache.cloudstack.api.response.ConfigurationResponse; -import org.apache.cloudstack.api.response.CounterResponse; -import org.apache.cloudstack.api.response.CreateCmdResponse; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.DomainRouterResponse; -import org.apache.cloudstack.api.response.EventResponse; -import org.apache.cloudstack.api.response.ExtractResponse; -import org.apache.cloudstack.api.response.FirewallResponse; -import org.apache.cloudstack.api.response.FirewallRuleResponse; -import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; -import org.apache.cloudstack.api.response.GuestOSResponse; -import org.apache.cloudstack.api.response.GuestVlanRangeResponse; -import org.apache.cloudstack.api.response.HostForMigrationResponse; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; -import org.apache.cloudstack.api.response.IPAddressResponse; -import org.apache.cloudstack.api.response.InstanceGroupResponse; -import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; -import org.apache.cloudstack.api.response.IpForwardingRuleResponse; -import org.apache.cloudstack.api.response.IsolationMethodResponse; -import org.apache.cloudstack.api.response.LBHealthCheckResponse; -import org.apache.cloudstack.api.response.LBStickinessResponse; -import org.apache.cloudstack.api.response.LDAPConfigResponse; -import org.apache.cloudstack.api.response.LoadBalancerResponse; -import org.apache.cloudstack.api.response.NetworkACLItemResponse; -import org.apache.cloudstack.api.response.NetworkACLResponse; -import org.apache.cloudstack.api.response.NetworkOfferingResponse; -import org.apache.cloudstack.api.response.NetworkResponse; -import org.apache.cloudstack.api.response.NicResponse; -import org.apache.cloudstack.api.response.NicSecondaryIpResponse; -import org.apache.cloudstack.api.response.PhysicalNetworkResponse; -import org.apache.cloudstack.api.response.PodResponse; -import org.apache.cloudstack.api.response.PortableIpRangeResponse; -import org.apache.cloudstack.api.response.PortableIpResponse; -import org.apache.cloudstack.api.response.PrivateGatewayResponse; -import org.apache.cloudstack.api.response.ProjectAccountResponse; -import org.apache.cloudstack.api.response.ProjectInvitationResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.ProviderResponse; -import org.apache.cloudstack.api.response.RegionResponse; -import org.apache.cloudstack.api.response.RemoteAccessVpnResponse; -import org.apache.cloudstack.api.response.ResourceCountResponse; -import org.apache.cloudstack.api.response.ResourceLimitResponse; -import org.apache.cloudstack.api.response.ResourceTagResponse; -import org.apache.cloudstack.api.response.S3Response; -import org.apache.cloudstack.api.response.SecurityGroupResponse; -import org.apache.cloudstack.api.response.ServiceOfferingResponse; -import org.apache.cloudstack.api.response.ServiceResponse; -import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse; -import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse; -import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; -import org.apache.cloudstack.api.response.SnapshotPolicyResponse; -import org.apache.cloudstack.api.response.SnapshotResponse; -import org.apache.cloudstack.api.response.SnapshotScheduleResponse; -import org.apache.cloudstack.api.response.StaticRouteResponse; -import org.apache.cloudstack.api.response.StorageNetworkIpRangeResponse; -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; -import org.apache.cloudstack.api.response.StoragePoolResponse; -import org.apache.cloudstack.api.response.SwiftResponse; -import org.apache.cloudstack.api.response.SystemVmInstanceResponse; -import org.apache.cloudstack.api.response.SystemVmResponse; -import org.apache.cloudstack.api.response.TemplatePermissionsResponse; -import org.apache.cloudstack.api.response.TemplateResponse; -import org.apache.cloudstack.api.response.TrafficMonitorResponse; -import org.apache.cloudstack.api.response.TrafficTypeResponse; -import org.apache.cloudstack.api.response.UsageRecordResponse; -import org.apache.cloudstack.api.response.UserResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.VMSnapshotResponse; -import org.apache.cloudstack.api.response.VirtualRouterProviderResponse; -import org.apache.cloudstack.api.response.VlanIpRangeResponse; -import org.apache.cloudstack.api.response.VolumeResponse; -import org.apache.cloudstack.api.response.VpcOfferingResponse; -import org.apache.cloudstack.api.response.VpcResponse; -import org.apache.cloudstack.api.response.VpnUsersResponse; -import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; -import org.apache.cloudstack.region.PortableIp; -import org.apache.cloudstack.region.PortableIpRange; -import org.apache.cloudstack.region.Region; -import org.apache.cloudstack.usage.Usage; - import com.cloud.async.AsyncJob; import com.cloud.capacity.Capacity; import com.cloud.configuration.Configuration; @@ -193,6 +91,106 @@ import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; +import org.apache.cloudstack.affinity.AffinityGroup; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ApiConstants.HostDetails; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; +import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.AutoScalePolicyResponse; +import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; +import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; +import org.apache.cloudstack.api.response.CapacityResponse; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.ConditionResponse; +import org.apache.cloudstack.api.response.ConfigurationResponse; +import org.apache.cloudstack.api.response.CounterResponse; +import org.apache.cloudstack.api.response.CreateCmdResponse; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.DomainRouterResponse; +import org.apache.cloudstack.api.response.EventResponse; +import org.apache.cloudstack.api.response.ExtractResponse; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; +import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.GuestVlanRangeResponse; +import org.apache.cloudstack.api.response.HostForMigrationResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; +import org.apache.cloudstack.api.response.IPAddressResponse; +import org.apache.cloudstack.api.response.InstanceGroupResponse; +import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; +import org.apache.cloudstack.api.response.IpForwardingRuleResponse; +import org.apache.cloudstack.api.response.IsolationMethodResponse; +import org.apache.cloudstack.api.response.LBHealthCheckResponse; +import org.apache.cloudstack.api.response.LBStickinessResponse; +import org.apache.cloudstack.api.response.LDAPConfigResponse; +import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.cloudstack.api.response.NetworkACLItemResponse; +import org.apache.cloudstack.api.response.NetworkACLResponse; +import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.NicSecondaryIpResponse; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.PortableIpRangeResponse; +import org.apache.cloudstack.api.response.PortableIpResponse; +import org.apache.cloudstack.api.response.PrivateGatewayResponse; +import org.apache.cloudstack.api.response.ProjectAccountResponse; +import org.apache.cloudstack.api.response.ProjectInvitationResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ProviderResponse; +import org.apache.cloudstack.api.response.RegionResponse; +import org.apache.cloudstack.api.response.RemoteAccessVpnResponse; +import org.apache.cloudstack.api.response.ResourceCountResponse; +import org.apache.cloudstack.api.response.ResourceLimitResponse; +import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.cloudstack.api.response.S3Response; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.ServiceResponse; +import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse; +import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse; +import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; +import org.apache.cloudstack.api.response.SnapshotPolicyResponse; +import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.SnapshotScheduleResponse; +import org.apache.cloudstack.api.response.StaticRouteResponse; +import org.apache.cloudstack.api.response.StorageNetworkIpRangeResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.SwiftResponse; +import org.apache.cloudstack.api.response.SystemVmInstanceResponse; +import org.apache.cloudstack.api.response.SystemVmResponse; +import org.apache.cloudstack.api.response.TemplatePermissionsResponse; +import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.TrafficMonitorResponse; +import org.apache.cloudstack.api.response.TrafficTypeResponse; +import org.apache.cloudstack.api.response.UsageRecordResponse; +import org.apache.cloudstack.api.response.UserResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; +import org.apache.cloudstack.api.response.VirtualRouterProviderResponse; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.api.response.VpcOfferingResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.api.response.VpnUsersResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.Region; +import org.apache.cloudstack.usage.Usage; + +import java.text.DecimalFormat; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; public interface ResponseGenerator { UserResponse createUserResponse(UserAccount user); @@ -260,7 +258,7 @@ public interface ResponseGenerator { StoragePoolResponse createStoragePoolResponse(StoragePool pool); - StoragePoolForMigrationResponse createStoragePoolForMigrationResponse(StoragePool pool); + StoragePoolResponse createStoragePoolForMigrationResponse(StoragePool pool); ClusterResponse createClusterResponse(Cluster cluster, Boolean showCapacities); diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java index 37d007c0376..ed6ca04c16f 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/storage/FindStoragePoolsForMigrationCmd.java @@ -16,24 +16,23 @@ // under the License. package org.apache.cloudstack.api.command.admin.storage; -import java.util.ArrayList; -import java.util.List; - +import com.cloud.async.AsyncJob; +import com.cloud.storage.StoragePool; +import com.cloud.utils.Pair; import org.apache.cloudstack.api.APICommand; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.VolumeResponse; -import com.cloud.async.AsyncJob; -import com.cloud.storage.StoragePool; -import com.cloud.utils.Pair; +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; @APICommand(name = "findStoragePoolsForMigration", description="Lists storage pools available for migration of a volume.", - responseObject=StoragePoolForMigrationResponse.class) + responseObject=StoragePoolResponse.class) public class FindStoragePoolsForMigrationCmd extends BaseListCmd { public static final Logger s_logger = Logger.getLogger(FindStoragePoolsForMigrationCmd.class.getName()); @@ -72,13 +71,13 @@ public class FindStoragePoolsForMigrationCmd extends BaseListCmd { public void execute() { Pair, List> pools = _mgr.listStoragePoolsForMigrationOfVolume(getId()); - ListResponse response = new ListResponse(); - List poolResponses = new ArrayList(); + ListResponse response = new ListResponse(); + List poolResponses = new ArrayList(); List allPools = pools.first(); List suitablePoolList = pools.second(); for (StoragePool pool : allPools) { - StoragePoolForMigrationResponse poolResponse = _responseGenerator.createStoragePoolForMigrationResponse(pool); + StoragePoolResponse poolResponse = _responseGenerator.createStoragePoolForMigrationResponse(pool); Boolean suitableForMigration = false; for (StoragePool suitablePool : suitablePoolList) { if (suitablePool.getId() == pool.getId()) { diff --git a/api/src/org/apache/cloudstack/api/response/StoragePoolForMigrationResponse.java b/api/src/org/apache/cloudstack/api/response/StoragePoolForMigrationResponse.java deleted file mode 100644 index 2cfc8d03c3c..00000000000 --- a/api/src/org/apache/cloudstack/api/response/StoragePoolForMigrationResponse.java +++ /dev/null @@ -1,259 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with 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. -package org.apache.cloudstack.api.response; - -import java.util.Date; - -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseResponse; -import org.apache.cloudstack.api.EntityReference; - -import com.cloud.serializer.Param; -import com.cloud.storage.StoragePool; -import com.cloud.storage.StoragePoolStatus; -import com.google.gson.annotations.SerializedName; - -@EntityReference(value=StoragePool.class) -public class StoragePoolForMigrationResponse extends BaseResponse { - @SerializedName("id") @Param(description="the ID of the storage pool") - private String id; - - @SerializedName("zoneid") @Param(description="the Zone ID of the storage pool") - private String zoneId; - - @SerializedName(ApiConstants.ZONE_NAME) @Param(description="the Zone name of the storage pool") - private String zoneName; - - @SerializedName("podid") @Param(description="the Pod ID of the storage pool") - private String podId; - - @SerializedName("podname") @Param(description="the Pod name of the storage pool") - private String podName; - - @SerializedName("name") @Param(description="the name of the storage pool") - private String name; - - @SerializedName("ipaddress") @Param(description="the IP address of the storage pool") - private String ipAddress; - - @SerializedName("path") @Param(description="the storage pool path") - private String path; - - @SerializedName("created") @Param(description="the date and time the storage pool was created") - private Date created; - - @SerializedName("type") @Param(description="the storage pool type") - private String type; - - @SerializedName("clusterid") @Param(description="the ID of the cluster for the storage pool") - private String clusterId; - - @SerializedName("clustername") @Param(description="the name of the cluster for the storage pool") - private String clusterName; - - @SerializedName("disksizetotal") @Param(description="the total disk size of the storage pool") - private Long diskSizeTotal; - - @SerializedName("disksizeallocated") @Param(description="the host's currently allocated disk size") - private Long diskSizeAllocated; - - @SerializedName("disksizeused") @Param(description="the host's currently used disk size") - private Long diskSizeUsed; - - @SerializedName("tags") @Param(description="the tags for the storage pool") - private String tags; - - @SerializedName(ApiConstants.STATE) @Param(description="the state of the storage pool") - private StoragePoolStatus state; - - @SerializedName(ApiConstants.SCOPE) @Param(description="the scope of the storage pool") - private String scope; - - @SerializedName(ApiConstants.HYPERVISOR) @Param(description="the hypervisor type of the storage pool") - private String hypervisor; - - @SerializedName("suitableformigration") @Param(description="true if this pool is suitable to migrate a volume," + - " false otherwise") - private Boolean suitableForMigration; - - /** - * @return the scope - */ - public String getScope() { - return scope; - } - - /** - * @param scope the scope to set - */ - public void setScope(String scope) { - this.scope = scope; - } - - public String getHypervisor() { - return hypervisor; - } - - public void setHypervisor(String hypervisor) { - this.hypervisor = hypervisor; - } - - @Override - public String getObjectId() { - return this.getId(); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getZoneId() { - return zoneId; - } - - public void setZoneId(String zoneId) { - this.zoneId = zoneId; - } - - public String getZoneName() { - return zoneName; - } - - public void setZoneName(String zoneName) { - this.zoneName = zoneName; - } - - public String getPodId() { - return podId; - } - - public void setPodId(String podId) { - this.podId = podId; - } - - public String getPodName() { - return podName; - } - - public void setPodName(String podName) { - this.podName = podName; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getIpAddress() { - return ipAddress; - } - - public void setIpAddress(String ipAddress) { - this.ipAddress = ipAddress; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public Date getCreated() { - return created; - } - - public void setCreated(Date created) { - this.created = created; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getClusterId() { - return clusterId; - } - - public void setClusterId(String clusterId) { - this.clusterId = clusterId; - } - - public String getClusterName() { - return clusterName; - } - - public void setClusterName(String clusterName) { - this.clusterName = clusterName; - } - - public Long getDiskSizeTotal() { - return diskSizeTotal; - } - - public void setDiskSizeTotal(Long diskSizeTotal) { - this.diskSizeTotal = diskSizeTotal; - } - - public Long getDiskSizeAllocated() { - return diskSizeAllocated; - } - - public void setDiskSizeAllocated(Long diskSizeAllocated) { - this.diskSizeAllocated = diskSizeAllocated; - } - - public Long getDiskSizeUsed() { - return diskSizeUsed; - } - - public void setDiskSizeUsed(Long diskSizeUsed) { - this.diskSizeUsed = diskSizeUsed; - } - - public String getTags() { - return tags; - } - - public void setTags(String tags) { - this.tags = tags; - } - - public StoragePoolStatus getState() { - return state; - } - - public void setState(StoragePoolStatus state) { - this.state = state; - } - - public void setSuitableForMigration(Boolean suitableForMigration) { - this.suitableForMigration = suitableForMigration; - } -} diff --git a/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java index 57a5ea14840..965407d9952 100644 --- a/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java +++ b/api/src/org/apache/cloudstack/api/response/StoragePoolResponse.java @@ -16,16 +16,15 @@ // under the License. package org.apache.cloudstack.api.response; -import java.util.Date; - -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseResponse; -import org.apache.cloudstack.api.EntityReference; - import com.cloud.serializer.Param; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; @EntityReference(value=StoragePool.class) public class StoragePoolResponse extends BaseResponse { @@ -89,6 +88,10 @@ public class StoragePoolResponse extends BaseResponse { @SerializedName(ApiConstants.HYPERVISOR) @Param(description="the hypervisor type of the storage pool") private String hypervisor; + @SerializedName("suitableformigration") @Param(description="true if this pool is suitable to migrate a volume," + + " false otherwise") + private Boolean suitableForMigration; + /** * @return the scope */ @@ -259,4 +262,8 @@ public class StoragePoolResponse extends BaseResponse { public void setState(StoragePoolStatus state) { this.state = state; } + + public void setSuitableForMigration(Boolean suitableForMigration) { + this.suitableForMigration = suitableForMigration; + } } diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 1afedac5144..e5fa2e19757 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -59,7 +59,6 @@ import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; @@ -171,8 +170,6 @@ import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; import com.cloud.network.as.dao.AutoScaleVmProfileDao; import com.cloud.network.as.dao.ConditionDao; import com.cloud.network.as.dao.CounterDao; -import com.cloud.network.dao.AccountGuestVlanMapDao; -import com.cloud.network.dao.AccountGuestVlanMapVO; import com.cloud.network.dao.FirewallRulesCidrsDao; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; @@ -197,7 +194,6 @@ import com.cloud.network.dao.Site2SiteVpnGatewayDao; import com.cloud.network.dao.Site2SiteVpnGatewayVO; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRuleVO; -import com.cloud.network.rules.LoadBalancer; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.security.SecurityGroupVO; @@ -211,7 +207,6 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectAccount; import com.cloud.projects.ProjectInvitation; import com.cloud.projects.ProjectService; -import com.cloud.region.ha.GlobalLoadBalancingRulesService; import com.cloud.resource.ResourceManager; import com.cloud.server.Criteria; import com.cloud.server.ManagementServer; @@ -1587,11 +1582,11 @@ public class ApiDBUtils { return _poolJoinDao.setStoragePoolResponse(vrData, vr); } - public static StoragePoolForMigrationResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO vr) { + public static StoragePoolResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO vr) { return _poolJoinDao.newStoragePoolForMigrationResponse(vr); } - public static StoragePoolForMigrationResponse fillStoragePoolForMigrationDetails(StoragePoolForMigrationResponse + public static StoragePoolResponse fillStoragePoolForMigrationDetails(StoragePoolResponse vrData, StoragePoolJoinVO vr){ return _poolJoinDao.setStoragePoolForMigrationResponse(vrData, vr); } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 029b14c5e0f..55929cf5ecb 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -116,7 +116,6 @@ import org.apache.cloudstack.api.response.SnapshotResponse; import org.apache.cloudstack.api.response.SnapshotScheduleResponse; import org.apache.cloudstack.api.response.StaticRouteResponse; import org.apache.cloudstack.api.response.StorageNetworkIpRangeResponse; -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.SwiftResponse; import org.apache.cloudstack.api.response.SystemVmInstanceResponse; @@ -947,9 +946,9 @@ public class ApiResponseHelper implements ResponseGenerator { } @Override - public StoragePoolForMigrationResponse createStoragePoolForMigrationResponse(StoragePool pool) { + public StoragePoolResponse createStoragePoolForMigrationResponse(StoragePool pool) { List viewPools = ApiDBUtils.newStoragePoolView(pool); - List listPools = ViewResponseHelper.createStoragePoolForMigrationResponse( + List listPools = ViewResponseHelper.createStoragePoolForMigrationResponse( viewPools.toArray(new StoragePoolJoinVO[viewPools.size()])); assert listPools != null && listPools.size() == 1 : "There should be one storage pool returned"; return listPools.get(0); diff --git a/server/src/com/cloud/api/query/ViewResponseHelper.java b/server/src/com/cloud/api/query/ViewResponseHelper.java index 827ae7b7a66..a61da69b2e8 100644 --- a/server/src/com/cloud/api/query/ViewResponseHelper.java +++ b/server/src/com/cloud/api/query/ViewResponseHelper.java @@ -16,36 +16,6 @@ // under the License. package com.cloud.api.query; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.Hashtable; -import java.util.List; - -import org.apache.cloudstack.affinity.AffinityGroupResponse; -import org.apache.cloudstack.api.ApiConstants.HostDetails; -import org.apache.cloudstack.api.ApiConstants.VMDetails; -import org.apache.cloudstack.api.response.AccountResponse; -import org.apache.cloudstack.api.response.AsyncJobResponse; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.cloudstack.api.response.DomainRouterResponse; -import org.apache.cloudstack.api.response.EventResponse; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.HostForMigrationResponse; -import org.apache.cloudstack.api.response.InstanceGroupResponse; -import org.apache.cloudstack.api.response.ProjectAccountResponse; -import org.apache.cloudstack.api.response.ProjectInvitationResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.ResourceTagResponse; -import org.apache.cloudstack.api.response.SecurityGroupResponse; -import org.apache.cloudstack.api.response.ServiceOfferingResponse; -import org.apache.cloudstack.api.response.StoragePoolResponse; -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; -import org.apache.cloudstack.api.response.UserResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.VolumeResponse; -import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.log4j.Logger; - import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.AccountJoinVO; import com.cloud.api.query.vo.AffinityGroupJoinVO; @@ -68,6 +38,34 @@ import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; import com.cloud.user.Account; import com.cloud.user.UserContext; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ApiConstants.HostDetails; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainRouterResponse; +import org.apache.cloudstack.api.response.EventResponse; +import org.apache.cloudstack.api.response.HostForMigrationResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.InstanceGroupResponse; +import org.apache.cloudstack.api.response.ProjectAccountResponse; +import org.apache.cloudstack.api.response.ProjectInvitationResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.UserResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Hashtable; +import java.util.List; /** * Helper class to generate response from DB view VO objects. @@ -285,11 +283,11 @@ public class ViewResponseHelper { return new ArrayList(vrDataList.values()); } - public static List createStoragePoolForMigrationResponse(StoragePoolJoinVO... pools) { - Hashtable vrDataList = new Hashtable(); + public static List createStoragePoolForMigrationResponse(StoragePoolJoinVO... pools) { + Hashtable vrDataList = new Hashtable(); // Initialise the vrdatalist with the input data for (StoragePoolJoinVO vr : pools) { - StoragePoolForMigrationResponse vrData = vrDataList.get(vr.getId()); + StoragePoolResponse vrData = vrDataList.get(vr.getId()); if ( vrData == null ) { // first time encountering this vm vrData = ApiDBUtils.newStoragePoolForMigrationResponse(vr); @@ -299,7 +297,7 @@ public class ViewResponseHelper { } vrDataList.put(vr.getId(), vrData); } - return new ArrayList(vrDataList.values()); + return new ArrayList(vrDataList.values()); } diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java index b7e467fd6d3..e438b0665f7 100644 --- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java +++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java @@ -16,14 +16,12 @@ // under the License. package com.cloud.api.query.dao; -import java.util.List; - -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; -import org.apache.cloudstack.api.response.StoragePoolResponse; - import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.storage.StoragePool; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.response.StoragePoolResponse; + +import java.util.List; public interface StoragePoolJoinDao extends GenericDao { @@ -31,9 +29,9 @@ public interface StoragePoolJoinDao extends GenericDao StoragePoolResponse setStoragePoolResponse(StoragePoolResponse response, StoragePoolJoinVO host); - StoragePoolForMigrationResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO host); + StoragePoolResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO host); - StoragePoolForMigrationResponse setStoragePoolForMigrationResponse(StoragePoolForMigrationResponse response, + StoragePoolResponse setStoragePoolForMigrationResponse(StoragePoolResponse response, StoragePoolJoinVO host); List newStoragePoolView(StoragePool group); diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java index e27697832b7..a6355ad9867 100644 --- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java @@ -22,7 +22,6 @@ import java.util.List; import javax.ejb.Local; import javax.inject.Inject; -import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -130,8 +129,8 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase Date: Tue, 4 Jun 2013 13:52:54 +0530 Subject: [PATCH 310/412] developer pom is for developer environment Naming the developer project to "Apache CloudStack developer mode" instead of tools. Tools are cli, marvin, etc Signed-off-by: Prasanna Santhanam --- developer/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer/pom.xml b/developer/pom.xml index 3dc276adc23..9bfb79294fd 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -13,7 +13,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 cloud-developer - Apache CloudStack Developer Tools + Apache CloudStack Developer Mode pom org.apache.cloudstack From bc345e55207c883516167cee89db67d1c74d4ce0 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 4 Jun 2013 14:08:05 +0530 Subject: [PATCH 311/412] CLOUDSTACK-2837 : InternalLoadBalancer greets Alena Extraneous logs used while debugging. :) Signed-off-by: Prasanna Santhanam --- ...nfigureInternalLoadBalancerElementCmd.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java b/api/src/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java index 7c3d1e95e57..86f30067b18 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java @@ -17,10 +17,13 @@ package org.apache.cloudstack.api.command.admin.internallb; -import java.util.List; - -import javax.inject.Inject; - +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.user.Account; +import com.cloud.user.UserContext; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -31,13 +34,8 @@ import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.log4j.Logger; -import com.cloud.event.EventTypes; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.VirtualRouterProvider; -import com.cloud.user.Account; -import com.cloud.user.UserContext; +import javax.inject.Inject; +import java.util.List; @APICommand(name = "configureInternalLoadBalancerElement", responseObject=InternalLoadBalancerElementResponse.class, description="Configures an Internal Load Balancer element.", since="4.2.0") @@ -98,11 +96,8 @@ public class ConfigureInternalLoadBalancerElementCmd extends BaseAsyncCmd { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException{ - s_logger.debug("hello alena"); UserContext.current().setEventDetails("Internal load balancer element: " + id); - s_logger.debug("hello alena"); VirtualRouterProvider result = _service.get(0).configureInternalLoadBalancerElement(getId(), getEnabled()); - s_logger.debug("hello alena"); if (result != null){ InternalLoadBalancerElementResponse routerResponse = _responseGenerator.createInternalLbElementResponse(result); routerResponse.setResponseName(getCommandName()); From 01ff51d0f92507151c81174b26c0227f0edb83ff Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Tue, 4 Jun 2013 15:00:40 +0530 Subject: [PATCH 312/412] multiple ip edits --- docs/en-US/multiple-ip-range.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/en-US/multiple-ip-range.xml b/docs/en-US/multiple-ip-range.xml index 487668d09d1..a8d68c00911 100644 --- a/docs/en-US/multiple-ip-range.xml +++ b/docs/en-US/multiple-ip-range.xml @@ -24,14 +24,14 @@ The feature can only be implemented on IPv4 addresses. &PRODUCT; provides you with the flexibility to add guest IP ranges from different subnets in - Basic zones and security groups-enabled Advanced zones. What it implies in the case of security - groups-enabled Advanced zones is multiple subnets can be added to the same VLAN. With the - addition of this feature, you will be able to add IP address ranges from the same subnet or from - a different one during IP address exhaustion. To support this feature, the capability of + Basic zones and security groups-enabled Advanced zones. For security groups-enabled Advanced + zones, it implies multiple subnets can be added to the same VLAN. With the addition of this + feature, you will be able to add IP address ranges from the same subnet or from a different one + when IP address are exhausted. To support this feature, the capability of createVlanIpRange API is extended to add IP ranges also from a different subnet. - As an admin, you should manually configure the gateway of the new subnet before adding the - IP range. &PRODUCT; supports only one gateway for a subnet; overlapping subnets are not + Ensure that you manually configure the gateway of the new subnet before adding the IP range. + Note that &PRODUCT; supports only one gateway for a subnet; overlapping subnets are not currently supported. Use the deleteVlanRange API to delete IP ranges. This operation fails if an IP from the remove range is in use. If the remove range contains the IP address on which the DHCP From e0fa773c8e7a182d9b168c85a8a22a4f629008ee Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Tue, 4 Jun 2013 16:07:36 +0530 Subject: [PATCH 313/412] CLOUDSTACK-817 --- docs/en-US/ip-vlan-tenant.xml | 205 ++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 docs/en-US/ip-vlan-tenant.xml diff --git a/docs/en-US/ip-vlan-tenant.xml b/docs/en-US/ip-vlan-tenant.xml new file mode 100644 index 00000000000..42124f0f446 --- /dev/null +++ b/docs/en-US/ip-vlan-tenant.xml @@ -0,0 +1,205 @@ + + +%BOOK_ENTITIES; +]> + +
    + Dedicated Resources: Public IP Addresses and VLANs Per Account + &PRODUCT; provides you the ability to reserve a set of public IP addresses and VLANs + exclusively for an account. During zone creation, you can continue to define a set of VLANs and + multiple public IP ranges. This feature extends the functionality to enable you to dedicate a + fixed set of VLANs and guest IP addresses for a tenant. + This feature provides you the following capabilities: + + + Reserve a VLAN range and public IP address range from an Advanced zone and assign it to + a domain or account + + + Disassociate a VLAN and public IP address range from an domain or account + + + View the number of public IP addresses allocated to an account + + + Check whether the required range is available and is conforms to account limits. + The maximum IPs per account limit cannot be superseded. + + +
    + Dedicating IP Address Ranges to an Account + + + Log in to the &PRODUCT; UI as administrator. + + + In the left navigation bar, click Infrastructure. + + + In Zones, click View All. + + + Choose the zone you want to work with. + + + Click the Physical Network tab. + + + In the Public node of the diagram, click Configure. + + + Click the IP Ranges tab. + You can either assign an existing IP range to an account, or create a new IP range and + assign to an account. + + + To assign an existing IP range to an account, perform the following: + + + Locate the IP range you want to work with. + + + Click Add Account + + + + + addAccount-icon.png: button to assign an IP range to an account. + + button. + The Add Account dialog is displayed. + + + Specify the following: + + + Account: The account to which you want to + assign the IP address range. + + + Domain: The domain associated with the + account. + + + To create a new IP range and assign an account, perform the following: + + + Specify the following: + + + Gateway + + + Netmask + + + VLAN + + + Start IP + + + End IP + + + Account: Perform the following: + + + Click Account. + The Add Account page is displayed. + + + Specify the following: + + + Account: The account to which you want to + assign an IP address range. + + + Domain: The domain associated with the + account. + + + + + Click OK. + + + + + + + Click Add. + + + + + + +
    +
    + Dedicating VLAN Ranges to an Account + + + After the &PRODUCT; Management Server is installed, log in to the &PRODUCT; UI as + administrator. + + + In the left navigation bar, click Infrastructure. + + + In Zones, click View All. + + + Choose the zone you want to work with. + + + Click the Physical Network tab. + + + In the Guest node of the diagram, click Configure. + + + Select the Dedicated VLAN Ranges tab. + + + Click Dedicate VLAN Range. + The Dedicate VLAN Range dialog is displayed. + + + Specify the following: + + + VLAN Range: The + VLAN range that you want to assign to an account. + + + Account: The + account to which you want to assign the selected VLAN range. + + + Domain: The + domain associated with the account. + + + + +
    +
    From c8f143e3a74258c730fd25af5512513c5d972650 Mon Sep 17 00:00:00 2001 From: Abhinandan Prateek Date: Tue, 4 Jun 2013 16:11:40 +0530 Subject: [PATCH 314/412] bug CLOUDSTACK-2445: removed vm id from exception message --- .../com/cloud/network/lb/LoadBalancingRulesManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 633357e4bdd..21e2887d05b 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -973,8 +973,8 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements } if (nicInSameNetwork == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("VM " + instanceId - + " cannot be added because it doesn't belong in the same network."); + InvalidParameterValueException ex = + new InvalidParameterValueException("VM with id specified cannot be added because it doesn't belong in the same network."); ex.addProxyObject(vm.getUuid(), "instanceId"); throw ex; } From 2a3cfe4b227d43815aa1ab926e3ff1495c64a5c5 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 4 Jun 2013 16:58:32 +0530 Subject: [PATCH 315/412] CLOUDSTACK-2782:UI Support to add VMware DC to CloudStack zone through the zone wizard --- ui/scripts/zoneWizard.js | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 04ed495383b..c07ef343825 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -3366,6 +3366,49 @@ } array1.push("&clustername=" + todb(clusterName)); + if(args.data.cluster.hypervisor == "VMware"){ + + var vmwareData = { + + zoneId: args.data.returnedZone.id, + username: args.data.cluster.vCenterUsername, + password: args.data.cluster.vCenterPassword, + name: args.data.cluster.vCenterDatacenter + + }; + + $.ajax({ + url: createURL('addVmwareDc&url=' + todb(url)), + data: vmwareData, + success: function(json) { + var item = json.addvmwaredcresponse.vmwaredc; + if(item.id != null){ + $.ajax({ + url: createURL("addCluster" + array1.join("")), + dataType: "json", + async: true, + success: function(json) { + stepFns.addPrimaryStorage({ + data: $.extend(args.data, { + returnedCluster: json.addclusterresponse.cluster[0] + }) + }); + + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + error('addCluster', errorMsg, { fn: 'addCluster', args: args }); + } + }); + } + + } + }); + + } + + else{ + $.ajax({ url: createURL("addCluster" + array1.join("")), dataType: "json", @@ -3391,6 +3434,8 @@ error('addCluster', errorMsg, { fn: 'addCluster', args: args }); } }); + + } }, addHost: function(args) { From ee3043b884d6c55773a7550650af6eff186c4b60 Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 4 Jun 2013 17:50:15 +0530 Subject: [PATCH 316/412] Changing drop box to textfield --- ui/scripts/system.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 5b8b12d0845..283c7dffcc0 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -2155,7 +2155,7 @@ fields: { vlanrange: { label: 'VLAN Range', - select: function(args) { + /* select: function(args) { var items = []; if(args.context.physicalNetworks[0].vlan != null && args.context.physicalNetworks[0].vlan.length > 0) { var vlanranges = args.context.physicalNetworks[0].vlan.split(";"); @@ -2164,7 +2164,7 @@ } } args.response.success({data: items}); - }, + },*/ validation: { required: true } }, account: { label: 'label.account', validation: { required: true } }, From cb595cafc7b4fad56e7da6b4f3a1e71402648d44 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 4 Jun 2013 15:51:31 +0530 Subject: [PATCH 317/412] CLOUDSTACK-2390:[GSLB] After removeFromGSLBRule, still CloudStack things that lb rule is active removing the GSLB rule to Lb rule mapping once rules are applied on the GSLB service provider --- .../GlobalLoadBalancingRulesServiceImpl.java | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java b/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java index 02c772027c8..7c46d4abb08 100644 --- a/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java +++ b/server/src/org/apache/cloudstack/region/gslb/GlobalLoadBalancingRulesServiceImpl.java @@ -260,7 +260,13 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR s_logger.debug("Configuring gslb rule configuration on the gslb service providers in the participating zones"); // apply the gslb rule on to the back end gslb service providers on zones participating in gslb - applyGlobalLoadBalancerRuleConfig(gslbRuleId, false); + if (!applyGlobalLoadBalancerRuleConfig(gslbRuleId, false)) { + s_logger.warn("Failed to add load balancer rules " + newLbRuleIds + " to global load balancer rule id " + + gslbRuleId); + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to add load balancer rules to GSLB rule "); + throw ex; + } // on success set state to Active gslbRule.setState(GlobalLoadBalancerRule.State.Active); @@ -269,7 +275,7 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR success = true; } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException("Failed to apply gslb config"); + throw new CloudRuntimeException("Failed to apply new GSLB configuration while assigning new LB rules to GSLB rule."); } return success; @@ -359,11 +365,28 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR s_logger.debug("Attempting to configure global load balancer rule configuration on the gslb service providers "); // apply the gslb rule on to the back end gslb service providers - applyGlobalLoadBalancerRuleConfig(gslbRuleId, false); + if (!applyGlobalLoadBalancerRuleConfig(gslbRuleId, false)) { + s_logger.warn("Failed to remove load balancer rules " + lbRuleIdsToremove + " from global load balancer rule id " + + gslbRuleId); + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to remove load balancer rule ids from GSLB rule "); + throw ex; + } - // on success set state to Active + txn.start(); + + // remove the mappings of gslb rule to Lb rule that are in revoked state + for (Long lbRuleId : lbRuleIdsToremove) { + GlobalLoadBalancerLbRuleMapVO removeGslbLbMap = _gslbLbMapDao.findByGslbRuleIdAndLbRuleId(gslbRuleId, lbRuleId); + _gslbLbMapDao.remove(removeGslbLbMap.getId()); + } + + // on success set state back to Active gslbRule.setState(GlobalLoadBalancerRule.State.Active); _gslbRuleDao.update(gslbRule.getId(), gslbRule); + + txn.commit(); + success = true; } catch (ResourceUnavailableException e) { throw new CloudRuntimeException("Failed to update removed load balancer details from gloabal load balancer"); @@ -402,8 +425,16 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR _accountMgr.checkAccess(caller, SecurityChecker.AccessType.ModifyEntry, true, gslbRule); - if (gslbRule.getState() == GlobalLoadBalancerRule.State.Revoke) { - throw new InvalidParameterValueException("global load balancer rule id: " + gslbRuleId + " is already in revoked state"); + if (gslbRule.getState() == com.cloud.region.ha.GlobalLoadBalancerRule.State.Staged) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Rule Id: " + gslbRuleId + " is still in Staged state so just removing it."); + } + _gslbRuleDao.remove(gslbRuleId); + return; + } else if (gslbRule.getState() == GlobalLoadBalancerRule.State.Add || gslbRule.getState() == GlobalLoadBalancerRule.State.Active) { + //mark the GSlb rule to be in revoke state + gslbRule.setState(GlobalLoadBalancerRule.State.Revoke); + _gslbRuleDao.update(gslbRuleId, gslbRule); } Transaction txn = Transaction.currentTxn(); @@ -418,10 +449,6 @@ public class GlobalLoadBalancingRulesServiceImpl implements GlobalLoadBalancingR } } - //mark the GSlb rule to be in revoke state - gslbRule.setState(GlobalLoadBalancerRule.State.Revoke); - _gslbRuleDao.update(gslbRuleId, gslbRule); - txn.commit(); boolean success = false; From b5148af0c6dfc583bcd1a52a7510df80e9eaa163 Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Tue, 4 Jun 2013 20:07:48 +0530 Subject: [PATCH 318/412] CLOUDSTACK-2809: Assign acl_id to VPC tier only when NetworkACL service is supported --- .../com/cloud/network/NetworkServiceImpl.java | 7 ++----- .../network/vpc/NetworkACLManagerImpl.java | 18 ++++++++++++++++++ .../src/com/cloud/network/vpc/VpcManager.java | 2 +- .../com/cloud/network/vpc/VpcManagerImpl.java | 10 ++++++++-- .../test/com/cloud/vpc/MockVpcManagerImpl.java | 2 +- .../com/cloud/vpc/NetworkACLManagerTest.java | 8 ++++++++ server/test/com/cloud/vpc/VpcApiUnitTest.java | 10 +++++----- 7 files changed, 43 insertions(+), 14 deletions(-) diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 98992846c4e..2bf9f402d34 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -1285,10 +1285,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new InvalidParameterValueException("Network offering can't be used for VPC networks"); } - if(aclId == null){ - //Use default deny all ACL, when aclId is not specified - aclId = NetworkACL.DEFAULT_DENY; - } else { + if(aclId != null){ NetworkACL acl = _networkACLDao.findById(aclId); if(acl == null){ throw new InvalidParameterValueException("Unable to find specified NetworkACL"); @@ -1938,7 +1935,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { //perform below validation if the network is vpc network if (network.getVpcId() != null && networkOfferingId != null) { Vpc vpc = _vpcMgr.getVpc(network.getVpcId()); - _vpcMgr.validateNtwkOffForNtwkInVpc(networkId, networkOfferingId, null, null, vpc, null, _accountMgr.getAccount(network.getAccountId())); + _vpcMgr.validateNtwkOffForNtwkInVpc(networkId, networkOfferingId, null, null, vpc, null, _accountMgr.getAccount(network.getAccountId()), null); } // don't allow to update network in Destroy state diff --git a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java index cef6454ab5b..171b8b9a6bf 100644 --- a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java +++ b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java @@ -16,8 +16,10 @@ // under the License. package com.cloud.network.vpc; +import com.cloud.configuration.ConfigurationManager; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; import com.cloud.network.Network.Service; @@ -29,6 +31,7 @@ import com.cloud.network.element.VpcProvider; import com.cloud.network.vpc.NetworkACLItem.State; import com.cloud.network.vpc.dao.NetworkACLDao; import com.cloud.network.vpc.dao.VpcGatewayDao; +import com.cloud.offering.NetworkOffering; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -73,6 +76,8 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana VpcGatewayDao _vpcGatewayDao; @Inject NetworkModel _ntwkModel; + @Inject + ConfigurationManager _configMgr; @Override public NetworkACL createNetworkACL(String name, String description, long vpcId) { @@ -133,9 +138,22 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana @Override public boolean replaceNetworkACL(NetworkACL acl, NetworkVO network) throws ResourceUnavailableException { + + NetworkOffering guestNtwkOff = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); + + if (guestNtwkOff == null) { + throw new InvalidParameterValueException("Can't find network offering associated with network: "+network.getUuid()); + } + + //verify that ACLProvider is supported by network offering + if(!_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL)){ + throw new InvalidParameterValueException("Cannot apply NetworkACL. Network Offering does not support NetworkACL service"); + } + network.setNetworkACLId(acl.getId()); //Update Network ACL if(_networkDao.update(network.getId(), network)){ + s_logger.debug("Updated network: "+network.getId()+ "with Network ACL Id: "+acl.getId()+", Applying ACL items"); //Apply ACL to network return applyACLToNetwork(network.getId()); } diff --git a/server/src/com/cloud/network/vpc/VpcManager.java b/server/src/com/cloud/network/vpc/VpcManager.java index f22e7e4bf83..e01413f78f3 100644 --- a/server/src/com/cloud/network/vpc/VpcManager.java +++ b/server/src/com/cloud/network/vpc/VpcManager.java @@ -164,7 +164,7 @@ public interface VpcManager extends VpcService{ * @param gateway * @param networkOwner TODO */ - void validateNtwkOffForNtwkInVpc(Long networkId, long newNtwkOffId, String newCidr, String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner); + void validateNtwkOffForNtwkInVpc(Long networkId, long newNtwkOffId, String newCidr, String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner, Long aclId); List getVpcPrivateGateways(long vpcId); } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 380a95e1882..1c4adde22e2 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -1039,7 +1039,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @DB @Override public void validateNtwkOffForNtwkInVpc(Long networkId, long newNtwkOffId, String newCidr, - String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner) { + String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner, Long aclId) { NetworkOffering guestNtwkOff = _configMgr.getNetworkOffering(newNtwkOffId); @@ -1084,6 +1084,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } } + + //5) When aclId is provided, verify that ACLProvider is supported by network offering + if(aclId != null && (!_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL))){ + throw new InvalidParameterValueException("Cannot apply NetworkACL. Network Offering does not support NetworkACL service"); + } + } @Override @@ -2034,7 +2040,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } //1) Validate if network can be created for VPC - validateNtwkOffForNtwkInVpc(null, ntwkOffId, cidr, networkDomain, vpc, gateway, owner); + validateNtwkOffForNtwkInVpc(null, ntwkOffId, cidr, networkDomain, vpc, gateway, owner, aclId); //2) Create network Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, diff --git a/server/test/com/cloud/vpc/MockVpcManagerImpl.java b/server/test/com/cloud/vpc/MockVpcManagerImpl.java index 921321f52da..7e40083c8bd 100644 --- a/server/test/com/cloud/vpc/MockVpcManagerImpl.java +++ b/server/test/com/cloud/vpc/MockVpcManagerImpl.java @@ -373,7 +373,7 @@ public class MockVpcManagerImpl extends ManagerBase implements VpcManager { } @Override - public void validateNtwkOffForNtwkInVpc(Long networkId, long newNtwkOffId, String newCidr, String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner) { + public void validateNtwkOffForNtwkInVpc(Long networkId, long newNtwkOffId, String newCidr, String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner, Long aclId) { // TODO Auto-generated method stub } diff --git a/server/test/com/cloud/vpc/NetworkACLManagerTest.java b/server/test/com/cloud/vpc/NetworkACLManagerTest.java index 76b811f8685..ddcfe7fabb7 100644 --- a/server/test/com/cloud/vpc/NetworkACLManagerTest.java +++ b/server/test/com/cloud/vpc/NetworkACLManagerTest.java @@ -15,6 +15,7 @@ package com.cloud.vpc; +import com.cloud.configuration.ConfigurationManager; import com.cloud.network.Network; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkModel; @@ -78,6 +79,8 @@ public class NetworkACLManagerTest extends TestCase{ @Inject NetworkDao _networkDao; @Inject + ConfigurationManager _configMgr; + @Inject NetworkModel _networkModel; @Inject List _networkAclElements; @@ -178,6 +181,11 @@ public class NetworkACLManagerTest extends TestCase{ return Mockito.mock(NetworkDao.class); } + @Bean + public ConfigurationManager configMgr() { + return Mockito.mock(ConfigurationManager.class); + } + @Bean public NetworkACLServiceProvider networkElements() { return Mockito.mock(NetworkACLServiceProvider.class); diff --git a/server/test/com/cloud/vpc/VpcApiUnitTest.java b/server/test/com/cloud/vpc/VpcApiUnitTest.java index e141c9658b8..400e00c8f3e 100644 --- a/server/test/com/cloud/vpc/VpcApiUnitTest.java +++ b/server/test/com/cloud/vpc/VpcApiUnitTest.java @@ -87,7 +87,7 @@ public class VpcApiUnitTest extends TestCase{ //1) correct network offering boolean result = false; try { - _vpcService.validateNtwkOffForNtwkInVpc(2L, 1, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO()); + _vpcService.validateNtwkOffForNtwkInVpc(2L, 1, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO(), null); result = true; } catch (Exception ex) { } finally { @@ -97,7 +97,7 @@ public class VpcApiUnitTest extends TestCase{ //2) invalid offering - source nat is not included result = false; try { - _vpcService.validateNtwkOffForNtwkInVpc(2L, 2, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO()); + _vpcService.validateNtwkOffForNtwkInVpc(2L, 2, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO(), null); result = true; } catch (InvalidParameterValueException ex) { } finally { @@ -107,7 +107,7 @@ public class VpcApiUnitTest extends TestCase{ //3) invalid offering - conserve mode is off result = false; try { - _vpcService.validateNtwkOffForNtwkInVpc(2L, 3, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO()); + _vpcService.validateNtwkOffForNtwkInVpc(2L, 3, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO(), null); result = true; } catch (InvalidParameterValueException ex) { } finally { @@ -117,7 +117,7 @@ public class VpcApiUnitTest extends TestCase{ //4) invalid offering - guest type shared result = false; try { - _vpcService.validateNtwkOffForNtwkInVpc(2L, 4, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO()); + _vpcService.validateNtwkOffForNtwkInVpc(2L, 4, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO(), null); result = true; } catch (InvalidParameterValueException ex) { } finally { @@ -127,7 +127,7 @@ public class VpcApiUnitTest extends TestCase{ //5) Invalid offering - no redundant router support result = false; try { - _vpcService.validateNtwkOffForNtwkInVpc(2L, 5, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO()); + _vpcService.validateNtwkOffForNtwkInVpc(2L, 5, "0.0.0.0", "111-", _vpcService.getVpc(1), "10.1.1.1", new AccountVO(), null); result = true; } catch (InvalidParameterValueException ex) { } finally { From 00715c9b86e830f10065e020ff27cc2d5112c94b Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Tue, 4 Jun 2013 20:22:02 +0530 Subject: [PATCH 319/412] Displaying ACL name as None if acl is not associated with a tier --- ui/scripts/vpc.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index ebfcc3b7b56..55f84f5a1ea 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2746,7 +2746,8 @@ async: true, success: function(json) { var jsonObj = json.listnetworksresponse.network[0]; - + if(jsonObj.aclid != null){ + $.ajax({ url:createURL("listNetworkACLLists&id=" + jsonObj.aclid), dataType:"json", @@ -2763,6 +2764,16 @@ args.response.error(parseXMLHttpResponse(json)); } }); + } + + else{ + args.response.success({ + actionFilter: cloudStack.actionFilter.guestNetwork, + data:$.extend(jsonObj,{aclname:'None'}) + + }); + + } } }); } From a3b3753da34150f2acc9d8963ab5d5c100cf8979 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 09:00:54 -0700 Subject: [PATCH 320/412] VPC UI, chart: Color connector lines for tiers w/ public network --- ui/modules/vpc/vpc.css | 34 ++++++++++++++++++++++++++++++++-- ui/modules/vpc/vpc.js | 19 ++++++++++++++++++- ui/scripts/vpc.js | 13 ++++++++++++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 466ce80cf85..bdab35f0d07 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -25,8 +25,30 @@ position: relative; } +.vpc-network-chart .info-box { + font-size: 12px; + color: #6E6B6B; + padding: 9px 1px 10px 20px; + background: #FFFFFF; + border: 1px dotted #808080; + position: absolute; + top: 42px; + left: 10px; + width: 737px; +} + +.vpc-network-chart .info-box .color-key { + display: block; + background: #2983E3; + padding: 1px; + float: left; + width: 10px; + height: 10px; + margin: 0px 9px 1px 0px; +} + .vpc-network-chart .tiers { - margin: 40px 46px 0 0; + margin: 66px 46px 0 0; width: 362px; float: right; } @@ -249,8 +271,10 @@ float: left; /*+placement:shift 10px 176px;*/ position: relative; + left: 10px; + top: 176px; left: 0px; - top: 214px; + top: 240px; } .vpc-network-chart .tier-item.router .header { @@ -325,6 +349,12 @@ background: #CCCCCC; } +.vpc-network-chart .connector-line.highlighted .connector-start, +.vpc-network-chart .connector-line.highlighted .connector-mid, +.vpc-network-chart .connector-line.highlighted .connector-end { + background: #2983E3; +} + .vpc-network-chart .connector-line .connector-start, .vpc-network-chart .connector-line .connector-end { height: 3px; diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index a3c2ec738e9..6081c972be7 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -117,12 +117,17 @@ var $connector = $('
    ').addClass('connector-line'); var $router = args.$router; var $tier = args.$tier; + var isHighlighted = args.isHighlighted; var $connectorStart = $('
    ').addClass('connector-start'); var $connectorMid = $('
    ').addClass('connector-mid'); var $connectorEnd = $('
    ').addClass('connector-end'); $connector.append($connectorStart, $connectorMid, $connectorEnd); + if (isHighlighted) { + $connector.addClass('highlighted'); + } + var posStartOffsetLeft = 5; var posStartOffsetTop = 10; var posStart = { @@ -309,6 +314,7 @@ var $chart = $('
    ').addClass('vpc-network-chart'); var $tiers = $('
    ').addClass('tiers'); var $toolbar = $('
    ').addClass('toolbar'); + var $info = $('
    ').addClass('info-box'); $toolbar.appendTo($chart); $tiers.appendTo($chart); @@ -344,7 +350,11 @@ // -- Needs to execute after chart generation is complete, // so that chart elements have positioning in place. $chart.bind('cloudStack.vpc.chartReady', function() { - elems.connectorLine({ $tier: $tier, $router: $router }).appendTo($chart); + elems.connectorLine({ + $tier: $tier, + $router: $router, + isHighlighted: tier._highlighted + }).appendTo($chart); }); }); @@ -362,6 +372,13 @@ if (args.complete) { args.complete($chart); } + + if ($chart.find('.connector-line.highlighted').size()) { + $info.appendTo($chart).append( + $('').addClass('color-key'), + $('').html('= Contains a public network') + ); + } } } }); diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 55f84f5a1ea..4ad565787b7 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3379,7 +3379,18 @@ error = true; } }); - + + // Highlight if any tier VM contains guest network + $.grep( + virtualMachines.virtualmachine ? virtualMachines.virtualmachine : [], + function(vm) { + return $.grep(vm.nic, + function(nic) { + return nic.type == 'Shared'; + }).length; + } + ).length ? tier._highlighted = true : tier._highlighted = false; + return $.extend(tier, { _dashboardItems: [ { From 727c5bae9ae861160b9deb238e2309d02c9022bf Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 10:45:52 -0700 Subject: [PATCH 321/412] VPC UI, chart: Tweak for better alignment --- ui/modules/vpc/vpc.css | 2 +- ui/modules/vpc/vpc.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index bdab35f0d07..401c2ba1906 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -30,7 +30,7 @@ color: #6E6B6B; padding: 9px 1px 10px 20px; background: #FFFFFF; - border: 1px dotted #808080; + border: 2px solid #CFCFCF; position: absolute; top: 42px; left: 10px; diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 6081c972be7..5e87dbb4fb6 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -134,15 +134,16 @@ top: $router.position().top + ($router.outerHeight() / 2 + ($tier.index() * posStartOffsetTop)), left: $router.position().left + $router.outerWidth() }; - var posStartWidth = 60 - (($tier.index() + 1) * posStartOffsetLeft); + var posStartWidth = 60 - ($tier.index() > 2 ? (($tier.index() + 1) * posStartOffsetLeft) : 0); var posEndOffset = 15; + var posEndWidthOffset = 3; var posEnd = { top: $tier.position().top + ($tier.outerHeight() / 2), left: posStart.left + posStartWidth + posEndOffset }; var posEndWidth = Math.abs($tier.position().left - - (posStart.left + posStartWidth)) - posEndOffset; + (posStart.left + posStartWidth)) + posEndWidthOffset; // Start line (next to router) $connectorStart.css({ From 4c1ace5e02edb04c0aee8641334e8a0eb2bd5b5c Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Thu, 30 May 2013 16:53:39 -0700 Subject: [PATCH 322/412] CLOUDSTACK-2775: Fix trunk port is not 1 on some hosts Now searching for eth- or em- prefix, as the port for going outside. --- scripts/vm/network/ovs-pvlan-vm.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/vm/network/ovs-pvlan-vm.sh b/scripts/vm/network/ovs-pvlan-vm.sh index fd384814cc4..06e41fe4219 100755 --- a/scripts/vm/network/ovs-pvlan-vm.sh +++ b/scripts/vm/network/ovs-pvlan-vm.sh @@ -86,7 +86,8 @@ then exit 1 fi -trunk_port=1 +# try to find the physical link to outside, only supports eth and em prefix now +trunk_port=`ovs-ofctl show $br | egrep "\((eth|em)[0-9]" | cut -d '(' -f 1|tr -d ' '` if [ "$op" == "add" ] then From eaea724044e1be12ccf1cd3bb748beebcaa558d0 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 13:34:57 -0700 Subject: [PATCH 323/412] VPC UI: Fix dashboard totals for admin viewing user VPCs --- ui/scripts/vpc.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 4ad565787b7..39934b3d1ce 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -3330,7 +3330,7 @@ // Get internal load balancers $.ajax({ - url: createURL('listLoadBalancers'), + url: createURL('listLoadBalancers&listAll=true'), async: false, data: { networkid: tier.id }, success: function(json) { @@ -3343,7 +3343,7 @@ // Get Public LB IPs $.ajax({ - url: createURL('listPublicIpAddresses'), + url: createURL('listPublicIpAddresses&listAll=true'), async: false, data: { networkid: tier.id, forloadbalancing: true }, success: function(json) { @@ -3356,7 +3356,7 @@ // Get static NAT IPs $.ajax({ - url: createURL('listPublicIpAddresses'), + url: createURL('listPublicIpAddresses&listAll=true'), async: false, data: { networkid: tier.id, isstaticnat: true }, success: function(json) { @@ -3369,7 +3369,7 @@ // Get VMs $.ajax({ - url: createURL('listVirtualMachines'), + url: createURL('listVirtualMachines&listAll=true'), async: false, data: { networkid: tier.id }, success: function(json) { From 86c7274998982f726668c1980e23f380a497a148 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 13:37:21 -0700 Subject: [PATCH 324/412] VPC UI: Fix padding/alignment --- ui/modules/vpc/vpc.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 401c2ba1906..21b1ce8cb03 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -137,7 +137,7 @@ } .vpc-network-chart .tier-item .content .dashboard { - height: 117px; + display: inline-block; } .vpc-network-chart .tier-item .content .dashboard-item { @@ -265,7 +265,7 @@ .vpc-network-chart .tier-item.router { width: 258px; - height: 218px; + height: 224px; background: #BDBDBD; border: 1px solid #808080; float: left; @@ -325,6 +325,7 @@ } .vpc-network-chart .tier-item.router .dashboard-item span { + padding-right: 10px; color: #FFFFFF; /*+text-shadow:0px 1px #000000;*/ -moz-text-shadow: 0px 1px #000000; From 95c9835f69746992200364c9eff19a6c973e5e3c Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 4 Jun 2013 13:42:05 -0700 Subject: [PATCH 325/412] CLOUDSTACK-2817: set SearchBuilder with the "scheme" attribute --- .../src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 21e2887d05b..d52859bd4c0 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -1933,6 +1933,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); sb.and("sourceIpAddress", sb.entity().getSourceIpAddressId(), SearchCriteria.Op.EQ); sb.and("networkId", sb.entity().getNetworkId(), SearchCriteria.Op.EQ); + sb.and("scheme", sb.entity().getScheme(), SearchCriteria.Op.EQ); if (instanceId != null) { SearchBuilder lbVMSearch = _lb2VmMapDao.createSearchBuilder(); From d8f81fe14d35ff272c465cfc821a48fde35dea6b Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 13:46:09 -0700 Subject: [PATCH 326/412] CLOUDSTACK-2205: Hide egress tab for shared networks --- ui/scripts/network.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index aa22290ef96..e451de2f10a 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -873,6 +873,7 @@ var isVPC = false; var isAdvancedSGZone = false; var hiddenTabs = []; + var isSharedNetwork; // Get network offering data $.ajax({ @@ -886,6 +887,10 @@ isVPC = true; } + if (networkoffering.guestiptype == 'Shared') { + isSharedNetwork = true; + } + $(networkoffering.service).each(function(){ var thisService = this; @@ -932,7 +937,7 @@ hiddenTabs.push("addloadBalancer"); } - if (isVPC || isAdvancedSGZone ) { + if (isVPC || isAdvancedSGZone || isSharedNetwork) { hiddenTabs.push('egressRules'); } From 5435495e18d466a8836c1d6a6d4605f039cdd967 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 4 Jun 2013 13:54:27 -0700 Subject: [PATCH 327/412] fix build --- .../internallbelement/ElementChildTestConfiguration.java | 2 +- .../cloudstack/internallbvmmgr/LbChildTestConfiguration.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbelement/ElementChildTestConfiguration.java b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbelement/ElementChildTestConfiguration.java index 8a67e84f951..bddf713e3a8 100644 --- a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbelement/ElementChildTestConfiguration.java +++ b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbelement/ElementChildTestConfiguration.java @@ -46,7 +46,7 @@ import com.cloud.vm.dao.DomainRouterDao; @Configuration @ComponentScan( basePackageClasses={ - NetUtils.class, + NetUtils.class }, includeFilters={@Filter(value=ElementChildTestConfiguration.Library.class, type=FilterType.CUSTOM)}, useDefaultFilters=false diff --git a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/LbChildTestConfiguration.java b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/LbChildTestConfiguration.java index 74e54b23295..4f03b27b013 100644 --- a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/LbChildTestConfiguration.java +++ b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/LbChildTestConfiguration.java @@ -56,7 +56,7 @@ import com.cloud.user.dao.AccountDao; @Configuration @ComponentScan( basePackageClasses={ - NetUtils.class, + NetUtils.class }, includeFilters={@Filter(value=LbChildTestConfiguration.Library.class, type=FilterType.CUSTOM)}, useDefaultFilters=false From 9f564fc77f1bbc108d10b474e03dcf4a197d5d7c Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 4 Jun 2013 13:58:11 -0700 Subject: [PATCH 328/412] CLOUDSTACK-747: internal LB in VPC - internalLB detailView - implement Delete Internal LB action. --- ui/scripts/vpc.js | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 39934b3d1ce..f4c53b445c6 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -433,9 +433,7 @@ } ); } - }); - - args.response.success(); + }); }, notification: { poll: pollAsyncJobResult @@ -444,12 +442,12 @@ }, detailView: { - name: 'Internal Lb details', + name: 'Internal LB details', actions: { assignVm: { - label: 'Assign VMs to LB', + label: 'Assign VMs to Internal LB', messages: { - notification: function(args) { return 'Assign VM to internal LB rule'; } + notification: function(args) { return 'Assign VMs to Internal LB'; } }, listView: $.extend(true, {}, cloudStack.sections.instances.listView, { type: 'checkbox', @@ -509,7 +507,40 @@ notification: { poll: pollAsyncJobResult } - } + }, + remove: { + label: 'Delete Internal LB', + messages: { + confirm: function(args) { + return 'Please confirm you want to delete Internal LB'; + }, + notification: function(args) { + return 'Delete Internal LB'; + } + }, + action: function(args) { + var data = { + id: args.context.internalLoadBalancers[0].id + }; + $.ajax({ + url: createURL('deleteLoadBalancer'), + data: data, + async: true, + success: function(json) { + var jid = json.deleteloadbalancerresponse.jobid; + args.response.success({ + _custom: { jobId: jid } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } }, tabs: { details: { From c190b05057ee82a700e9387a316c2b7cd4d8ebda Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 14:04:14 -0700 Subject: [PATCH 329/412] CLOUDSTACK-2185: Allow custom 'ipaddr' argument when creating NIC IP --- ui/scripts/network.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index e451de2f10a..0eb17c99dca 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -1549,22 +1549,30 @@ add: { label: 'label.acquire.new.ip', addRow: 'true', + createForm: { + title: 'label.acquire.new.ip', + desc: 'message.acquire.new.ip', + fields: { + ipaddr: { label: 'label.ip.address' } + } + }, messages: { - confirm: function(args) { - return 'message.acquire.new.ip'; - }, notification: function(args) { return 'label.acquire.new.ip'; } }, action: function(args) { - var dataObj = {}; + var dataObj = { + nicId: args.context.nics[0].id + }; + + if (args.data.ipaddr) { + dataObj.ipaddr = args.data.ipaddr; + } $.ajax({ url: createURL('addIpToNic'), - data: { - nicId: args.context.nics[0].id - }, + data: dataObj, success: function(json) { args.response.success({ _custom: { From e78fa02da3c2ded599d4943df0e4561017f9b6bf Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 14:08:48 -0700 Subject: [PATCH 330/412] VPC UI: Fix router 'top' alignment --- ui/modules/vpc/vpc.css | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/modules/vpc/vpc.css b/ui/modules/vpc/vpc.css index 21b1ce8cb03..db57d94c5d6 100644 --- a/ui/modules/vpc/vpc.css +++ b/ui/modules/vpc/vpc.css @@ -271,10 +271,8 @@ float: left; /*+placement:shift 10px 176px;*/ position: relative; - left: 10px; - top: 176px; left: 0px; - top: 240px; + top: 237px; } .vpc-network-chart .tier-item.router .header { From 55148d90a8abb9e399d54e3d1c06c0584d1f27b0 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 14:16:00 -0700 Subject: [PATCH 331/412] CLOUDSTACK-2185: Display VM IP on static NAT detail page --- ui/scripts/ui-custom/ipRules.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/scripts/ui-custom/ipRules.js b/ui/scripts/ui-custom/ipRules.js index a2f721a882f..34b2398b6ac 100644 --- a/ui/scripts/ui-custom/ipRules.js +++ b/ui/scripts/ui-custom/ipRules.js @@ -70,10 +70,12 @@ response: { success: function(args) { var vmID = args.data.virtualmachineid; + var vmIP = args.data.vmipaddress; var vmName = args.data.virtualmachinename; $vmName.append( - $('').html('VM: ' + vmName) + $('').html('VM: ' + _s(vmName)), + $('').html('
    VM IP: ' + vmIP) ); $vmName.click(function() { From 7e8d19963d66e64219c512193e566944cc560c2d Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 4 Jun 2013 14:21:59 -0700 Subject: [PATCH 332/412] CLOUDSTACK-2842: UI - fix a JS error "elems is undefined" when popping up a dialog box that has zone dropdown and there is no zone created yet. --- ui/scripts/instances.js | 2 +- ui/scripts/network.js | 6 +++--- ui/scripts/storage.js | 2 +- ui/scripts/system.js | 10 +++++----- ui/scripts/templates.js | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 6a589baf83d..29e4ac3e29d 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -70,7 +70,7 @@ listAll: true }, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 0eb17c99dca..8d92b171702 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -558,7 +558,7 @@ listAll: true }, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -4394,7 +4394,7 @@ listAll: true }, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -4509,7 +4509,7 @@ url: createURL('listZones'), data: data, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; var advZones = $.grep(zones, function(zone) { return zone.networktype == 'Advanced' && ! zone.securitygroupsenabled; }); diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index e81633466bc..2c03d398d59 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -299,7 +299,7 @@ listAll: true }, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 283c7dffcc0..bb197c40899 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8708,7 +8708,7 @@ url: createURL('listZones'), data: data, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -9303,7 +9303,7 @@ url: createURL('listZones'), data: data, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -10504,7 +10504,7 @@ url: createURL('listZones'), data: data, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -11503,7 +11503,7 @@ url: createURL('listZones'), data: data, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -12417,7 +12417,7 @@ url: createURL('listZones'), data: data, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js index 91038b904fa..b78a94df34e 100644 --- a/ui/scripts/templates.js +++ b/ui/scripts/templates.js @@ -60,7 +60,7 @@ listAll: true }, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { @@ -1006,7 +1006,7 @@ listAll: true }, success: function(json) { - var zones = json.listzonesresponse.zone; + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; args.response.success({ data: $.map(zones, function(zone) { From 90df4e4df0ccece0693cc862c2f59214f99518cf Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Tue, 4 Jun 2013 14:24:49 -0700 Subject: [PATCH 333/412] CLOUDSTACK-2840: get the latest information from the DB about the number of rules in non-revoked state for the ip address when figuring out if the internal lb vm needs to be destroyed. Instead of relying on the information passed down by the NetworkManager as the network manager might pass only rules in transition state omitting the Active rules --- .../dao/ApplicationLoadBalancerRuleDao.java | 1 + .../ApplicationLoadBalancerRuleDaoImpl.java | 22 +++++++++++++++++-- .../element/InternalLoadBalancerElement.java | 18 +++++---------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDao.java b/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDao.java index c385e62f6ab..47f1d361216 100644 --- a/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDao.java +++ b/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDao.java @@ -31,5 +31,6 @@ public interface ApplicationLoadBalancerRuleDao extends GenericDao listBySourceIpAndNotRevoked(Ip sourceIp, long sourceNetworkId); List listLbIpsBySourceIpNetworkIdAndScheme(long sourceIpNetworkId, Scheme scheme); + long countBySourceIpAndNotRevoked(Ip sourceIp, long sourceIpNetworkId); } diff --git a/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDaoImpl.java b/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDaoImpl.java index 880c67e732c..6036b5a2d60 100644 --- a/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDaoImpl.java +++ b/engine/schema/src/org/apache/cloudstack/lb/dao/ApplicationLoadBalancerRuleDaoImpl.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO; import org.springframework.stereotype.Component; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.State; import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -41,8 +42,8 @@ public class ApplicationLoadBalancerRuleDaoImpl extends GenericDaoBase listIps; final GenericSearchBuilder CountBy; protected final SearchBuilder NotRevokedSearch; - - + final GenericSearchBuilder CountNotRevoked; + protected ApplicationLoadBalancerRuleDaoImpl() { AllFieldsSearch = createSearchBuilder(); @@ -69,6 +70,13 @@ public class ApplicationLoadBalancerRuleDaoImpl extends GenericDaoBase sc = CountNotRevoked.create(); + sc.setParameters("sourceIp", sourceIp); + sc.setParameters("sourceIpNetworkId", sourceIpNetworkId); + sc.setParameters("state", State.Revoke); + List results = customSearch(sc, null); + return results.get(0); + } + } diff --git a/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java b/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java index 4b9308b6606..14b616cb749 100644 --- a/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java +++ b/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java @@ -64,7 +64,6 @@ import com.cloud.network.element.VirtualRouterProviderVO; import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter.Role; -import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LoadBalancerContainer; import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.offering.NetworkOffering; @@ -394,23 +393,16 @@ public class InternalLoadBalancerElement extends AdapterBase implements LoadBala //1) Group rules by the source ip address as NetworkManager always passes the entire network lb config to the element Map> groupedRules = groupBySourceIp(rules); - //2) Count rules in revoke state Set vmsToDestroy = new HashSet(); for (Ip sourceIp : groupedRules.keySet()) { + //2) Check if there are non revoked rules for the source ip address List rulesToCheck = groupedRules.get(sourceIp); - int revoke = 0; - for (LoadBalancingRule ruleToCheck : rulesToCheck) { - if (ruleToCheck.getState() == FirewallRule.State.Revoke){ - revoke++; - } - } - - if (revoke == rulesToCheck.size()) { - s_logger.debug("Have to destroy internal lb vm for source ip " + sourceIp); + if (_appLbDao.countBySourceIpAndNotRevoked(sourceIp, rulesToCheck.get(0).getNetworkId()) == 0) { + s_logger.debug("Have to destroy internal lb vm for source ip " + sourceIp + " as it has 0 rules in non-Revoke state"); vmsToDestroy.add(sourceIp); - } - } + } + } return vmsToDestroy; } From 317c8e06066f3d713cc59b87c634fa6909be463a Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 15:03:15 -0700 Subject: [PATCH 334/412] CLOUDSTACK-2185: Display VM IP on PF multi-edit --- ui/css/cloudstack3.css | 18 ++++++++++++------ ui/scripts/network.js | 4 +--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 732efdbb714..0514586d1bf 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -38,9 +38,9 @@ div.toolbar, div.toolbar, .multi-wizard .progress ul li, .multi-wizard.zone-wizard .select-container .field .select-array-item { -/*\*/ + /*\*/ display: block; -/**/ + /**/ -height: 1px; } @@ -3461,7 +3461,7 @@ div.view table td.editable div.action.cancel { /*** Actions*/ table td.actions { cursor: default; -/*Make fixed*/ + /*Make fixed*/ width: 200px; min-width: 200px; max-width: 200px; @@ -4329,7 +4329,7 @@ Dialogs*/ margin: 6px 9px 9px; padding: 9px; color: #FFFFFF; -/*Adjusting the font size for proper display*/ + /*Adjusting the font size for proper display*/ font-size: 10px; border-left: 1px solid #6A6A6A; border-right: 1px solid #6A6A6A; @@ -7857,6 +7857,13 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t font-weight: bold; } +.multi-edit .data .data-body .data-item tr td.add-vm p { + text-indent: 0; + padding-left: 9px; + margin-top: 3px; + margin-bottom: 6px; +} + .multi-edit .data .data-body .data-item tr td.multi-actions .icon { /*+placement:shift -3px -2px;*/ position: relative; @@ -7975,7 +7982,7 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t .multi-edit .header-fields input[type=submit] { } -/* Sortable */ +/*Sortable*/ .multi-edit table tbody tr td.reorder, .multi-edit table thead tr th.reorder { width: 30px !important; @@ -7983,7 +7990,6 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t max-width: 30px !important; } - /*Security Rules*/ .security-rules .multi-edit input { width: 69px; diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 8d92b171702..f7d3ea43973 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -3692,9 +3692,7 @@ $.extend(item, { _itemData: $.map(data.listvirtualmachinesresponse.virtualmachine, function(vm) { return $.extend(vm, { - _displayName: vm.id == vm.displayname ? - (vm.instancename ? vm.instancename : vm.name) - : vm.displayname + _displayName: '

    VM: ' + vm.name + '

    ' + '

    IP: ' + item.vmguestip + '

    ' // Also display attached IP }); }), _context: { From e883526449398daa3bc386460c5f6c79007ed939 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Tue, 4 Jun 2013 15:25:50 -0700 Subject: [PATCH 335/412] CLOUDSTACK-1763: Better confirm message for acquire NIC IP --- client/WEB-INF/classes/resources/messages.properties | 1 + ui/css/cloudstack3.css | 4 ++++ ui/dictionary.jsp | 1 + ui/scripts/network.js | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index ce20fa49f1c..0de4cfff7d0 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +message.acquire.ip.nic=Please confirm that you would like to acquire a new secondary IP for this NIC.
    NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine. message.select.affinity.groups=Please select any affinity groups you want this VM to belong to: message.no.affinity.groups=You do not have any affinity groups. Please continue to the next step. label.action.delete.nic=Remove NIC diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 0514586d1bf..c2d0526910c 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -3748,6 +3748,10 @@ Dialogs*/ font-size: 15px; } +.ui-dialog div.form-container span.message br { + margin-bottom: 13px; +} + .ui-dialog div.form-container div.form-item { width: 100%; display: inline-block; diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index d7f7dd58d63..e1f9a30d5d4 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -25,6 +25,7 @@ under the License. <% long now = System.currentTimeMillis(); %>
    ').addClass('reorder').appendTo($thead); $('').addClass('reorder').appendTo($inputForm); $multi.find('.data-body').sortable({ - handle: '.action.moveDrag' + handle: '.action.moveDrag', + + update: function(event, ui) { + reorder.moveDrag.action({ + context: $.extend(true, {}, context, { + // Passes all rules, so that each index can be updated + multiRule: $multi.find('.data-item').map(function(index, item) { + return $(item).data('json-obj'); + }) + }), + response: { + success: function(args) { + } + } + }); + } }); } diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 5c2f0266d1b..d27e1fc44eb 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -30,7 +30,29 @@ reorder: { moveDrag: { action: function(args) { - args.response.success(); + $(args.context.multiRule.toArray().reverse()).map(function(index, rule) { + $.ajax({ + url: createURL('updateNetworkACLItem'), + data: { + id: rule.id, + number: index + 1 + }, + success: function(json) { + var pollTimer = setInterval(function() { + pollAsyncJobResult({ + _custom: { jobId: json.createnetworkaclresponse.jobid }, + complete: function() { + clearInterval(pollTimer); + }, + error: function(errorMsg) { + clearInterval(pollTimer); + cloudStack.dialog.notice(errorMsg); + } + }); + }, 1000); + } + }); + }); } } }, From 0eb73e9dec3ac47bbce4349430598c7b8d640c59 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 16:28:09 -0700 Subject: [PATCH 077/412] Update VPC tier dashboard - Remove 'Port forwarders' -- Getting IPs by PF is not supported by API - Split Public + Internal LB into separate items - Use API response 'count' for count number, instead of array '.length' to fix issues with larger results --- ui/modules/vpc/vpc.js | 4 +- ui/scripts/vpc.js | 258 ++++++++++++++++++++++-------------------- 2 files changed, 135 insertions(+), 127 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index cc0273fcbb1..e313b8ab2b5 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -168,7 +168,7 @@ $total.find('span').html(dashboardItem.totalMultiLine); $total.addClass('multiline'); } else { - $total.find('span').html(dashboardItem.total); + $total.find('span').html(dashboardItem.total ? dashboardItem.total : 0); } $dashboardItem.append($total, $name); @@ -183,7 +183,7 @@ var $section = $('
    '); if ($.isFunction(section)) { - section = cloudStack.vpc.sections[id]() + section = cloudStack.vpc.sections[id](); } if (section.listView) { diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index d27e1fc44eb..2445adcd721 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -341,7 +341,7 @@ }, tierStaticNATs: function() { - return cloudStack.vpc.ipAddresses.listView(); + return cloudStack.vpc.staticNatIpAddresses.listView(); }, // Internal load balancers @@ -1185,7 +1185,7 @@ ipAddresses: { listView: function() { var listView = $.extend(true, {}, cloudStack.sections.network.sections.ipAddresses); - + listView.listView.fields = { ipaddress: listView.listView.fields.ipaddress, zonename: listView.listView.fields.zonename, @@ -1196,6 +1196,35 @@ return listView; } }, + staticNatIpAddresses: { + listView: function() { + var listView = $.extend(true, {}, cloudStack.sections.network.sections.ipAddresses); + + listView.listView.fields = { + ipaddress: listView.listView.fields.ipaddress, + zonename: listView.listView.fields.zonename, + associatednetworkname: { label: 'label.network.name' }, + state: listView.listView.fields.state + }; + + listView.listView.dataProvider = function(args) { + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: { networkid: args.context.networks[0].id, isstaticnat: true }, + success: function(json) { + args.response.success({ + data: json.listpublicipaddressesresponse.publicipaddress + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + }; + + return listView; + } + }, acl: { multiEdit: aclMultiEdit, @@ -3171,29 +3200,16 @@ async: true, success: function(json) { var networks = json.listnetworksresponse.network; - var loadBalancers, networkACLLists, publicIpAddresses, privateGateways, vpnGateways, portForwardingRules; + var networkACLLists, publicIpAddresses, privateGateways, vpnGateways; var error = false; - // Get load balancers - $.ajax({ - url: createURL('listLoadBalancers'), - data: { vpcid: args.context.vpc[0].id }, - success: function(json) { - loadBalancers = json.listloadbalancerssresponse.loadbalancer ? - json.listloadbalancerssresponse.loadbalancer : []; - }, - error: function(json) { - error = true; - } - }); - // Get network ACL lists $.ajax({ url: createURL('listNetworkACLLists'), data: { 'vpc_id': args.context.vpc[0].id }, + async: false, success: function(json) { - networkACLLists = json.listnetworkacllistsresponse.networkacllist ? - json.listnetworkacllistsresponse.networkacllist : []; + networkACLLists = json.listnetworkacllistsresponse; }, error: function(json) { error = true; @@ -3203,23 +3219,10 @@ // Get public IPs $.ajax({ url: createURL('listPublicIpAddresses'), + async: false, data: { 'vpcid': args.context.vpc[0].id }, success: function(json) { - publicIpAddresses = json.listpublicipaddressesresponse.publicipaddress ? - json.listpublicipaddressesresponse.publicipaddress : []; - }, - error: function(json) { - error = true; - } - }); - - // Get port forwarding rules - $.ajax({ - url: createURL('listPortForwardingRules'), - data: { 'vpcid': args.context.vpc[0].id }, - success: function(json) { - portForwardingRules = json.listportforwardingrulesresponse.portforwardingrule ? - json.listportforwardingrulesresponse.portforwardingrule : []; + publicIpAddresses = json.listpublicipaddressesresponse; }, error: function(json) { error = true; @@ -3229,10 +3232,10 @@ // Get private gateways $.ajax({ url: createURL('listPrivateGateways'), + async: false, data: { 'vpcid': args.context.vpc[0].id }, success: function(json) { - privateGateways = json.listprivategatewaysresponse.privategateway ? - json.listprivategatewaysresponse.privategateway : []; + privateGateways = json.listprivategatewaysresponse; }, error: function(json) { error = true; @@ -3242,106 +3245,111 @@ // Get VPN gateways $.ajax({ url: createURL('listVpnGateways'), + async: false, data: { 'vpcid': args.context.vpc[0].id }, success: function(json) { - vpnGateways = json.listvpngatewaysresponse.vpngateway ? - json.listvpngatewaysresponse.vpngateway : []; + vpnGateways = json.listvpngatewaysresponse; }, error: function(json) { error = true; } }); - var dataTimer = setInterval(function() { - var complete = loadBalancers && networkACLLists && publicIpAddresses && privateGateways && vpnGateways; - - if (complete) { - clearInterval(dataTimer); - - if(networks != null && networks.length > 0) { - for(var i = 0; i < networks.length; i++) { - $.ajax({ - url: createURL("listVirtualMachines"), - dataType: "json", - data: { - networkid: networks[i].id, - listAll: true - }, - async: false, - success: function(json) { - networks[i].virtualMachines = json.listvirtualmachinesresponse.virtualmachine; - } - }); - } + args.response.success({ + routerDashboard: [ + { + id: 'privateGateways', + name: 'Private gateways', + total: privateGateways.count + }, + { + id: 'publicIPs', + name: 'Public IP addresses', + total: publicIpAddresses.count + }, + { + id: 'siteToSiteVPNs', + name: 'Site-to-site VPNs', + total: vpnGateways.count + }, + { + id: 'networkACLLists', + name: 'Network ACL lists', + total: networkACLLists.count } - args.response.success({ - routerDashboard: [ - { - id: 'privateGateways', - name: 'Private gateways', - total: privateGateways.length - }, - { - id: 'publicIPs', - name: 'Public IP addresses', - total: publicIpAddresses.length - }, - { - id: 'siteToSiteVPNs', - name: 'Site-to-site VPNs', - total: vpnGateways.length - }, - { - id: 'networkACLLists', - name: 'Network ACL lists', - total: networkACLLists.length - } - ], - tiers: $(networks).map(function(index, tier) { - var internalLoadBalancers = $.grep(loadBalancers, function(lb) { - return lb.networkid == tier.id; - }); - - return $.extend(tier, { - _dashboardItems: [ - { - id: 'tierLoadBalancers', - name: 'Load balancers', - totalMultiLine: internalLoadBalancers.length + ' Internal
    0 Public' - }, - { - id: 'tierPortForwarders', - name: 'Port forwarders', - total: $.grep(publicIpAddresses, function(ip) { - return $.grep( - portForwardingRules, - function(pf) { - return pf.ipaddressid == ip.id; - } - ).length ? true : false; - }).length - }, - { - id: 'tierStaticNATs', - name: 'Static NATs', - total: $.grep(publicIpAddresses, function(ip) { - return ip.associatednetworkid == tier.id && ip.isstaticnat; - }).length - }, - { - id: 'tierVMs', - name: 'Virtual Machines', - total: $.isArray(tier.virtualMachines) ? tier.virtualMachines.length : 0 - } - ] - }); - }) + ], + tiers: $(networks).map(function(index, tier) { + var internalLoadBalancers, publicLoadBalancers, virtualMachines, staticNatIps; + + // Get internal load balancers + $.ajax({ + url: createURL('listLoadBalancers'), + async: false, + data: { networkid: tier.id }, + success: function(json) { + internalLoadBalancers = json.listloadbalancerssresponse; + }, + error: function(json) { + error = true; + } }); - } else if (error) { - clearInterval(dataTimer); - cloudStack.dialog.notice({ message: 'Error loading dashboard data.' }); - } - }, 500); + + // Get VMs + $.ajax({ + url: createURL('listVirtualMachines'), + async: false, + data: { networkid: tier.id }, + success: function(json) { + virtualMachines = json.listvirtualmachinesresponse; + }, + error: function(json) { + error = true; + } + }); + + // Get static NAT IPs + $.ajax({ + url: createURL('listPublicIpAddresses'), + async: false, + data: { networkid: tier.id, isstaticnat: true }, + success: function(json) { + staticNatIps = json.listpublicipaddressesresponse; + }, + error: function(json) { + error = true; + } + }); + + return $.extend(tier, { + _dashboardItems: [ + { + id: 'tierLoadBalancers', + name: 'Internal LB', + total: internalLoadBalancers.count + }, + { + id: 'tierLoadBalancers', + name: 'Public LB', + total: 0 + }, + { + id: 'tierStaticNATs', + name: 'Static NATs', + total: staticNatIps.count + }, + { + id: 'tierVMs', + name: 'Virtual Machines', + total: virtualMachines.count + } + ] + }); + }) + }); + + if (error) { + cloudStack.dialog.notice({ message: 'Error loading dashboard data.' }); + } } }); } From 9071456596df15e3570c882c18143c93bbc38eee Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 22 May 2013 16:32:20 -0700 Subject: [PATCH 078/412] Split 'internal LB' / 'public LB' into separate list views, intead of subsections --- ui/scripts/vpc.js | 651 +++++++++++++++++++++++----------------------- 1 file changed, 320 insertions(+), 331 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 2445adcd721..fa0ed926551 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -345,349 +345,338 @@ }, // Internal load balancers - tierLoadBalancers: { - listView: true, - sectionSelect: { - label: 'Select LB type' - }, - sections: { - internalLoadBalancers: { - type: 'select', - title: 'Internal LB', - listView: { - id: 'internalLoadBalancers', - fields: { - name: { label: 'label.name' }, - sourceipaddress: { label: 'Source IP Address' } + internalLoadBalancers: { + title: 'Internal LB', + listView: { + id: 'internalLoadBalancers', + fields: { + name: { label: 'label.name' }, + sourceipaddress: { label: 'Source IP Address' } + }, + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + networkid: args.context.networks[0].id }, - dataProvider: function(args) { - $.ajax({ - url: createURL('listLoadBalancers'), - data: { - networkid: args.context.networks[0].id - }, - success: function(json) { - var items = json.listloadbalancerssresponse.loadbalancer; - args.response.success({ data: items }); - - } - }); - }, - actions: { - add: { - label: 'Add Internal LB', - createForm: { - title: 'Add Internal LB', - fields: { - name: { label: 'label.name', validation: { required: true } }, - description: { label: 'label.description', validation: { required: false } }, - sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, - sourceport: { label: 'sourceport', validation: { required: true } }, - instanceport: { label: 'instanceport', validation: { required: true } }, - algorithm: { - label: 'label.algorithm', - validation: { required: true }, - select: function(args) { - args.response.success({ - data: [ - { id: 'source', description: 'source' }, - { id: 'roundrobin', description: 'roundrobin' }, - { id: 'leastconn', description: 'leastconn' } - ] - }); - } - } + success: function(json) { + var items = json.listloadbalancerssresponse.loadbalancer; + args.response.success({ data: items }); + + } + }); + }, + actions: { + add: { + label: 'Add Internal LB', + createForm: { + title: 'Add Internal LB', + fields: { + name: { label: 'label.name', validation: { required: true } }, + description: { label: 'label.description', validation: { required: false } }, + sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, + sourceport: { label: 'sourceport', validation: { required: true } }, + instanceport: { label: 'instanceport', validation: { required: true } }, + algorithm: { + label: 'label.algorithm', + validation: { required: true }, + select: function(args) { + args.response.success({ + data: [ + { id: 'source', description: 'source' }, + { id: 'roundrobin', description: 'roundrobin' }, + { id: 'leastconn', description: 'leastconn' } + ] + }); } - }, - messages: { - notification: function(args) { - return 'Add Internal LB'; - } - }, - action: function(args) { - var data = { - name: args.data.name, - sourceport: args.data.sourceport, - instanceport: args.data.instanceport, - algorithm: args.data.algorithm, - networkid: args.context.networks[0].id, - sourceipaddressnetworkid: args.context.networks[0].id, - scheme: 'Internal' - }; - if(args.data.description != null && args.data.description.length > 0){ - $.extend(data, { - description: args.data.description - }); - } - if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ - $.extend(data, { - sourceipaddress: args.data.sourceipaddress - }); - } - $.ajax({ - url: createURL('createLoadBalancer'), - data: data, - success: function(json){ - var jid = json.createloadbalancerresponse.jobid; - args.response.success( - {_custom: - {jobId: jid, - getUpdatedItem: function(json) { - return json.queryasyncjobresultresponse.jobresult.loadbalancer; - } - } - } - ); - } - }); - - args.response.success(); - }, - notification: { - poll: pollAsyncJobResult - } + } } }, - - detailView: { - name: 'Internal Lb details', - actions: { - assignVm: { - label: 'Assign VMs to LB', - messages: { - notification: function(args) { return 'Assign VM to internal LB rule'; } - }, - listView: $.extend(true, {}, cloudStack.sections.instances.listView, { - type: 'checkbox', - filters: false, - dataProvider: function(args) { - $.ajax({ - url: createURL('listVirtualMachines'), - data: { - networkid: args.context.networks[0].id, - listAll: true - }, - success: function(json) { - var instances = json.listvirtualmachinesresponse.virtualmachine; - - // Pre-select existing instances in LB rule - $(instances).map(function(index, instance) { - instance._isSelected = $.grep( - args.context.internalLoadBalancers[0].loadbalancerinstance, - - function(lbInstance) { - return lbInstance.id == instance.id; - } - ).length ? true : false; - }); - - args.response.success({ - data: instances - }); - } - }); - } - }), - action: function(args) { - var vms = args.context.instances; - var array1 = []; - for(var i = 0; i < vms.length; i++) { - array1.push(vms[i].id); - } - var virtualmachineids = array1.join(','); - - $.ajax({ - url: createURL('assignToLoadBalancerRule'), - data: { - id: args.context.internalLoadBalancers[0].id, - virtualmachineids: virtualmachineids - }, - dataType: 'json', - async: true, - success: function(data) { - var jid = data.assigntoloadbalancerruleresponse.jobid; - args.response.success({ - _custom: { jobId: jid } - }); - } - }); - }, - notification: { - poll: pollAsyncJobResult - } - } - }, - tabs: { - details: { - title: 'label.details', - fields: [ - { - name: { label: 'label.name' } - }, - { - id: { label: 'label.id' } - } - ], - dataProvider: function(args) { - $.ajax({ - url: createURL('listLoadBalancers'), - data: { - id: args.context.internalLoadBalancers[0].id - }, - success: function(json) { - var item = json.listloadbalancerssresponse.loadbalancer[0]; - args.response.success({ data: item }); - } - }); - } - }, - rules: { - title: 'label.rules', - multiple: true, - fields: [ - { - sourceport: { label: 'Source Port' }, - instanceport: { label: 'Instance Port' } - } - ], - dataProvider: function(args) { - $.ajax({ - url: createURL('listLoadBalancers'), - data: { - id: args.context.internalLoadBalancers[0].id - }, - success: function(json) { - var item = json.listloadbalancerssresponse.loadbalancer[0]; - args.response.success({ data: item.loadbalancerrule }); - } - }); - } - } , - assignedVms: { - title: 'Assigned VMs', - multiple: true, - fields: [ - { - name: { label: 'label.name' }, - ipaddress: { label: 'label.ip.address' } - } - ], - dataProvider: function(args) { - $.ajax({ - url: createURL('listLoadBalancers'), - data: { - id: args.context.internalLoadBalancers[0].id - }, - success: function(json) { - var item = json.listloadbalancerssresponse.loadbalancer[0]; - args.response.success({ data: item.loadbalancerinstance }); - } - }); - } - } + messages: { + notification: function(args) { + return 'Add Internal LB'; + } + }, + action: function(args) { + var data = { + name: args.data.name, + sourceport: args.data.sourceport, + instanceport: args.data.instanceport, + algorithm: args.data.algorithm, + networkid: args.context.networks[0].id, + sourceipaddressnetworkid: args.context.networks[0].id, + scheme: 'Internal' + }; + if(args.data.description != null && args.data.description.length > 0){ + $.extend(data, { + description: args.data.description + }); + } + if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ + $.extend(data, { + sourceipaddress: args.data.sourceipaddress + }); } - } + $.ajax({ + url: createURL('createLoadBalancer'), + data: data, + success: function(json){ + var jid = json.createloadbalancerresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.loadbalancer; + } + } + } + ); + } + }); + + args.response.success(); + }, + notification: { + poll: pollAsyncJobResult + } } }, - - publicLoadBalancers: { - type: 'select', - title: 'Public LB', - listView: { - id: 'publicLoadBalancers', - fields: { - ipaddress: { label: 'label.ip.address' }, - type: { label: 'label.type' } - }, - dataProvider: function(args) { - args.response.success({ - data: [ - { ipaddress: '10.3.2.1', type: 'Internal' }, - { ipaddress: '10.3.2.3', type: 'Internal' }, - { ipaddress: '10.232.1.4', type: 'Public' } - ] - }); - }, - actions: { - add: { - label: 'Add Public LB', - createForm: { - title: 'Add Public LB', - fields: { - name: { label: 'label.name', validation: { required: true } }, - description: { label: 'label.description', validation: { required: false } }, - sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, - sourceport: { label: 'sourceport', validation: { required: true } }, - instanceport: { label: 'instanceport', validation: { required: true } }, - algorithm: { - label: 'label.algorithm', - validation: { required: true }, - select: function(args) { - args.response.success({ - data: [ - { id: 'source', description: 'source' }, - { id: 'roundrobin', description: 'roundrobin' }, - { id: 'leastconn', description: 'leastconn' } - ] - }); - } - } - } - }, - messages: { - notification: function(args) { - return 'Add Public LB'; - } - }, - action: function(args) { - var data = { - name: args.data.name, - sourceport: args.data.sourceport, - instanceport: args.data.instanceport, - algorithm: args.data.algorithm, - networkid: args.context.networks[0].id, - sourceipaddressnetworkid: args.context.networks[0].id, - scheme: 'Public' - }; - if(args.data.description != null && args.data.description.length > 0){ - $.extend(data, { - description: args.data.description - }); - } - if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ - $.extend(data, { - sourceipaddress: args.data.sourceipaddress - }); - } + + detailView: { + name: 'Internal Lb details', + actions: { + assignVm: { + label: 'Assign VMs to LB', + messages: { + notification: function(args) { return 'Assign VM to internal LB rule'; } + }, + listView: $.extend(true, {}, cloudStack.sections.instances.listView, { + type: 'checkbox', + filters: false, + dataProvider: function(args) { $.ajax({ - url: createURL('createLoadBalancer'), - data: data, - success: function(json){ - var jid = json.createloadbalancerresponse.jobid; - args.response.success( - {_custom: - {jobId: jid, - getUpdatedItem: function(json) { - return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; + url: createURL('listVirtualMachines'), + data: { + networkid: args.context.networks[0].id, + listAll: true + }, + success: function(json) { + var instances = json.listvirtualmachinesresponse.virtualmachine; + + // Pre-select existing instances in LB rule + $(instances).map(function(index, instance) { + instance._isSelected = $.grep( + args.context.internalLoadBalancers[0].loadbalancerinstance, + + function(lbInstance) { + return lbInstance.id == instance.id; } - } - } - ); + ).length ? true : false; + }); + + args.response.success({ + data: instances + }); } }); - - args.response.success(); - }, - notification: { - poll: function(args) { - args.complete({ - data: { - ipaddress: '10.0.3.2', - type: 'Internal' - } + } + }), + action: function(args) { + var vms = args.context.instances; + var array1 = []; + for(var i = 0; i < vms.length; i++) { + array1.push(vms[i].id); + } + var virtualmachineids = array1.join(','); + + $.ajax({ + url: createURL('assignToLoadBalancerRule'), + data: { + id: args.context.internalLoadBalancers[0].id, + virtualmachineids: virtualmachineids + }, + dataType: 'json', + async: true, + success: function(data) { + var jid = data.assigntoloadbalancerruleresponse.jobid; + args.response.success({ + _custom: { jobId: jid } }); } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + tabs: { + details: { + title: 'label.details', + fields: [ + { + name: { label: 'label.name' } + }, + { + id: { label: 'label.id' } } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + id: args.context.internalLoadBalancers[0].id + }, + success: function(json) { + var item = json.listloadbalancerssresponse.loadbalancer[0]; + args.response.success({ data: item }); + } + }); + } + }, + rules: { + title: 'label.rules', + multiple: true, + fields: [ + { + sourceport: { label: 'Source Port' }, + instanceport: { label: 'Instance Port' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + id: args.context.internalLoadBalancers[0].id + }, + success: function(json) { + var item = json.listloadbalancerssresponse.loadbalancer[0]; + args.response.success({ data: item.loadbalancerrule }); + } + }); + } + } , + assignedVms: { + title: 'Assigned VMs', + multiple: true, + fields: [ + { + name: { label: 'label.name' }, + ipaddress: { label: 'label.ip.address' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listLoadBalancers'), + data: { + id: args.context.internalLoadBalancers[0].id + }, + success: function(json) { + var item = json.listloadbalancerssresponse.loadbalancer[0]; + args.response.success({ data: item.loadbalancerinstance }); + } + }); + } + } + } + } + } + }, + publicLoadBalancers: { + title: 'Public LB', + listView: { + id: 'publicLoadBalancers', + fields: { + ipaddress: { label: 'label.ip.address' }, + type: { label: 'label.type' } + }, + dataProvider: function(args) { + args.response.success({ + data: [ + { ipaddress: '10.3.2.1', type: 'Internal' }, + { ipaddress: '10.3.2.3', type: 'Internal' }, + { ipaddress: '10.232.1.4', type: 'Public' } + ] + }); + }, + actions: { + add: { + label: 'Add Public LB', + createForm: { + title: 'Add Public LB', + fields: { + name: { label: 'label.name', validation: { required: true } }, + description: { label: 'label.description', validation: { required: false } }, + sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, + sourceport: { label: 'sourceport', validation: { required: true } }, + instanceport: { label: 'instanceport', validation: { required: true } }, + algorithm: { + label: 'label.algorithm', + validation: { required: true }, + select: function(args) { + args.response.success({ + data: [ + { id: 'source', description: 'source' }, + { id: 'roundrobin', description: 'roundrobin' }, + { id: 'leastconn', description: 'leastconn' } + ] + }); + } + } + } + }, + messages: { + notification: function(args) { + return 'Add Public LB'; + } + }, + action: function(args) { + var data = { + name: args.data.name, + sourceport: args.data.sourceport, + instanceport: args.data.instanceport, + algorithm: args.data.algorithm, + networkid: args.context.networks[0].id, + sourceipaddressnetworkid: args.context.networks[0].id, + scheme: 'Public' + }; + if(args.data.description != null && args.data.description.length > 0){ + $.extend(data, { + description: args.data.description + }); + } + if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ + $.extend(data, { + sourceipaddress: args.data.sourceipaddress + }); + } + $.ajax({ + url: createURL('createLoadBalancer'), + data: data, + success: function(json){ + var jid = json.createloadbalancerresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; + } + } + } + ); + } + }); + + args.response.success(); + }, + notification: { + poll: function(args) { + args.complete({ + data: { + ipaddress: '10.0.3.2', + type: 'Internal' + } + }); } } } @@ -3323,12 +3312,12 @@ return $.extend(tier, { _dashboardItems: [ { - id: 'tierLoadBalancers', + id: 'internalLoadBalancers', name: 'Internal LB', total: internalLoadBalancers.count }, { - id: 'tierLoadBalancers', + id: 'publicLoadBalancers', name: 'Public LB', total: 0 }, From d515602873b7cd0810ce602b274e5b4e57bebd0b Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 23 May 2013 10:39:32 -0700 Subject: [PATCH 079/412] CLOUDSTACK UI - VPC - site-to-site VPN - VPN connection - fix a JS error "elems is undefined" when clicking Create VPN Connection. --- ui/scripts/vpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index fa0ed926551..923c51039b6 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2060,7 +2060,7 @@ listAll: true }, success: function(json) { - var items = json.listvpncustomergatewaysresponse.vpncustomergateway; + var items = json.listvpncustomergatewaysresponse.vpncustomergateway ? json.listvpncustomergatewaysresponse.vpncustomergateway: []; args.response.success({ data: $.map(items, function(item) { return { From bc7d7e64c0f4cbcf3960b2387caa7cfff88e9c77 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 23 May 2013 11:39:27 -0700 Subject: [PATCH 080/412] CLOUDSTACK-747: UI - VPC tier - implement count of Public LB IP. --- ui/scripts/vpc.js | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 923c51039b6..ae3001af2f1 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -582,10 +582,10 @@ } } }, - publicLoadBalancers: { + publicLbIps: { title: 'Public LB', listView: { - id: 'publicLoadBalancers', + id: 'publicLbIps', fields: { ipaddress: { label: 'label.ip.address' }, type: { label: 'label.type' } @@ -3268,7 +3268,7 @@ } ], tiers: $(networks).map(function(index, tier) { - var internalLoadBalancers, publicLoadBalancers, virtualMachines, staticNatIps; + var internalLoadBalancers, publicLbIps, virtualMachines, staticNatIps; // Get internal load balancers $.ajax({ @@ -3283,19 +3283,19 @@ } }); - // Get VMs + // Get Public LB IPs $.ajax({ - url: createURL('listVirtualMachines'), + url: createURL('listPublicIpAddresses'), async: false, - data: { networkid: tier.id }, + data: { networkid: tier.id, forloadbalancing: true }, success: function(json) { - virtualMachines = json.listvirtualmachinesresponse; + publicLbIps = json.listpublicipaddressesresponse; }, error: function(json) { error = true; } - }); - + }); + // Get static NAT IPs $.ajax({ url: createURL('listPublicIpAddresses'), @@ -3309,6 +3309,19 @@ } }); + // Get VMs + $.ajax({ + url: createURL('listVirtualMachines'), + async: false, + data: { networkid: tier.id }, + success: function(json) { + virtualMachines = json.listvirtualmachinesresponse; + }, + error: function(json) { + error = true; + } + }); + return $.extend(tier, { _dashboardItems: [ { @@ -3317,9 +3330,9 @@ total: internalLoadBalancers.count }, { - id: 'publicLoadBalancers', - name: 'Public LB', - total: 0 + id: 'publicLbIps', + name: 'Public LB IP', + total: publicLbIps.count }, { id: 'tierStaticNATs', From a292bfbfcd8823d28c2f0f4b706be0d1ed9256f2 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Thu, 23 May 2013 11:53:25 -0700 Subject: [PATCH 081/412] CLOUDSTACK-747: UI - VPC tier - Public LB IP - populate listView of Public LB IP. --- ui/scripts/vpc.js | 119 +++++++++++----------------------------------- 1 file changed, 28 insertions(+), 91 deletions(-) diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index ae3001af2f1..2183aba55fd 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -587,100 +587,37 @@ listView: { id: 'publicLbIps', fields: { - ipaddress: { label: 'label.ip.address' }, - type: { label: 'label.type' } - }, - dataProvider: function(args) { - args.response.success({ - data: [ - { ipaddress: '10.3.2.1', type: 'Internal' }, - { ipaddress: '10.3.2.3', type: 'Internal' }, - { ipaddress: '10.232.1.4', type: 'Public' } - ] - }); - }, - actions: { - add: { - label: 'Add Public LB', - createForm: { - title: 'Add Public LB', - fields: { - name: { label: 'label.name', validation: { required: true } }, - description: { label: 'label.description', validation: { required: false } }, - sourceipaddress: { label: 'Source IP Address', validation: { required: false } }, - sourceport: { label: 'sourceport', validation: { required: true } }, - instanceport: { label: 'instanceport', validation: { required: true } }, - algorithm: { - label: 'label.algorithm', - validation: { required: true }, - select: function(args) { - args.response.success({ - data: [ - { id: 'source', description: 'source' }, - { id: 'roundrobin', description: 'roundrobin' }, - { id: 'leastconn', description: 'leastconn' } - ] - }); - } - } - } - }, - messages: { - notification: function(args) { - return 'Add Public LB'; - } - }, - action: function(args) { - var data = { - name: args.data.name, - sourceport: args.data.sourceport, - instanceport: args.data.instanceport, - algorithm: args.data.algorithm, - networkid: args.context.networks[0].id, - sourceipaddressnetworkid: args.context.networks[0].id, - scheme: 'Public' - }; - if(args.data.description != null && args.data.description.length > 0){ - $.extend(data, { - description: args.data.description - }); - } - if(args.data.sourceipaddress != null && args.data.sourceipaddress.length > 0){ - $.extend(data, { - sourceipaddress: args.data.sourceipaddress - }); - } - $.ajax({ - url: createURL('createLoadBalancer'), - data: data, - success: function(json){ - var jid = json.createloadbalancerresponse.jobid; - args.response.success( - {_custom: - {jobId: jid, - getUpdatedItem: function(json) { - return json.queryasyncjobresultresponse.jobresult.loadbalancerrule; - } - } - } - ); - } - }); - - args.response.success(); - }, - notification: { - poll: function(args) { - args.complete({ - data: { - ipaddress: '10.0.3.2', - type: 'Internal' - } - }); + ipaddress: { + label: 'label.ips', + converter: function(text, item) { + if (item.issourcenat) { + return text + ' [' + _l('label.source.nat') + ']'; } + + return text; } + }, + zonename: { label: 'label.zone' }, + virtualmachinedisplayname: { label: 'label.vm.name' }, + state: { + converter: function(str) { + // For localization + return str; + }, + label: 'label.state', indicator: { 'Allocated': 'on', 'Released': 'off' } } - } + }, + dataProvider: function(args) { + $.ajax({ + url: createURL('listPublicIpAddresses'), + async: false, + data: { networkid: args.context.networks[0].id, forloadbalancing: true }, + success: function(json) { + var items = json.listpublicipaddressesresponse; + args.response.success({ data: items }); + } + }); + } } }, From 07715b91afd44c1ebc0a6977923a0de0f5c1b1ee Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Thu, 23 May 2013 18:45:38 -0700 Subject: [PATCH 082/412] CLOUDSTACK-2639: Add flag for booting complete Don't execute any command before VR complete booting up process. --- .../config/etc/init.d/cloud-early-config | 3 +++ patches/systemvm/debian/config/etc/rc.local | 3 +++ .../opt/cloud/bin/get_template_version.sh | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/patches/systemvm/debian/config/etc/init.d/cloud-early-config b/patches/systemvm/debian/config/etc/init.d/cloud-early-config index d918670edab..ca3b970210b 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -33,6 +33,9 @@ PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" # Fix haproxy directory issue mkdir -p /var/lib/haproxy +# Clear boot up flag, it would be created by rc.local after boot up done +rm /var/cache/cloud/boot_up_done + [ -x /sbin/ifup ] || exit 0 . /lib/lsb/init-functions diff --git a/patches/systemvm/debian/config/etc/rc.local b/patches/systemvm/debian/config/etc/rc.local index cb434a23526..6119497596b 100755 --- a/patches/systemvm/debian/config/etc/rc.local +++ b/patches/systemvm/debian/config/etc/rc.local @@ -13,3 +13,6 @@ do logger -t cloud "Stopping $svc" service $svc stop done + +date > /var/cache/cloud/boot_up_done +logger -t cloud "Boot up process done" diff --git a/patches/systemvm/debian/config/opt/cloud/bin/get_template_version.sh b/patches/systemvm/debian/config/opt/cloud/bin/get_template_version.sh index 298bc380f82..233ec983f8e 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/get_template_version.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/get_template_version.sh @@ -16,5 +16,31 @@ # specific language governing permissions and limitations # under the License. +# As the last command send to router before any rules operation, wait until boot up done + +__TIMEOUT=60 +__FLAGFILE=/var/cache/cloud/boot_up_done +done=0 +for i in `seq 1 $(($__TIMEOUT * 10))` +do + if [ -e $__FLAGFILE ] + then + done=1 + break + fi + sleep 0.1 + if [ $((i % 10)) -eq 0 ] + then + logger -t cloud "Waiting for VM boot up done for one second" + fi +done + +if [ -z $done ] +then + # declare we failed booting process + echo "Waited 60 seconds but boot up haven't been completed" + exit +fi + echo -n `cat /etc/cloudstack-release`'&' cat /var/cache/cloud/cloud-scripts-signature From 2276760b3b38380c61097a15cc0cb3325a565b3d Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Fri, 24 May 2013 11:27:16 +0800 Subject: [PATCH 083/412] fix CLOUDSTACK-2649 createLoadBalancerRule API:both ports getting same value(public port value assigned to private port) --- .../src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 0118ca534e1..1d7b3121158 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -1395,7 +1395,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements Purpose.LoadBalancing, FirewallRuleType.User, networkId, null); LoadBalancerVO newRule = new LoadBalancerVO(xId, name, description, - sourceIpId, srcPort, srcPort, algorithm, + sourceIpId, srcPort, destPort, algorithm, networkId, ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId()); // verify rule is supported by Lb provider of the network From 704471e6deb1f72b6cc5a9fdcad15927dfc689a8 Mon Sep 17 00:00:00 2001 From: Jayapal Date: Mon, 20 May 2013 11:00:51 +0530 Subject: [PATCH 084/412] CLOUDSTACK-2386 Fixed srx firewall icmp rule Signed-off-by: Abhinandan Prateek --- .../JuniperSRXExternalFirewallElement.java | 1 + .../network/resource/JuniperSrxResource.java | 35 +++++++++---------- .../ExternalFirewallDeviceManagerImpl.java | 4 ++- utils/src/com/cloud/utils/net/NetUtils.java | 2 ++ 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java index a429306a680..c00d99abf88 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java @@ -242,6 +242,7 @@ PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, Junip // Set capabilities for Firewall service Map firewallCapabilities = new HashMap(); firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); + firewallCapabilities.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp,all"); firewallCapabilities.put(Capability.MultipleIps, "true"); firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress, egress"); diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java index a0068c3784c..fd065d58f87 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java @@ -750,7 +750,7 @@ public class JuniperSrxResource implements ServerResource { s_logger.debug(msg); } - private void shutdownGuestNetwork(GuestNetworkType type, long accountId, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException { + private void shutdownGuestNetwork(GuestNetworkType type, long accountId, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException { // Remove static and destination NAT rules for the guest network removeStaticAndDestNatRulesInPrivateVlan(privateVlanTag, privateGateway, privateCidrSize); @@ -766,10 +766,10 @@ public class JuniperSrxResource implements ServerResource { manageSourceNatPool(SrxCommand.DELETE, sourceNatIpAddress); manageProxyArp(SrxCommand.DELETE, publicVlanTag, sourceNatIpAddress); manageUsageFilter(SrxCommand.DELETE, _usageFilterIPOutput, privateSubnet, null, genIpFilterTermName(sourceNatIpAddress)); - manageUsageFilter(SrxCommand.DELETE, _usageFilterIPInput, sourceNatIpAddress, null, genIpFilterTermName(sourceNatIpAddress)); + manageUsageFilter(SrxCommand.DELETE, _usageFilterIPInput, sourceNatIpAddress, null, genIpFilterTermName(sourceNatIpAddress)); } else if (type.equals(GuestNetworkType.INTERFACE_NAT)) { manageUsageFilter(SrxCommand.DELETE, _usageFilterVlanOutput, null, privateVlanTag, null); - manageUsageFilter(SrxCommand.DELETE, _usageFilterVlanInput, null, privateVlanTag, null); + manageUsageFilter(SrxCommand.DELETE, _usageFilterVlanInput, null, privateVlanTag, null); } String msg = "Shut down guest network with type " + type +". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway; @@ -841,21 +841,24 @@ public class JuniperSrxResource implements ServerResource { commitConfiguration(); } else { for (FirewallRuleTO rule : rules) { - int startPort = 0, endPort = 0; + int startPort = NetUtils.PORT_RANGE_MIN, endPort = NetUtils.PORT_RANGE_MAX; if (rule.getSrcPortRange() != null) { startPort = rule.getSrcPortRange()[0]; endPort = rule.getSrcPortRange()[1]; - FirewallFilterTerm term = new FirewallFilterTerm(genIpIdentifier(rule.getSrcIp()) + "-" + String.valueOf(rule.getId()), rule.getSourceCidrList(), - rule.getSrcIp(), rule.getProtocol(), startPort, endPort, - rule.getIcmpType(), rule.getIcmpCode(), genIpIdentifier(rule.getSrcIp()) + _usageFilterIPInput.getCounterIdentifier()); - if (!rule.revoked()) { - manageFirewallFilter(SrxCommand.ADD, term, _publicZoneInputFilterName); - } else { - manageFirewallFilter(SrxCommand.DELETE, term, _publicZoneInputFilterName); - } } - commitConfiguration(); + + FirewallFilterTerm term = new FirewallFilterTerm(genIpIdentifier(rule.getSrcIp()) + "-" + String.valueOf(rule.getId()), rule.getSourceCidrList(), + rule.getSrcIp(), rule.getProtocol(), startPort, endPort, + rule.getIcmpType(), rule.getIcmpCode(), genIpIdentifier(rule.getSrcIp()) + _usageFilterIPInput.getCounterIdentifier()); + if (!rule.revoked()) { + manageProxyArp(SrxCommand.ADD, getVlanTag(rule.getSrcVlanTag()), rule.getSrcIp()); + manageFirewallFilter(SrxCommand.ADD, term, _publicZoneInputFilterName); + } else { + manageFirewallFilter(SrxCommand.DELETE, term, _publicZoneInputFilterName); + manageProxyArp(SrxCommand.DELETE, getVlanTag(rule.getSrcVlanTag()), rule.getSrcIp()); + } } + commitConfiguration(); } return new Answer(cmd); @@ -925,7 +928,6 @@ public class JuniperSrxResource implements ServerResource { } private void addStaticNatRule(Long publicVlanTag, String publicIp, String privateIp, List rules) throws ExecutionException { - manageProxyArp(SrxCommand.ADD, publicVlanTag, publicIp); manageStaticNatRule(SrxCommand.ADD, publicIp, privateIp); manageAddressBookEntry(SrxCommand.ADD, _privateZone, privateIp, null); @@ -937,7 +939,6 @@ public class JuniperSrxResource implements ServerResource { private void removeStaticNatRule(Long publicVlanTag, String publicIp, String privateIp) throws ExecutionException { manageStaticNatRule(SrxCommand.DELETE, publicIp, privateIp); - manageProxyArp(SrxCommand.DELETE, publicVlanTag, publicIp); // Remove any existing security policy and clean up applications removeSecurityPolicyAndApplications(SecurityPolicyType.STATIC_NAT, privateIp); @@ -1196,8 +1197,7 @@ public class JuniperSrxResource implements ServerResource { } private void addDestinationNatRule(Protocol protocol, Long publicVlanTag, String publicIp, String privateIp, int srcPortStart, int srcPortEnd, int destPortStart, int destPortEnd) throws ExecutionException { - manageProxyArp(SrxCommand.ADD, publicVlanTag, publicIp); - + int offset = 0; for (int srcPort = srcPortStart; srcPort <= srcPortEnd; srcPort++) { int destPort = destPortStart + offset; @@ -1220,7 +1220,6 @@ public class JuniperSrxResource implements ServerResource { private void removeDestinationNatRule(Long publicVlanTag, String publicIp, String privateIp, int srcPort, int destPort) throws ExecutionException { manageDestinationNatRule(SrxCommand.DELETE, publicIp, privateIp, srcPort, destPort); manageDestinationNatPool(SrxCommand.DELETE, privateIp, destPort); - manageProxyArp(SrxCommand.DELETE, publicVlanTag, publicIp); removeSecurityPolicyAndApplications(SecurityPolicyType.DESTINATION_NAT, privateIp); diff --git a/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java index 4a90a77f428..9d24e478fe2 100644 --- a/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java @@ -550,7 +550,9 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl ruleTO = new FirewallRuleTO(rule, guestVlanTag, rule.getTrafficType()); } else { IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); - ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr()); + Vlan vlan = _vlanDao.findById(sourceIp.getVlanId()); + + ruleTO = new FirewallRuleTO(rule, vlan.getVlanTag(), sourceIp.getAddress().addr()); } rulesTO.add(ruleTO); } diff --git a/utils/src/com/cloud/utils/net/NetUtils.java b/utils/src/com/cloud/utils/net/NetUtils.java index 37dcef382aa..8c094c85088 100755 --- a/utils/src/com/cloud/utils/net/NetUtils.java +++ b/utils/src/com/cloud/utils/net/NetUtils.java @@ -61,6 +61,8 @@ public class NetUtils { public final static String ALL_PROTO = "all"; public final static String ALL_CIDRS = "0.0.0.0/0"; + public final static int PORT_RANGE_MIN = 0; + public final static int PORT_RANGE_MAX = 65535; public final static int DEFAULT_AUTOSCALE_VM_DESTROY_TIME = 2 * 60; // Grace period before Vm is destroyed public final static int DEFAULT_AUTOSCALE_POLICY_INTERVAL_TIME = 30; From 51cf797d5ef69060c95dcb0fce47b2d96f16e26e Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Thu, 23 May 2013 22:57:13 -0700 Subject: [PATCH 085/412] CLOUDSTACK-2659 Advanced Zone - during VM deployment need to check network permissions for shared account-specific guest networks too Changes: - Check network permissions for account specific shared networks as well - Changes to error to include network uuid instead of id --- server/src/com/cloud/network/NetworkModelImpl.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index 8971f8c163b..010cb9d5c54 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -32,6 +32,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -1488,24 +1489,25 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { @Override public void checkNetworkPermissions(Account owner, Network network) { // Perform account permission check - if (network.getGuestType() != Network.GuestType.Shared) { + if (network.getGuestType() != Network.GuestType.Shared + || (network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Account)) { AccountVO networkOwner = _accountDao.findById(network.getAccountId()); if(networkOwner == null) - throw new PermissionDeniedException("Unable to use network with id= " + network.getId() + ", network does not have an owner"); + throw new PermissionDeniedException("Unable to use network with id= " + ((network != null)? ((NetworkVO)network).getUuid() : "") + ", network does not have an owner"); if(owner.getType() != Account.ACCOUNT_TYPE_PROJECT && networkOwner.getType() == Account.ACCOUNT_TYPE_PROJECT){ if(!_projectAccountDao.canAccessProjectAccount(owner.getAccountId(), network.getAccountId())){ - throw new PermissionDeniedException("Unable to use network with id= " + network.getId() + ", permission denied"); + throw new PermissionDeniedException("Unable to use network with id= " + ((network != null)? ((NetworkVO)network).getUuid() : "") + ", permission denied"); } }else{ List networkMap = _networksDao.listBy(owner.getId(), network.getId()); if (networkMap == null || networkMap.isEmpty()) { - throw new PermissionDeniedException("Unable to use network with id= " + network.getId() + ", permission denied"); + throw new PermissionDeniedException("Unable to use network with id= " + ((network != null)? ((NetworkVO)network).getUuid() : "") + ", permission denied"); } } } else { if (!isNetworkAvailableInDomain(network.getId(), owner.getDomainId())) { - throw new PermissionDeniedException("Shared network id=" + network.getUuid() + " is not available in domain id=" + owner.getDomainId()); + throw new PermissionDeniedException("Shared network id=" + ((network != null)? ((NetworkVO)network).getUuid() : "") + " is not available in domain id=" + owner.getDomainId()); } } } From 3e02a76f001b14ad25e5fdc55efc137457e5e52b Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Fri, 24 May 2013 11:09:33 +0530 Subject: [PATCH 086/412] CLOUDSTACK-528: Config value has to be encrypted for Hidden category configs --- .../com/cloud/upgrade/dao/Upgrade302to40.java | 41 +++++++++++++++++++ setup/db/db/schema-302to40.sql | 3 -- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java index ecda872dfa4..6f31fdd2b8e 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java @@ -18,6 +18,7 @@ package com.cloud.upgrade.dao; import java.io.File; +import java.io.UnsupportedEncodingException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -72,6 +73,7 @@ public class Upgrade302to40 extends Upgrade30xBase implements DbUpgrade { fixForeignKeys(conn); setupExternalNetworkDevices(conn); fixZoneUsingExternalDevices(conn); + encryptConfig(conn); } @Override @@ -1079,4 +1081,43 @@ public class Upgrade302to40 extends Upgrade30xBase implements DbUpgrade { s_logger.info("Successfully upgraded networks using F5 and SRX devices to have a entry in the network_external_lb_device_map and network_external_firewall_device_map"); } } + + private void encryptConfig(Connection conn){ + //Encrypt config params and change category to Hidden + s_logger.debug("Encrypting Config values"); + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = conn.prepareStatement("select name, value from `cloud`.`configuration` where name in ('router.ram.size', 'secondary.storage.vm', 'security.hash.key') and category <> 'Hidden'"); + rs = pstmt.executeQuery(); + while (rs.next()) { + String name = rs.getString(1); + String value = rs.getString(2); + if (value == null) { + continue; + } + String encryptedValue = DBEncryptionUtil.encrypt(value); + pstmt = conn.prepareStatement("update `cloud`.`configuration` set value=?, category = 'Hidden' where name=?"); + pstmt.setBytes(1, encryptedValue.getBytes("UTF-8")); + pstmt.setString(2, name); + pstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable encrypt configuration values ", e); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable encrypt configuration values ", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + s_logger.debug("Done encrypting Config values"); + } } diff --git a/setup/db/db/schema-302to40.sql b/setup/db/db/schema-302to40.sql index 7fa73483db6..832228cb434 100644 --- a/setup/db/db/schema-302to40.sql +++ b/setup/db/db/schema-302to40.sql @@ -114,9 +114,6 @@ UPDATE `cloud`.`configuration` set component='NetworkManager' where name='router UPDATE `cloud`.`configuration` set component='NetworkManager' where name='router.template.id'; UPDATE `cloud`.`configuration` set category='Advanced' where name='capacity.skipcounting.hours'; UPDATE `cloud`.`configuration` set category='Advanced' where name='use.local.storage'; -UPDATE `cloud`.`configuration` set category='Hidden' where name='router.ram.size'; -UPDATE `cloud`.`configuration` set category='Hidden' where name='secondary.storage.vm'; -UPDATE `cloud`.`configuration` set category='Hidden' where name='security.hash.key'; UPDATE `cloud`.`configuration` set description = 'Percentage (as a value between 0 and 1) of local storage utilization above which alerts will be sent about low local storage available.' where name = 'cluster.localStorage.capacity.notificationthreshold'; DELETE FROM `cloud`.`configuration` WHERE name='direct.agent.pool.size'; From 3b48299a0395916ad3dc8a95b6d98afd96ecf251 Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Fri, 24 May 2013 12:37:09 +0530 Subject: [PATCH 087/412] CLOUDSTACK-2664: Fixed delete query. Added log message when CloudRuntimeException is thrown during upgrade --- .../schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java | 5 ++++- engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java index 9bc0ba599c2..e23815b7d28 100755 --- a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -261,7 +261,10 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { } txn.commit(); - } finally { + } catch (CloudRuntimeException e){ + s_logger.error("Unable to upgrade the database", e); + throw new CloudRuntimeException("Unable to upgrade the database", e); + }finally { txn.close(); } } diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index 95abe5f161e..f28d49687ea 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -759,7 +759,7 @@ public class Upgrade410to420 implements DbUpgrade { while (rs.next()) { long id = rs.getLong(1); // remove Firewall service for SG shared network offering - pstmt = conn.prepareStatement("DELETE `cloud`.`ntwk_offering_service_map` where network_offering_id=? and service='Firewall'"); + pstmt = conn.prepareStatement("DELETE from `cloud`.`ntwk_offering_service_map` where network_offering_id=? and service='Firewall'"); pstmt.setLong(1, id); pstmt.executeUpdate(); } From b2be0dbf6b48aa1d7a7e9cc7b5ab92dac84516b0 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 24 May 2013 11:12:39 +0530 Subject: [PATCH 088/412] Fixing incorrect ipaddress references test_network: references to ipaddress are ipaddress.ipaddress Added more logging to volume tests Signed-off-by: Prasanna Santhanam --- test/integration/smoke/test_network.py | 34 ++++++++++++-------------- test/integration/smoke/test_volumes.py | 2 +- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py index 61ddf46e9ef..9dfdc6e59d2 100644 --- a/test/integration/smoke/test_network.py +++ b/test/integration/smoke/test_network.py @@ -971,12 +971,12 @@ class TestLoadBalancingRule(cloudstackTestCase): try: self.debug("SSHing into IP address: %s after adding VMs (ID: %s , %s)" % ( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.vm_1.id, self.vm_2.id )) ssh_1 = remoteSSHClient( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.services['lbrule']["publicport"], self.vm_1.username, self.vm_1.password @@ -990,12 +990,12 @@ class TestLoadBalancingRule(cloudstackTestCase): self.debug("SSHing again into IP address: %s with VMs (ID: %s , %s) added to LB rule" % ( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.vm_1.id, self.vm_2.id )) ssh_2 = remoteSSHClient( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.services['lbrule']["publicport"], self.vm_1.username, self.vm_1.password @@ -1019,11 +1019,11 @@ class TestLoadBalancingRule(cloudstackTestCase): self.debug("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % ( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.vm_2.id )) ssh_1 = remoteSSHClient( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.services['lbrule']["publicport"], self.vm_1.username, self.vm_1.password @@ -1033,7 +1033,7 @@ class TestLoadBalancingRule(cloudstackTestCase): self.debug("Hostnames after removing VM2: %s" % str(hostnames)) except Exception as e: self.fail("%s: SSH failed for VM with IP Address: %s" % - (e, self.non_src_nat_ip.ipaddress)) + (e, self.non_src_nat_ip.ipaddress.ipaddress)) self.assertIn( self.vm_1.name, @@ -1045,11 +1045,11 @@ class TestLoadBalancingRule(cloudstackTestCase): with self.assertRaises(Exception): self.fail("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % ( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.vm_1.id )) ssh_1 = remoteSSHClient( - self.non_src_nat_ip.ipaddress, + self.non_src_nat_ip.ipaddress.ipaddress, self.services['lbrule']["publicport"], self.vm_1.username, self.vm_1.password @@ -1203,7 +1203,7 @@ class TestRebootRouter(cloudstackTestCase): self.debug("SSH into VM (ID : %s ) after reboot" % self.vm_1.id) remoteSSHClient( - self.nat_rule.ipaddress, + self.nat_rule.ipaddress.ipaddress, self.services["natrule"]["publicport"], self.vm_1.username, self.vm_1.password @@ -1211,8 +1211,7 @@ class TestRebootRouter(cloudstackTestCase): except Exception as e: self.fail( "SSH Access failed for %s: %s" % \ - (self.vm_1.ipaddress, e) - ) + (self.vm_1.ipaddress, e)) return def tearDown(self): @@ -1579,7 +1578,6 @@ class TestReleaseIP(cloudstackTestCase): "Check if disassociated IP Address is no longer available" ) - self.debug("List NAT Rule response" + str(list_nat_rule)) # ListPortForwardingRules should not list # associated rules with Public IP address try: @@ -1587,18 +1585,18 @@ class TestReleaseIP(cloudstackTestCase): self.apiclient, id=self.nat_rule.id ) + self.debug("List NAT Rule response" + str(list_nat_rule)) except cloudstackAPIException: self.debug("Port Forwarding Rule is deleted") # listLoadBalancerRules should not list # associated rules with Public IP address - self.debug("List LB Rule response" + str(list_lb_rule)) try: list_lb_rule = list_lb_rules( self.apiclient, id=self.lb_rule.id ) - + self.debug("List LB Rule response" + str(list_lb_rule)) except cloudstackAPIException: self.debug("Port Forwarding Rule is deleted") @@ -1658,7 +1656,6 @@ class TestDeleteAccount(cloudstackTestCase): try: src_nat_ip_addr = src_nat_ip_addrs[0] - except Exception as e: self.fail("SSH failed for VM with IP: %s" % src_nat_ip_addr.ipaddress) @@ -1740,10 +1737,9 @@ class TestDeleteAccount(cloudstackTestCase): "Check routers are properly deleted." ) except Exception as e: - raise Exception( - "Exception raised while fetching routers for account: %s" % - self.account.name) + "Encountered %s raised while fetching routers for account: %s" % (e, + self.account.name)) return def tearDown(self): diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index 89b013a516f..7987136f88e 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -209,8 +209,8 @@ class TestCreateVolume(cloudstackTestCase): ) try: ssh = self.virtual_machine.get_ssh_client() + self.debug("Rebooting VM %s" % self.virtual_machine.id) ssh.execute("reboot") - except Exception as e: self.fail("SSH access failed for VM %s - %s" % (self.virtual_machine.ipaddress, e)) From a6d13cb7994e99203f99113ae901d110af8ba417 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 24 May 2013 14:22:47 +0530 Subject: [PATCH 089/412] Unwrap the test docstring for the testrunner Signed-off-by: Prasanna Santhanam --- test/integration/smoke/test_network.py | 222 ++++++++++++------------- 1 file changed, 110 insertions(+), 112 deletions(-) diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py index 9dfdc6e59d2..eb33c630ccf 100644 --- a/test/integration/smoke/test_network.py +++ b/test/integration/smoke/test_network.py @@ -189,8 +189,7 @@ class TestPublicIP(cloudstackTestCase): @attr(tags = ["advanced", "advancedns", "smoke"]) def test_public_ip_admin_account(self): - """Test for Associate/Disassociate - public IP address for admin account""" + """Test for Associate/Disassociate public IP address for admin account""" # Validate the following: # 1. listPubliIpAddresses API returns the list of acquired addresses @@ -240,8 +239,7 @@ class TestPublicIP(cloudstackTestCase): @attr(tags = ["advanced", "advancedns", "smoke"]) def test_public_ip_user_account(self): - """Test for Associate/Disassociate - public IP address for user account""" + """Test for Associate/Disassociate public IP address for user account""" # Validate the following: # 1. listPubliIpAddresses API returns the list of acquired addresses @@ -885,102 +883,102 @@ class TestLoadBalancingRule(cloudstackTestCase): # Check if VM is in Running state before creating LB rule vm_response = VirtualMachine.list( - self.apiclient, - account=self.account.name, - domainid=self.account.domainid - ) + self.apiclient, + account=self.account.name, + domainid=self.account.domainid + ) self.assertEqual( - isinstance(vm_response, list), - True, - "Check list VM returns a valid list" - ) + isinstance(vm_response, list), + True, + "Check list VM returns a valid list" + ) self.assertNotEqual( - len(vm_response), - 0, - "Check Port Forwarding Rule is created" - ) + len(vm_response), + 0, + "Check Port Forwarding Rule is created" + ) for vm in vm_response: self.assertEqual( - vm.state, - 'Running', - "VM state should be Running before creating a NAT rule." - ) + vm.state, + 'Running', + "VM state should be Running before creating a NAT rule." + ) #Create Load Balancer rule and assign VMs to rule lb_rule = LoadBalancerRule.create( - self.apiclient, - self.services["lbrule"], - self.non_src_nat_ip.ipaddress.id, - accountid=self.account.name - ) + self.apiclient, + self.services["lbrule"], + self.non_src_nat_ip.ipaddress.id, + accountid=self.account.name + ) self.cleanup.append(lb_rule) lb_rule.assign(self.apiclient, [self.vm_1, self.vm_2]) lb_rules = list_lb_rules( - self.apiclient, - id=lb_rule.id - ) + self.apiclient, + id=lb_rule.id + ) self.assertEqual( - isinstance(lb_rules, list), - True, - "Check list response returns a valid list" - ) + isinstance(lb_rules, list), + True, + "Check list response returns a valid list" + ) #verify listLoadBalancerRules lists the added load balancing rule self.assertNotEqual( - len(lb_rules), - 0, - "Check Load Balancer Rule in its List" - ) + len(lb_rules), + 0, + "Check Load Balancer Rule in its List" + ) self.assertEqual( - lb_rules[0].id, - lb_rule.id, - "Check List Load Balancer Rules returns valid Rule" - ) + lb_rules[0].id, + lb_rule.id, + "Check List Load Balancer Rules returns valid Rule" + ) # listLoadBalancerRuleInstances should list # all instances associated with that LB rule lb_instance_rules = list_lb_instances( - self.apiclient, - id=lb_rule.id - ) + self.apiclient, + id=lb_rule.id + ) self.assertEqual( - isinstance(lb_instance_rules, list), - True, - "Check list response returns a valid list" - ) + isinstance(lb_instance_rules, list), + True, + "Check list response returns a valid list" + ) self.assertNotEqual( - len(lb_instance_rules), - 0, - "Check Load Balancer instances Rule in its List" - ) + len(lb_instance_rules), + 0, + "Check Load Balancer instances Rule in its List" + ) self.assertIn( - lb_instance_rules[0].id, - [self.vm_1.id, self.vm_2.id], - "Check List Load Balancer instances Rules returns valid VM ID" - ) + lb_instance_rules[0].id, + [self.vm_1.id, self.vm_2.id], + "Check List Load Balancer instances Rules returns valid VM ID" + ) self.assertIn( - lb_instance_rules[1].id, - [self.vm_1.id, self.vm_2.id], - "Check List Load Balancer instances Rules returns valid VM ID" - ) + lb_instance_rules[1].id, + [self.vm_1.id, self.vm_2.id], + "Check List Load Balancer instances Rules returns valid VM ID" + ) try: self.debug("SSHing into IP address: %s after adding VMs (ID: %s , %s)" % - ( - self.non_src_nat_ip.ipaddress.ipaddress, - self.vm_1.id, - self.vm_2.id - )) + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_1.id, + self.vm_2.id + )) ssh_1 = remoteSSHClient( - self.non_src_nat_ip.ipaddress.ipaddress, - self.services['lbrule']["publicport"], - self.vm_1.username, - self.vm_1.password - ) + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) # If Round Robin Algorithm is chosen, # each ssh command should alternate between VMs @@ -989,71 +987,71 @@ class TestLoadBalancingRule(cloudstackTestCase): time.sleep(self.services["lb_switch_wait"]) self.debug("SSHing again into IP address: %s with VMs (ID: %s , %s) added to LB rule" % - ( - self.non_src_nat_ip.ipaddress.ipaddress, - self.vm_1.id, - self.vm_2.id - )) + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_1.id, + self.vm_2.id + )) ssh_2 = remoteSSHClient( - self.non_src_nat_ip.ipaddress.ipaddress, - self.services['lbrule']["publicport"], - self.vm_1.username, - self.vm_1.password - ) + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) hostnames.append(ssh_2.execute("hostname")[0]) self.debug("Hostnames after adding 2 VMs to LB rule: %s" % str(hostnames)) self.assertIn( - self.vm_1.name, - hostnames, - "Check if ssh succeeded for server1" - ) + self.vm_1.name, + hostnames, + "Check if ssh succeeded for server1" + ) self.assertIn( - self.vm_2.name, - hostnames, - "Check if ssh succeeded for server2" - ) + self.vm_2.name, + hostnames, + "Check if ssh succeeded for server2" + ) #SSH should pass till there is a last VM associated with LB rule lb_rule.remove(self.apiclient, [self.vm_2]) self.debug("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % - ( - self.non_src_nat_ip.ipaddress.ipaddress, - self.vm_2.id - )) + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_2.id + )) ssh_1 = remoteSSHClient( - self.non_src_nat_ip.ipaddress.ipaddress, - self.services['lbrule']["publicport"], - self.vm_1.username, - self.vm_1.password - ) + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) hostnames.append(ssh_1.execute("hostname")[0]) self.debug("Hostnames after removing VM2: %s" % str(hostnames)) except Exception as e: self.fail("%s: SSH failed for VM with IP Address: %s" % - (e, self.non_src_nat_ip.ipaddress.ipaddress)) + (e, self.non_src_nat_ip.ipaddress.ipaddress)) self.assertIn( - self.vm_1.name, - hostnames, - "Check if ssh succeeded for server1" - ) + self.vm_1.name, + hostnames, + "Check if ssh succeeded for server1" + ) lb_rule.remove(self.apiclient, [self.vm_1]) with self.assertRaises(Exception): - self.fail("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % - ( - self.non_src_nat_ip.ipaddress.ipaddress, - self.vm_1.id - )) + self.debug("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_1.id + )) ssh_1 = remoteSSHClient( - self.non_src_nat_ip.ipaddress.ipaddress, - self.services['lbrule']["publicport"], - self.vm_1.username, - self.vm_1.password - ) + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) ssh_1.execute("hostname")[0] return @@ -1556,7 +1554,7 @@ class TestReleaseIP(cloudstackTestCase): @attr(tags = ["advanced", "advancedns", "smoke"]) def test_releaseIP(self): - """Test for Associate/Disassociate public IP address""" + """Test for release public IP address""" self.debug("Deleting Public IP : %s" % self.ip_addr.id) From 09c6030ae9fd759b59634a10e98eb2698a848539 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 24 May 2013 14:23:14 +0530 Subject: [PATCH 090/412] Correct the resize volume tests Resize would fail on XenServer if the VM isn't stopped before resizing. Ensuring VM stop based on the hypervisor host detected that the VM is resident on. Signed-off-by: Prasanna Santhanam --- test/integration/smoke/test_volumes.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index 7987136f88e..d9c808a01d9 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -536,7 +536,7 @@ class TestVolumes(cloudstackTestCase): @attr(tags = ["advanced", "advancedns", "smoke", "basic"]) def test_07_resize_fail(self): - """Verify invalid options fail to Resize a volume""" + """Test resize (negative) non-existent volume""" # Verify the size is the new size is what we wanted it to be. self.debug("Fail Resize Volume ID: %s" % self.volume.id) @@ -584,7 +584,12 @@ class TestVolumes(cloudstackTestCase): self.virtual_machine.attach_volume(self.apiClient, self.volume) self.attached = True #stop the vm if it is on xenserver - if self.services['hypervisor'].lower() == "xenserver": + hosts = Host.list(self.apiClient, id=self.virtual_machine.hostid) + self.assertTrue(isinstance(hosts, list)) + self.assertTrue(len(hosts) > 0) + self.debug("Found %s host" % hosts[0].hypervisor) + + if hosts[0].hypervisor == "XenServer": self.virtual_machine.stop(self.apiClient) self.apiClient.resizeVolume(cmd) @@ -610,23 +615,28 @@ class TestVolumes(cloudstackTestCase): True, "Verify the volume did not resize" ) - if self.services['hypervisor'].lower() == "xenserver": + if hosts[0].hypervisor == "XenServer": self.virtual_machine.start(self.apiClient) @attr(tags = ["advanced", "advancedns", "smoke", "basic"]) def test_08_resize_volume(self): - """Resize a volume""" + """Test resize a volume""" # Verify the size is the new size is what we wanted it to be. self.debug( "Attaching volume (ID: %s) to VM (ID: %s)" % ( self.volume.id, self.virtual_machine.id )) + self.virtual_machine.attach_volume(self.apiClient, self.volume) self.attached = True + hosts = Host.list(self.apiClient, id=self.virtual_machine.hostid) + self.assertTrue(isinstance(hosts, list)) + self.assertTrue(len(hosts) > 0) + self.debug("Found %s host" % hosts[0].hypervisor) - if self.services['hypervisor'].lower() == "xenserver": + if hosts[0].hypervisor == "XenServer": self.virtual_machine.stop(self.apiClient) self.debug("Resize Volume ID: %s" % self.volume.id) @@ -659,7 +669,9 @@ class TestVolumes(cloudstackTestCase): "Check if the volume resized appropriately" ) - if self.services['hypervisor'].lower() == "xenserver": + #start the vm if it is on xenserver + + if hosts[0].hypervisor == "XenServer": self.virtual_machine.start(self.apiClient) @attr(tags = ["advanced", "advancedns", "smoke","basic"]) From 47a562a2c043c32fdf1f3108552cfce170b8b27e Mon Sep 17 00:00:00 2001 From: Bharat Kumar Date: Tue, 21 May 2013 12:21:01 +0530 Subject: [PATCH 091/412] Cloudstack-2548: Failed to delete public IP range Public IP range deletion after it is dedicated fails with IP range being used for DHCP offers. Signed-off-by: Prasanna Santhanam --- .../cloud/configuration/ConfigurationManagerImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 47c54822adb..214e292963f 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3071,9 +3071,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + } else { + // when there is no dhcp support in the network. + if (!deletePublicIPRange(vlanDbId)) { + return false; + } + _vlanDao.expunge(vlanDbId); + return true; } } - throw new InvalidParameterValueException("One of the ips in the range is used to provide Dhcp service to this subnet. cannot delete this range as "); + return false; } From a23f151df57a16457587207e612b3f26911806e6 Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Fri, 24 May 2013 14:41:17 +0530 Subject: [PATCH 092/412] KVM snapshots backUp path in 22 had entire path, instead of relative path. Fix modifies the path to make it relative during upgrade --- .../cloud/upgrade/dao/Upgrade410to420.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index f28d49687ea..e7f47331eab 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -76,6 +76,7 @@ public class Upgrade410to420 implements DbUpgrade { addHostDetailsIndex(conn); updateNetworksForPrivateGateways(conn); removeFirewallServiceFromSharedNetworkOfferingWithSGService(conn); + fix22xKVMSnapshots(conn); } private void updateSystemVmTemplates(Connection conn) { @@ -779,4 +780,43 @@ public class Upgrade410to420 implements DbUpgrade { } } + private void fix22xKVMSnapshots(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + s_logger.debug("Updating KVM snapshots"); + try { + pstmt = conn.prepareStatement("select id, backup_snap_id from `cloud`.`snapshots` where hypervisor_type='KVM' and removed is null and backup_snap_id is not null"); + rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + String backUpPath = rs.getString(2); + // Update Backup Path. Remove anything before /snapshots/ + // e.g 22x Path /mnt/0f14da63-7033-3ca5-bdbe-fa62f4e2f38a/snapshots/1/2/6/i-2-6-VM_ROOT-6_20121219072022 + // Above path should change to /snapshots/1/2/6/i-2-6-VM_ROOT-6_20121219072022 + int index = backUpPath.indexOf("snapshots"+File.separator); + if (index > 1){ + String correctedPath = File.separator + backUpPath.substring(index); + s_logger.debug("Updating Snapshot with id: "+id+" original backup path: "+backUpPath+ " updated backup path: "+correctedPath); + pstmt = conn.prepareStatement("UPDATE `cloud`.`snapshots` set backup_snap_id=? where id = ?"); + pstmt.setString(1, correctedPath); + pstmt.setLong(2, id); + pstmt.executeUpdate(); + } + } + s_logger.debug("Done updating KVM snapshots"); + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to update backup id for KVM snapshots", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } } From 6e1103e9240388332a2d39cf39a4afd30f02cc4e Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 24 May 2013 14:43:59 +0530 Subject: [PATCH 093/412] Removing tests that were written in the old design The APIs addVolumeDetail and addNicDetail do not exist. These were part of the old resource tagging design. Signed-off-by: Prasanna Santhanam --- test/integration/smoke/test_nicdetail.py | 224 ------------------ test/integration/smoke/test_volumedetail.py | 239 -------------------- 2 files changed, 463 deletions(-) delete mode 100644 test/integration/smoke/test_nicdetail.py delete mode 100644 test/integration/smoke/test_volumedetail.py diff --git a/test/integration/smoke/test_nicdetail.py b/test/integration/smoke/test_nicdetail.py deleted file mode 100644 index 3d8b1d62a47..00000000000 --- a/test/integration/smoke/test_nicdetail.py +++ /dev/null @@ -1,224 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with 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. -""" P1 tests for Scaling up Vm -""" -#Import Local Modules -import marvin -from marvin.cloudstackTestCase import * -from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient -from marvin.integration.lib.utils import * -from marvin.integration.lib.base import * -from marvin.integration.lib.common import * -from nose.plugins.attrib import attr -#Import System modules -import time - -_multiprocess_shared_ = True -class Services: - """Test VM Life Cycle Services - """ - - def __init__(self): - self.services = { - - "account": { - "email": "test@test.com", - "firstname": "Test", - "lastname": "User", - "username": "test", - # Random characters are appended in create account to - # ensure unique username generated each time - "password": "password", - }, - "small": - # Create a small virtual machine instance with disk offering - { - "displayname": "testserver", - "username": "root", # VM creds for SSH - "password": "password", - "ssh_port": 22, - "hypervisor": 'XenServer', - "privateport": 22, - "publicport": 22, - "protocol": 'TCP', - }, - "disk_offering": { - "displaytext": "Small", - "name": "Small", - "storagetype": "shared", - "disksize": 1 - }, - "service_offerings": - { - "small": - { - # Small service offering ID to for change VM - # service offering from medium to small - "name": "SmallInstance", - "displaytext": "SmallInstance", - "cpunumber": 1, - "cpuspeed": 100, - "memory": 256, - }, - "big": - { - # Big service offering ID to for change VM - "name": "BigInstance", - "displaytext": "BigInstance", - "cpunumber": 1, - "cpuspeed": 100, - "memory": 512, - } - }, - #Change this - "template": { - "displaytext": "xs", - "name": "xs", - "passwordenabled": False, - }, - "diskdevice": '/dev/xvdd', - # Disk device where ISO is attached to instance - "mount_dir": "/mnt/tmp", - "sleep": 60, - "timeout": 10, - #Migrate VM to hostid - "ostype": 'CentOS 5.6 (64-bit)', - # CentOS 5.3 (64-bit) - } - -class TestNicDetail(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - cls.api_client = super(TestNicDetail, cls).getClsTestClient().getApiClient() - cls.services = Services().services - - # Get Zone, Domain and templates - domain = get_domain(cls.api_client, cls.services) - zone = get_zone(cls.api_client, cls.services) - cls.services['mode'] = zone.networktype - - # Set Zone - - # Create account, service offerings, vm. - cls.account = Account.create( - cls.api_client, - cls.services["account"], - domainid=domain.id - ) - - cls.nic = "163738c7-ce3a-481d-ac68-4a8337043415"; - #how does it work - cls._cleanup = [ - cls.account - ] - - @classmethod - def tearDownClass(cls): - cls.api_client = super(TestNicDetail, cls).getClsTestClient().getApiClient() - cleanup_resources(cls.api_client, cls._cleanup) - return - - def setUp(self): - self.apiclient = self.testClient.getApiClient() - self.dbclient = self.testClient.getDbConnection() - self.cleanup = [] - - def tearDown(self): - #Clean up, terminate the created ISOs - cleanup_resources(self.apiclient, self.cleanup) - return - - @attr(tags = ["advanced", "xenserver"]) - def test_01_updatenicdetail(self): - """Test nic detail - """ - # Validate the following - # Scale up the vm and see if it scales to the new svc offering and is finally in running state - - self.debug("Testing ADD nic detail Nic-ID: %s " % ( - self.nic - )) - - cmd = addNicDetail.addNicDetailCmd() - cmd.name = self.nic - cmd.value = self.nic - cmd.id = self.nic - self.apiclient.addNicDetail(cmd) - - listNicDetailCmd = listNicDetails.listNicDetailsCmd() - listNicDetailCmd.id = self.nic - listNicDetailResponse = self.api_client.listVirtualMachines(listNicDetailCmd) - - self.assertNotEqual(len(listNicDetailResponse), 0, "Check if the list API \ - returns a non-empty response") - - nicdetail = listNicDetailResponse[0] - - #self.assertEqual(nicdetail.id, self.nic, "Check if the Nic returned is the same as the one we asked for") - - - self.assertEqual(nicdetail.name, self.nic, "Check if Nic has right name") - - self.assertEqual(nicdetail.value, self.nic, "Check if Nic has right value") - - #updatenicdetail - self.debug("Testing UPDATE nic detail Nic-ID: %s " % ( - self.nic - )) - cmd = updateNicDetail.updateNicDetailCmd() - cmd.name = self.nic - cmd.value = self.disk_offering.id - cmd.id = self.nic - self.apiclient.addNicDetail(cmd) - - listNicDetailCmd = listNicDetails.listNicDetailsCmd() - listNicDetailCmd.id = self.nic - listNicDetailResponse = self.api_client.listVirtualMachines(listNicDetailCmd) - - self.assertNotEqual(len(listNicDetailResponse), 0, "Check if the list API \ - returns a non-empty response") - - nicdetail = listNicDetailResponse[0] - - #self.assertEqual(nicdetail.id, self.nic, "Check if the Nic returned is the same as the one we asked for") - - - self.assertEqual(nicdetail.name, self.nic, "Check if Nic has right name") - - self.assertEqual(nicdetail.value, self.disk_offering.id, "Check if Nic has right value") - - - #remove detail - self.debug("Testing REMOVE nic detail Nic-ID: %s " % ( - self.nic - )) - cmd = removeNicDetail.removeNicDetailCmd() - cmd.name = self.nic - cmd.id = self.nic - self.apiclient.removeNicDetail(cmd) - - listNicDetailCmd = listNicDetails.listNicDetailsCmd() - listNicDetailCmd.id = self.nic - listNicDetailResponse = self.api_client.listVirtualMachines(listNicDetailCmd) - - self.assertEqual(listNicDetailResponse, None, "Check if the list API \ - returns a non-empty response") - - - return diff --git a/test/integration/smoke/test_volumedetail.py b/test/integration/smoke/test_volumedetail.py deleted file mode 100644 index f734dbb4de6..00000000000 --- a/test/integration/smoke/test_volumedetail.py +++ /dev/null @@ -1,239 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with 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. -""" P1 tests for Scaling up Vm -""" -#Import Local Modules -import marvin -from marvin.cloudstackTestCase import * -from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient -from marvin.integration.lib.utils import * -from marvin.integration.lib.base import * -from marvin.integration.lib.common import * -from nose.plugins.attrib import attr -#Import System modules -import time - -_multiprocess_shared_ = True -class Services: - """Test VM Life Cycle Services - """ - - def __init__(self): - self.services = { - - "account": { - "email": "test@test.com", - "firstname": "Test", - "lastname": "User", - "username": "test", - # Random characters are appended in create account to - # ensure unique username generated each time - "password": "password", - }, - "small": - # Create a small virtual machine instance with disk offering - { - "displayname": "testserver", - "username": "root", # VM creds for SSH - "password": "password", - "ssh_port": 22, - "hypervisor": 'XenServer', - "privateport": 22, - "publicport": 22, - "protocol": 'TCP', - }, - "disk_offering": { - "displaytext": "Small", - "name": "Small", - "storagetype": "shared", - "disksize": 1 - }, - "service_offerings": - { - "small": - { - # Small service offering ID to for change VM - # service offering from medium to small - "name": "SmallInstance", - "displaytext": "SmallInstance", - "cpunumber": 1, - "cpuspeed": 100, - "memory": 256, - }, - "big": - { - # Big service offering ID to for change VM - "name": "BigInstance", - "displaytext": "BigInstance", - "cpunumber": 1, - "cpuspeed": 100, - "memory": 512, - } - }, - #Change this - "template": { - "displaytext": "xs", - "name": "xs", - "passwordenabled": False, - }, - "diskdevice": '/dev/xvdd', - # Disk device where ISO is attached to instance - "mount_dir": "/mnt/tmp", - "sleep": 60, - "timeout": 10, - #Migrate VM to hostid - "ostype": 'CentOS 5.6 (64-bit)', - # CentOS 5.3 (64-bit) - } - -class TestVolumeDetail(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - cls.api_client = super(TestVolumeDetail, cls).getClsTestClient().getApiClient() - cls.services = Services().services - - # Get Zone, Domain and templates - domain = get_domain(cls.api_client, cls.services) - zone = get_zone(cls.api_client, cls.services) - cls.services['mode'] = zone.networktype - - # Set Zones and disk offerings ?? - - # Create account, service offerings, vm. - cls.account = Account.create( - cls.api_client, - cls.services["account"], - domainid=domain.id - ) - - - cls.disk_offering = DiskOffering.create( - cls.api_client, - cls.services["disk_offering"] - ) - - #create a volume - cls.volume = Volume.create( - cls.api_client, - { "diskname" : "ndm"}, - zoneid=zone.id, - account=cls.account.name, - domainid=cls.account.domainid, - diskofferingid=cls.disk_offering.id - ) - #how does it work ?? - cls._cleanup = [ - cls.volume, - cls.account - ] - - @classmethod - def tearDownClass(cls): - cls.api_client = super(TestVolumeDetail, cls).getClsTestClient().getApiClient() - cleanup_resources(cls.api_client, cls._cleanup) - return - - def setUp(self): - self.apiclient = self.testClient.getApiClient() - self.dbclient = self.testClient.getDbConnection() - self.cleanup = [] - - def tearDown(self): - #Clean up, terminate the created ISOs - cleanup_resources(self.apiclient, self.cleanup) - return - - @attr(tags = ["advanced", "xenserver"]) - def test_01_updatevolumedetail(self): - """Test volume detail - """ - # Validate the following - # Scale up the vm and see if it scales to the new svc offering and is finally in running state - - self.debug("Testing ADD volume detail Volume-ID: %s " % ( - self.volume.id - )) - - cmd = addVolumeDetail.addVolumeDetailCmd() - cmd.name = self.volume.id - cmd.value = self.volume.id - cmd.id = self.volume.id - self.apiclient.addVolumeDetail(cmd) - - listVolumeDetailCmd = listVolumeDetails.listVolumeDetailsCmd() - listVolumeDetailCmd.id = self.volume.id - listVolumeDetailResponse = self.api_client.listVirtualMachines(listVolumeDetailCmd) - - self.assertNotEqual(len(listVolumeDetailResponse), 0, "Check if the list API \ - returns a non-empty response") - - volumedetail = listVolumeDetailResponse[0] - - #self.assertEqual(volumedetail.id, self.volume.id, "Check if the Volume returned is the same as the one we asked for") - - - self.assertEqual(volumedetail.name, self.volume.id, "Check if Volume has right name") - - self.assertEqual(volumedetail.value, self.volume.id, "Check if Volume has right value") - - #updatevolumedetail - self.debug("Testing UPDATE volume detail Volume-ID: %s " % ( - self.volume.id - )) - cmd = updateVolumeDetail.updateVolumeDetailCmd() - cmd.name = self.volume.id - cmd.value = self.disk_offering.id - cmd.id = self.volume.id - self.apiclient.addVolumeDetail(cmd) - - listVolumeDetailCmd = listVolumeDetails.listVolumeDetailsCmd() - listVolumeDetailCmd.id = self.volume.id - listVolumeDetailResponse = self.api_client.listVirtualMachines(listVolumeDetailCmd) - - self.assertNotEqual(len(listVolumeDetailResponse), 0, "Check if the list API \ - returns a non-empty response") - - volumedetail = listVolumeDetailResponse[0] - - #self.assertEqual(volumedetail.id, self.volume.id, "Check if the Volume returned is the same as the one we asked for") - - - self.assertEqual(volumedetail.name, self.volume.id, "Check if Volume has right name") - - self.assertEqual(volumedetail.value, self.disk_offering.id, "Check if Volume has right value") - - - #remove detail - self.debug("Testing REMOVE volume detail Volume-ID: %s " % ( - self.volume.id - )) - cmd = removeVolumeDetail.removeVolumeDetailCmd() - cmd.name = self.volume.id - cmd.id = self.volume.id - self.apiclient.removeVolumeDetail(cmd) - - listVolumeDetailCmd = listVolumeDetails.listVolumeDetailsCmd() - listVolumeDetailCmd.id = self.volume.id - listVolumeDetailResponse = self.api_client.listVirtualMachines(listVolumeDetailCmd) - - self.assertEqual(listVolumeDetailResponse, None, "Check if the list API \ - returns a non-empty response") - - - return From ba67e5d56ed9e960e947925f82d2dda2204389ce Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Fri, 24 May 2013 16:00:39 +0530 Subject: [PATCH 094/412] CLOUDSTACK-2452: Fixed account upgrade for usage. Added cloumn default. Updated usage response for PF , LB and VPN to return UUID instead of Id --- .../src/com/cloud/api/ApiResponseHelper.java | 297 +++++++++--------- setup/db/db/schema-410to420.sql | 2 + 2 files changed, 153 insertions(+), 146 deletions(-) diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 540c8e9a4d7..89739cfde22 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -34,6 +34,30 @@ import java.util.TimeZone; import javax.inject.Inject; +import com.cloud.network.GuestVlan; +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkTrafficType; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.Site2SiteCustomerGateway; +import com.cloud.network.Site2SiteVpnConnection; +import com.cloud.network.Site2SiteVpnGateway; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.network.VpnUser; +import com.cloud.network.VpnUserVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.HealthCheckPolicy; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.rules.StaticNatRule; +import com.cloud.network.rules.StickinessPolicy; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -193,25 +217,11 @@ import com.cloud.event.Event; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.hypervisor.HypervisorCapabilities; -import com.cloud.network.GuestVlan; -import com.cloud.network.IpAddress; -import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; -import com.cloud.network.NetworkModel; -import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.TrafficType; -import com.cloud.network.PhysicalNetwork; -import com.cloud.network.PhysicalNetworkServiceProvider; -import com.cloud.network.PhysicalNetworkTrafficType; -import com.cloud.network.RemoteAccessVpn; -import com.cloud.network.Site2SiteCustomerGateway; -import com.cloud.network.Site2SiteVpnConnection; -import com.cloud.network.Site2SiteVpnGateway; -import com.cloud.network.VirtualRouterProvider; -import com.cloud.network.VpnUser; import com.cloud.network.as.AutoScalePolicy; import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.network.as.AutoScaleVmProfile; @@ -223,14 +233,7 @@ import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.router.VirtualRouter; -import com.cloud.network.rules.FirewallRule; -import com.cloud.network.rules.FirewallRuleVO; -import com.cloud.network.rules.HealthCheckPolicy; -import com.cloud.network.rules.LoadBalancer; import com.cloud.network.rules.LoadBalancerContainer.Scheme; -import com.cloud.network.rules.PortForwardingRule; -import com.cloud.network.rules.StaticNatRule; -import com.cloud.network.rules.StickinessPolicy; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupVO; import com.cloud.network.security.SecurityRule; @@ -3452,147 +3455,149 @@ public class ApiResponseHelper implements ResponseGenerator { } - @Override - public UsageRecordResponse createUsageResponse(Usage usageRecord) { - UsageRecordResponse usageRecResponse = new UsageRecordResponse(); + @Override + public UsageRecordResponse createUsageResponse(Usage usageRecord) { + UsageRecordResponse usageRecResponse = new UsageRecordResponse(); - Account account = ApiDBUtils.findAccountByIdIncludingRemoved(usageRecord.getAccountId()); - if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { - //find the project - Project project = ApiDBUtils.findProjectByProjectAccountIdIncludingRemoved(account.getId()); - usageRecResponse.setProjectId(project.getUuid()); - usageRecResponse.setProjectName(project.getName()); - } else { - usageRecResponse.setAccountId(account.getUuid()); - usageRecResponse.setAccountName(account.getAccountName()); - } + Account account = ApiDBUtils.findAccountByIdIncludingRemoved(usageRecord.getAccountId()); + if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { + //find the project + Project project = ApiDBUtils.findProjectByProjectAccountIdIncludingRemoved(account.getId()); + usageRecResponse.setProjectId(project.getUuid()); + usageRecResponse.setProjectName(project.getName()); + } else { + usageRecResponse.setAccountId(account.getUuid()); + usageRecResponse.setAccountName(account.getAccountName()); + } - Domain domain = ApiDBUtils.findDomainById(usageRecord.getDomainId()); - if (domain != null) { - usageRecResponse.setDomainId(domain.getUuid()); - } + Domain domain = ApiDBUtils.findDomainById(usageRecord.getDomainId()); + if (domain != null) { + usageRecResponse.setDomainId(domain.getUuid()); + } - if (usageRecord.getZoneId() != null) { - DataCenter zone = ApiDBUtils.findZoneById(usageRecord.getZoneId()); - if (zone != null) { - usageRecResponse.setZoneId(zone.getUuid()); - } - } - usageRecResponse.setDescription(usageRecord.getDescription()); - usageRecResponse.setUsage(usageRecord.getUsageDisplay()); - usageRecResponse.setUsageType(usageRecord.getUsageType()); - if (usageRecord.getVmInstanceId() != null) { - VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getVmInstanceId()); - usageRecResponse.setVirtualMachineId(vm.getUuid()); - } - usageRecResponse.setVmName(usageRecord.getVmName()); - if (usageRecord.getTemplateId() != null) { - VMTemplateVO template = ApiDBUtils.findTemplateById(usageRecord.getTemplateId()); - if (template != null) { - usageRecResponse.setTemplateId(template.getUuid()); - } - } + if (usageRecord.getZoneId() != null) { + DataCenter zone = ApiDBUtils.findZoneById(usageRecord.getZoneId()); + if (zone != null) { + usageRecResponse.setZoneId(zone.getUuid()); + } + } + usageRecResponse.setDescription(usageRecord.getDescription()); + usageRecResponse.setUsage(usageRecord.getUsageDisplay()); + usageRecResponse.setUsageType(usageRecord.getUsageType()); + if (usageRecord.getVmInstanceId() != null) { + VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getVmInstanceId()); + usageRecResponse.setVirtualMachineId(vm.getUuid()); + } + usageRecResponse.setVmName(usageRecord.getVmName()); + if (usageRecord.getTemplateId() != null) { + VMTemplateVO template = ApiDBUtils.findTemplateById(usageRecord.getTemplateId()); + if (template != null) { + usageRecResponse.setTemplateId(template.getUuid()); + } + } - if(usageRecord.getUsageType() == UsageTypes.RUNNING_VM || usageRecord.getUsageType() == UsageTypes.ALLOCATED_VM){ - ServiceOfferingVO svcOffering = _entityMgr.findByIdIncludingRemoved(ServiceOfferingVO.class, usageRecord.getOfferingId().toString()); - //Service Offering Id - usageRecResponse.setOfferingId(svcOffering.getUuid()); - //VM Instance ID - VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(vm.getUuid()); - //Hypervisor Type - usageRecResponse.setType(usageRecord.getType()); + if(usageRecord.getUsageType() == UsageTypes.RUNNING_VM || usageRecord.getUsageType() == UsageTypes.ALLOCATED_VM){ + ServiceOfferingVO svcOffering = _entityMgr.findByIdIncludingRemoved(ServiceOfferingVO.class, usageRecord.getOfferingId().toString()); + //Service Offering Id + usageRecResponse.setOfferingId(svcOffering.getUuid()); + //VM Instance ID + VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(vm.getUuid()); + //Hypervisor Type + usageRecResponse.setType(usageRecord.getType()); - } else if(usageRecord.getUsageType() == UsageTypes.IP_ADDRESS){ - //isSourceNAT - usageRecResponse.setSourceNat((usageRecord.getType().equals("SourceNat"))?true:false); - //isSystem - usageRecResponse.setSystem((usageRecord.getSize() == 1)?true:false); - //IP Address ID - IPAddressVO ip = _entityMgr.findByIdIncludingRemoved(IPAddressVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(ip.getUuid()); + } else if(usageRecord.getUsageType() == UsageTypes.IP_ADDRESS){ + //isSourceNAT + usageRecResponse.setSourceNat((usageRecord.getType().equals("SourceNat"))?true:false); + //isSystem + usageRecResponse.setSystem((usageRecord.getSize() == 1)?true:false); + //IP Address ID + IPAddressVO ip = _entityMgr.findByIdIncludingRemoved(IPAddressVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(ip.getUuid()); - } else if(usageRecord.getUsageType() == UsageTypes.NETWORK_BYTES_SENT || usageRecord.getUsageType() == UsageTypes.NETWORK_BYTES_RECEIVED){ - //Device Type - usageRecResponse.setType(usageRecord.getType()); - if(usageRecord.getType().equals("DomainRouter")){ - //Domain Router Id - VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(vm.getUuid()); - } else { - //External Device Host Id - HostVO host = _entityMgr.findByIdIncludingRemoved(HostVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(host.getUuid()); - } - //Network ID - NetworkVO network = _entityMgr.findByIdIncludingRemoved(NetworkVO.class, usageRecord.getNetworkId().toString()); - usageRecResponse.setNetworkId(network.getUuid()); + } else if(usageRecord.getUsageType() == UsageTypes.NETWORK_BYTES_SENT || usageRecord.getUsageType() == UsageTypes.NETWORK_BYTES_RECEIVED){ + //Device Type + usageRecResponse.setType(usageRecord.getType()); + if(usageRecord.getType().equals("DomainRouter")){ + //Domain Router Id + VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(vm.getUuid()); + } else { + //External Device Host Id + HostVO host = _entityMgr.findByIdIncludingRemoved(HostVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(host.getUuid()); + } + //Network ID + NetworkVO network = _entityMgr.findByIdIncludingRemoved(NetworkVO.class, usageRecord.getNetworkId().toString()); + usageRecResponse.setNetworkId(network.getUuid()); - } else if(usageRecord.getUsageType() == UsageTypes.VOLUME){ - //Volume ID - VolumeVO volume = _entityMgr.findByIdIncludingRemoved(VolumeVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(volume.getUuid()); - //Volume Size - usageRecResponse.setSize(usageRecord.getSize()); - //Disk Offering Id - if(usageRecord.getOfferingId() != null){ - DiskOfferingVO diskOff = _entityMgr.findByIdIncludingRemoved(DiskOfferingVO.class, usageRecord.getOfferingId().toString()); - usageRecResponse.setOfferingId(diskOff.getUuid()); - } + } else if(usageRecord.getUsageType() == UsageTypes.VOLUME){ + //Volume ID + VolumeVO volume = _entityMgr.findByIdIncludingRemoved(VolumeVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(volume.getUuid()); + //Volume Size + usageRecResponse.setSize(usageRecord.getSize()); + //Disk Offering Id + if(usageRecord.getOfferingId() != null){ + DiskOfferingVO diskOff = _entityMgr.findByIdIncludingRemoved(DiskOfferingVO.class, usageRecord.getOfferingId().toString()); + usageRecResponse.setOfferingId(diskOff.getUuid()); + } - } else if(usageRecord.getUsageType() == UsageTypes.TEMPLATE || usageRecord.getUsageType() == UsageTypes.ISO){ - //Template/ISO ID - VMTemplateVO tmpl = _entityMgr.findByIdIncludingRemoved(VMTemplateVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(tmpl.getUuid()); - //Template/ISO Size - usageRecResponse.setSize(usageRecord.getSize()); + } else if(usageRecord.getUsageType() == UsageTypes.TEMPLATE || usageRecord.getUsageType() == UsageTypes.ISO){ + //Template/ISO ID + VMTemplateVO tmpl = _entityMgr.findByIdIncludingRemoved(VMTemplateVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(tmpl.getUuid()); + //Template/ISO Size + usageRecResponse.setSize(usageRecord.getSize()); - } else if(usageRecord.getUsageType() == UsageTypes.SNAPSHOT){ - //Snapshot ID - SnapshotVO snap = _entityMgr.findByIdIncludingRemoved(SnapshotVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(snap.getUuid()); - //Snapshot Size - usageRecResponse.setSize(usageRecord.getSize()); + } else if(usageRecord.getUsageType() == UsageTypes.SNAPSHOT){ + //Snapshot ID + SnapshotVO snap = _entityMgr.findByIdIncludingRemoved(SnapshotVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(snap.getUuid()); + //Snapshot Size + usageRecResponse.setSize(usageRecord.getSize()); - } else if(usageRecord.getUsageType() == UsageTypes.LOAD_BALANCER_POLICY){ - //Load Balancer Policy ID - usageRecResponse.setUsageId(usageRecord.getUsageId().toString()); + } else if(usageRecord.getUsageType() == UsageTypes.LOAD_BALANCER_POLICY){ + //Load Balancer Policy ID + LoadBalancerVO lb = _entityMgr.findByIdIncludingRemoved(LoadBalancerVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(lb.getUuid()); + } else if(usageRecord.getUsageType() == UsageTypes.PORT_FORWARDING_RULE){ + //Port Forwarding Rule ID + PortForwardingRuleVO pf = _entityMgr.findByIdIncludingRemoved(PortForwardingRuleVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(pf.getUuid()); - } else if(usageRecord.getUsageType() == UsageTypes.PORT_FORWARDING_RULE){ - //Port Forwarding Rule ID - usageRecResponse.setUsageId(usageRecord.getUsageId().toString()); + } else if(usageRecord.getUsageType() == UsageTypes.NETWORK_OFFERING){ + //Network Offering Id + NetworkOfferingVO netOff = _entityMgr.findByIdIncludingRemoved(NetworkOfferingVO.class, usageRecord.getOfferingId().toString()); + usageRecResponse.setOfferingId(netOff.getUuid()); + //is Default + usageRecResponse.setDefault((usageRecord.getUsageId() == 1)? true:false); - } else if(usageRecord.getUsageType() == UsageTypes.NETWORK_OFFERING){ - //Network Offering Id - NetworkOfferingVO netOff = _entityMgr.findByIdIncludingRemoved(NetworkOfferingVO.class, usageRecord.getOfferingId().toString()); - usageRecResponse.setOfferingId(netOff.getUuid()); - //is Default - usageRecResponse.setDefault((usageRecord.getUsageId() == 1)? true:false); + } else if(usageRecord.getUsageType() == UsageTypes.VPN_USERS){ + //VPN User ID + VpnUserVO vpnUser = _entityMgr.findByIdIncludingRemoved(VpnUserVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(vpnUser.getUuid()); - } else if(usageRecord.getUsageType() == UsageTypes.VPN_USERS){ - //VPN User ID - usageRecResponse.setUsageId(usageRecord.getUsageId().toString()); + } else if(usageRecord.getUsageType() == UsageTypes.SECURITY_GROUP){ + //Security Group Id + SecurityGroupVO sg = _entityMgr.findByIdIncludingRemoved(SecurityGroupVO.class, usageRecord.getUsageId().toString()); + usageRecResponse.setUsageId(sg.getUuid()); + } - } else if(usageRecord.getUsageType() == UsageTypes.SECURITY_GROUP){ - //Security Group Id - SecurityGroupVO sg = _entityMgr.findByIdIncludingRemoved(SecurityGroupVO.class, usageRecord.getUsageId().toString()); - usageRecResponse.setUsageId(sg.getUuid()); - } + if (usageRecord.getRawUsage() != null) { + DecimalFormat decimalFormat = new DecimalFormat("###########.######"); + usageRecResponse.setRawUsage(decimalFormat.format(usageRecord.getRawUsage())); + } - if (usageRecord.getRawUsage() != null) { - DecimalFormat decimalFormat = new DecimalFormat("###########.######"); - usageRecResponse.setRawUsage(decimalFormat.format(usageRecord.getRawUsage())); - } + if (usageRecord.getStartDate() != null) { + usageRecResponse.setStartDate(getDateStringInternal(usageRecord.getStartDate())); + } + if (usageRecord.getEndDate() != null) { + usageRecResponse.setEndDate(getDateStringInternal(usageRecord.getEndDate())); + } - if (usageRecord.getStartDate() != null) { - usageRecResponse.setStartDate(getDateStringInternal(usageRecord.getStartDate())); - } - if (usageRecord.getEndDate() != null) { - usageRecResponse.setEndDate(getDateStringInternal(usageRecord.getEndDate())); - } - - return usageRecResponse; - } + return usageRecResponse; + } public String getDateStringInternal(Date inputDate) { diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 885eff46132..435bb83d3c9 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1040,8 +1040,10 @@ CREATE VIEW `cloud`.`service_offering_view` AS -- Add "default" field to account/user tables ALTER TABLE `cloud`.`account` ADD COLUMN `default` int(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 if account is default'; +ALTER TABLE `cloud_usage`.`account` ADD COLUMN `default` int(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 if account is default'; ALTER TABLE `cloud`.`user` ADD COLUMN `default` int(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 if user is default'; UPDATE `cloud`.`account` SET `cloud`.`account`.`default`=1 WHERE id IN (1,2); +UPDATE `cloud_usage`.`account` SET `default`=1 WHERE id IN (1,2); UPDATE `cloud`.`user` SET `cloud`.`user`.`default`=1 WHERE id IN (1,2); ALTER VIEW `cloud`.`user_view` AS From 23baacba62e6169aeeaeabb3441b393ee5491851 Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Fri, 24 May 2013 16:04:23 +0530 Subject: [PATCH 095/412] CLOUDSTACK-2669 - Adding resource limit and count logic for scale vm --- .../src/com/cloud/vm/UserVmManagerImpl.java | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 5e206567bed..c10a9d8d595 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -819,6 +819,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } + @Override public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { Long vmId = cmd.getVmId(); @@ -1112,21 +1113,47 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use //Check if its a scale "up" ServiceOffering newServiceOffering = _configMgr.getServiceOffering(newServiceOfferingId); - ServiceOffering oldServiceOffering = _configMgr.getServiceOffering(vmInstance.getServiceOfferingId()); - if(newServiceOffering.getSpeed() <= oldServiceOffering.getSpeed() - && newServiceOffering.getRamSize() <= oldServiceOffering.getRamSize()){ + ServiceOffering currentServiceOffering = _configMgr.getServiceOffering(vmInstance.getServiceOfferingId()); + int newCpu = newServiceOffering.getCpu(); + int newMemory = newServiceOffering.getRamSize(); + int newSpeed = newServiceOffering.getSpeed(); + int currentCpu = currentServiceOffering.getCpu(); + int currentMemory = currentServiceOffering.getRamSize(); + int currentSpeed = currentServiceOffering.getSpeed(); + + if(newSpeed <= currentSpeed + && newMemory <= currentMemory + && newCpu <= currentCpu){ throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering should have both cpu and memory greater than the old values"); } + // Check resource limits + if (newCpu > currentCpu) { + _resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu, + newCpu - currentCpu); + } + if (newMemory > currentMemory) { + _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, + newMemory - currentMemory); + } + // Dynamically upgrade the running vms boolean success = false; if(vmInstance.getState().equals(State.Running)){ int retry = _scaleRetry; + // Increment CPU and Memory count accordingly. + if (newCpu > currentCpu) { + _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long (newCpu - currentCpu)); + } + if (newMemory > currentMemory) { + _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long (newMemory - currentMemory)); + } + while (retry-- != 0) { // It's != so that it can match -1. try{ // #1 Check existing host has capacity - boolean existingHostHasCapacity = _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), newServiceOffering.getSpeed() - oldServiceOffering.getSpeed(), - (newServiceOffering.getRamSize() - oldServiceOffering.getRamSize()) * 1024L * 1024L, false, ApiDBUtils.getCpuOverprovisioningFactor(), 1f, false); // TO DO fill it with mem. + boolean existingHostHasCapacity = _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), newServiceOffering.getSpeed() - currentServiceOffering.getSpeed(), + (newServiceOffering.getRamSize() - currentServiceOffering.getRamSize()) * 1024L * 1024L, false, ApiDBUtils.getCpuOverprovisioningFactor(), 1f, false); // TO DO fill it with mem. // #2 migrate the vm if host doesn't have capacity if (!existingHostHasCapacity){ @@ -1136,7 +1163,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // #3 scale the vm now _itMgr.upgradeVmDb(vmId, newServiceOfferingId); vmInstance = _vmInstanceDao.findById(vmId); - vmInstance = _itMgr.reConfigureVm(vmInstance, oldServiceOffering, existingHostHasCapacity); + vmInstance = _itMgr.reConfigureVm(vmInstance, currentServiceOffering, existingHostHasCapacity); success = true; return success; }catch(InsufficientCapacityException e ){ @@ -1151,8 +1178,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use s_logger.warn("Received exception while scaling ",e); }finally{ if(!success){ - _itMgr.upgradeVmDb(vmId, oldServiceOffering.getId()); // rollback + _itMgr.upgradeVmDb(vmId, currentServiceOffering.getId()); // rollback + // Decrement CPU and Memory count accordingly. + if (newCpu > currentCpu) { + _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long (newCpu - currentCpu)); + } + if (newMemory > currentMemory) { + _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long (newMemory - currentMemory)); + } } + + } } } From a34e577d1be5a25d7eb884147212ab51786b55e2 Mon Sep 17 00:00:00 2001 From: Harikrishna Patnala Date: Fri, 24 May 2013 16:38:55 +0530 Subject: [PATCH 096/412] CLOUDSTACK-1453: support restore for VM created from ISO This is to support restore a vm to a new/currently_attached ISO. In the restorevm API we have an optional parameter templateId to restore the vm to the new template/ISO ID. --- .../api/command/user/vm/RestoreVMCmd.java | 5 ++- .../src/com/cloud/vm/UserVmManagerImpl.java | 43 ++++++++++++++----- .../test/com/cloud/vm/UserVmManagerTest.java | 42 ++++++++++++++++-- 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java index 9c33f97c317..2f7d8e10a77 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java @@ -35,7 +35,7 @@ import com.cloud.user.Account; import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; -@APICommand(name = "restoreVirtualMachine", description="Restore a VM to original template or new template", responseObject=UserVmResponse.class, since="3.0.0") +@APICommand(name = "restoreVirtualMachine", description="Restore a VM to original template/ISO or new template/ISO", responseObject=UserVmResponse.class, since="3.0.0") public class RestoreVMCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(RestoreVMCmd.class); private static final String s_name = "restorevmresponse"; @@ -44,9 +44,10 @@ public class RestoreVMCmd extends BaseAsyncCmd { required=true, description="Virtual Machine ID") private Long vmId; - @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.UUID, entityType = TemplateResponse.class, description="an optional template Id to restore vm from the new template") + @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.UUID, entityType = TemplateResponse.class, description="an optional template Id to restore vm from the new template. This can be an ISO id in case of restore vm deployed using ISO") private Long templateId; + @Override public String getEventType() { return EventTypes.EVENT_VM_RESTORE; diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index c10a9d8d595..96b95293b47 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -4237,6 +4237,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use long vmId = cmd.getVmId(); Long newTemplateId = cmd.getTemplateId(); + UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Cannot find VM with ID " + vmId); @@ -4292,21 +4293,35 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use VolumeVO root = rootVols.get(0); Long templateId = root.getTemplateId(); + boolean isISO = false; if(templateId == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Currently there is no support to reset a vm that is deployed using ISO " + vm.getUuid()); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; + // Assuming that for a vm deployed using ISO, template ID is set to NULL + isISO = true; + templateId = vm.getIsoId(); } VMTemplateVO template = null; + //newTemplateId can be either template or ISO id. In the following snippet based on the vm deployment (from template or ISO) it is handled accordingly if(newTemplateId != null) { template = _templateDao.findById(newTemplateId); _accountMgr.checkAccess(caller, null, true, template); + if (isISO) { + if (!template.getFormat().equals(ImageFormat.ISO)) { + throw new InvalidParameterValueException("Invalid ISO id provided to restore the VM "); + } + } else { + if (template.getFormat().equals(ImageFormat.ISO)) { + throw new InvalidParameterValueException("Invalid template id provided to restore the VM "); + } + } } else { + if (isISO && templateId == null) { + throw new CloudRuntimeException("Cannot restore the VM since there is no ISO attached to VM"); + } template = _templateDao.findById(templateId); if (template == null) { InvalidParameterValueException ex = new InvalidParameterValueException( - "Cannot find template for specified volumeid and vmId"); + "Cannot find template/ISO for specified volumeid and vmId"); ex.addProxyObject(vm, vmId, "vmId"); ex.addProxyObject(root, root.getId(), "volumeId"); throw ex; @@ -4325,13 +4340,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } - /* If new template is provided allocate a new volume from new template otherwise allocate new volume from original template */ + /* If new template/ISO is provided allocate a new volume from new template/ISO otherwise allocate new volume from original template/ISO */ VolumeVO newVol = null; - if (newTemplateId != null){ - newVol = volumeMgr.allocateDuplicateVolume(root, newTemplateId); - vm.setGuestOSId(template.getGuestOSId()); - vm.setTemplateId(newTemplateId); - _vmDao.update(vmId, vm); + if (newTemplateId != null) { + if (isISO) { + newVol = volumeMgr.allocateDuplicateVolume(root, null); + vm.setIsoId(newTemplateId); + vm.setGuestOSId(template.getGuestOSId()); + vm.setTemplateId(newTemplateId); + _vmDao.update(vmId, vm); + } else { + newVol = volumeMgr.allocateDuplicateVolume(root, newTemplateId); + vm.setGuestOSId(template.getGuestOSId()); + vm.setTemplateId(newTemplateId); + _vmDao.update(vmId, vm); + } } else { newVol = volumeMgr.allocateDuplicateVolume(root, null); } diff --git a/server/test/com/cloud/vm/UserVmManagerTest.java b/server/test/com/cloud/vm/UserVmManagerTest.java index 6a9711401c9..5eedfa5d815 100755 --- a/server/test/com/cloud/vm/UserVmManagerTest.java +++ b/server/test/com/cloud/vm/UserVmManagerTest.java @@ -24,10 +24,8 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; import java.lang.reflect.Field; import java.util.List; @@ -64,6 +62,7 @@ import com.cloud.storage.VolumeManager; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.Storage.ImageFormat; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountService; @@ -200,6 +199,7 @@ public class UserVmManagerTest { doReturn(false).when(_rootVols).isEmpty(); when(_rootVols.get(eq(0))).thenReturn(_volumeMock); doReturn(3L).when(_volumeMock).getTemplateId(); + doReturn(ImageFormat.VHD).when(_templateMock).getFormat(); when(_templateDao.findById(anyLong())).thenReturn(_templateMock); doNothing().when(_accountMgr).checkAccess(_account, null, true, _templateMock); when(_itMgr.stop(_vmMock, _userMock, _account)).thenReturn(true); @@ -220,6 +220,40 @@ public class UserVmManagerTest { } + // Test restoreVM on providing new ISO Id, when VM(deployed using ISO) is in running state + @Test + public void testRestoreVMF5() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException { + doReturn(VirtualMachine.State.Running).when(_vmMock).getState(); + when(_vmDao.findById(anyLong())).thenReturn(_vmMock); + when(_volsDao.findByInstanceAndType(314L, Volume.Type.ROOT)).thenReturn(_rootVols); + doReturn(false).when(_rootVols).isEmpty(); + when(_rootVols.get(eq(0))).thenReturn(_volumeMock); + doReturn(null).when(_volumeMock).getTemplateId(); + doReturn(3L).when(_vmMock).getIsoId(); + doReturn(ImageFormat.ISO).when(_templateMock).getFormat(); + when(_templateDao.findById(anyLong())).thenReturn(_templateMock); + doNothing().when(_accountMgr).checkAccess(_account, null, true, _templateMock); + when(_itMgr.stop(_vmMock, _userMock, _account)).thenReturn(true); + when(_storageMgr.allocateDuplicateVolume(_volumeMock, null)).thenReturn(_volumeMock); + doNothing().when(_vmMock).setIsoId(14L); + when(_templateMock.getGuestOSId()).thenReturn(5L); + doNothing().when(_vmMock).setGuestOSId(anyLong()); + doNothing().when(_vmMock).setTemplateId(3L); + when(_vmDao.update(314L, _vmMock)).thenReturn(true); + when(_itMgr.start(_vmMock, null, _userMock, _account)).thenReturn(_vmMock); + when(_storageMgr.allocateDuplicateVolume(_volumeMock, null)).thenReturn(_volumeMock); + doNothing().when(_volsDao).attachVolume(anyLong(), anyLong(), anyLong()); + when(_volumeMock.getId()).thenReturn(3L); + doNothing().when(_volsDao).detachVolume(anyLong()); + + when(_templateMock.getUuid()).thenReturn("b1a3626e-72e0-4697-8c7c-a110940cc55d"); + + _userVmMgr.restoreVMInternal(_account, _vmMock, 14L); + + verify(_vmMock, times(1)).setIsoId(14L); + + } // Test scaleVm on incompatible HV. @Test(expected=InvalidParameterValueException.class) public void testScaleVMF1() throws Exception { From 8b9d6d81a229441cc5a9e3c0bf61e81de9ea6c5d Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Fri, 24 May 2013 17:28:11 +0530 Subject: [PATCH 097/412] Implicit dedication mode --- ui/scripts/configuration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 9932cdc3aa6..211d7b786b7 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -171,7 +171,7 @@ var items=[]; items.push({id:'',description:''}); items.push({id:'Strict', description:'Strict'}); - items.push({id:'Preffered', description:'Preffered'}); + items.push({id:'Preferred', description:'Preferred'}); args.response.success({data:items}); } }, From b000781bfc25b8df1b7b1c8c76bf57484a4c4f03 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Fri, 24 May 2013 18:51:24 +0530 Subject: [PATCH 098/412] fixing disassociateIpAddress API to call 'releasePortableIpAddress' network service method for portable ip address. Also fix ensures events coresponding to the portabl ip are generated correctly. --- api/src/com/cloud/event/EventTypes.java | 2 +- .../api/command/user/address/AssociateIPAddrCmd.java | 6 +++++- .../api/command/user/address/DisassociateIPAddrCmd.java | 8 ++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index fcac8e8e65d..da96294c14c 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -103,7 +103,7 @@ public class EventTypes { public static final String EVENT_NET_IP_ASSIGN = "NET.IPASSIGN"; public static final String EVENT_NET_IP_RELEASE = "NET.IPRELEASE"; public static final String EVENT_PORTABLE_IP_ASSIGN = "PORTABLE.IPASSIGN"; - public static final String EVENT_PORTABLE_IP_RELEASE = "PORTABLEIPRELEASE"; + public static final String EVENT_PORTABLE_IP_RELEASE = "PORTABLE.IPRELEASE"; public static final String EVENT_NET_RULE_ADD = "NET.RULEADD"; public static final String EVENT_NET_RULE_DELETE = "NET.RULEDELETE"; public static final String EVENT_NET_RULE_MODIFY = "NET.RULEMODIFY"; diff --git a/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java b/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java index f37e82060fb..b99ca6347e0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java @@ -191,7 +191,11 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { @Override public String getEventType() { - return EventTypes.EVENT_NET_IP_ASSIGN; + if (isPortable()) { + return EventTypes.EVENT_PORTABLE_IP_ASSIGN; + } else { + return EventTypes.EVENT_NET_IP_ASSIGN; + } } @Override diff --git a/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java b/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java index 41691ea86d0..3d431f4ac03 100644 --- a/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java @@ -78,7 +78,7 @@ public class DisassociateIPAddrCmd extends BaseAsyncCmd { if (!isPortable(id)) { result = _networkService.releaseIpAddress(getIpAddressId()); } else { - result = _networkService.releaseIpAddress(getIpAddressId()); + result = _networkService.releasePortableIpAddress(getIpAddressId()); } if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); @@ -90,7 +90,11 @@ public class DisassociateIPAddrCmd extends BaseAsyncCmd { @Override public String getEventType() { - return EventTypes.EVENT_NET_IP_RELEASE; + if (!isPortable(id)) { + return EventTypes.EVENT_NET_IP_RELEASE; + } else { + return EventTypes.EVENT_PORTABLE_IP_RELEASE; + } } @Override From d8f5f2ea10eca540c10e93469fd5307fe25f0959 Mon Sep 17 00:00:00 2001 From: Jayapal Date: Fri, 24 May 2013 17:43:58 +0530 Subject: [PATCH 099/412] CLOUDSTACK-2671 Updated vpc private gateway scipt args --- .../com/cloud/hypervisor/vmware/resource/VmwareResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index be4775452f3..630d1b42e50 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1527,7 +1527,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa snatArgs += "eth" + ethDeviceNum; Pair result_gateway = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, - "/opt/cloud/bin/vpc_privateGateway.sh " + args); + "/opt/cloud/bin/vpc_privateGateway.sh " + snatArgs); if (!result_gateway.first()) { throw new InternalErrorException("Unable to configure source NAT for public IP address."); From ed2ce2737133de6277bba0be9b7e10d3faaf887d Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 24 May 2013 19:21:40 +0530 Subject: [PATCH 100/412] CLOUDSTACK-2652: requests module differs on Python 2.6 On 2.6 response.json returns the JSOn in the response while on 2.7 response.json() is a method. Since Marvin installs on both platforms fixing the error appropriately Signed-off-by: Prasanna Santhanam --- tools/marvin/marvin/cloudstackConnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py index e3977dcf7d4..b092ef0c32f 100644 --- a/tools/marvin/marvin/cloudstackConnection.py +++ b/tools/marvin/marvin/cloudstackConnection.py @@ -219,7 +219,10 @@ class cloudConnection(object): cmdname, self.auth, payload=payload, method=method) self.logging.debug("Request: %s Response: %s" % (response.url, response.text)) - response = jsonHelper.getResultObj(response.json(), response_type) + try: + response = jsonHelper.getResultObj(response.json(), response_type) + except TypeError: + response = jsonHelper.getResultObj(response.json, response_type) if isAsync == "false": return response From bef3a2edb7f23035a7d397d2d68caf6cecc0f497 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Fri, 24 May 2013 12:09:07 +0200 Subject: [PATCH 101/412] CLOUDSTACK-1191: rbd: Use cloning for deploying templates instead of a copy RBD format 2 supports cloning (aka layering) where one base image can serve as a parent image for multiple child images. This enables fast deployment of a large amount of virtual machines, but it also saves spaces on the Ceph cluster and improves performance due to better caching. Qemu-img doesn't support RBD format 2 (yet), so to enable these functions the RADOS/RBD Java bindings are required. This patch also enables deployment of System VMs on RBD storage pools. Since we no longer require a patchdisk for passing the boot arguments we are able to deploy these VMs on RBD. --- .../AbstractStoragePoolAllocator.java | 6 - plugins/hypervisors/kvm/pom.xml | 9 + .../resource/LibvirtComputingResource.java | 7 + .../kvm/storage/LibvirtStorageAdaptor.java | 248 ++++++++++++++++-- pom.xml | 1 + 5 files changed, 246 insertions(+), 25 deletions(-) diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index 3a66b859b39..53267013412 100755 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -167,12 +167,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement return false; } - DiskOfferingVO diskOffering = _diskOfferingDao.findById(dskCh.getDiskOfferingId()); - if (diskOffering.getSystemUse() && pool.getPoolType() == StoragePoolType.RBD) { - s_logger.debug("Skipping RBD pool " + pool.getName() + " as a suitable pool. RBD is not supported for System VM's"); - return false; - } - Long clusterId = pool.getClusterId(); ClusterVO cluster = _clusterDao.findById(clusterId); diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml index 613c817668e..1babe7cbf56 100644 --- a/plugins/hypervisors/kvm/pom.xml +++ b/plugins/hypervisors/kvm/pom.xml @@ -24,6 +24,10 @@ libvirt-org http://libvirt.org/maven2 + + ceph-com + http://ceph.com/maven + @@ -36,6 +40,11 @@ libvirt ${cs.libvirt-java.version} + + com.ceph + rados + ${cs.rados-java.version} + install diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 1e20d75a6a0..c34d1eb1821 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1253,6 +1253,13 @@ ServerResource { } private CopyVolumeAnswer execute(CopyVolumeCommand cmd) { + /** + This method is only used for copying files from Primary Storage TO Secondary Storage + + It COULD also do it the other way around, but the code in the ManagementServerImpl shows + that it always sets copyToSecondary to true + + */ boolean copyToSecondary = cmd.toSecondaryStorage(); String volumePath = cmd.getVolumePath(); StorageFilerTO pool = cmd.getPool(); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index e7e4bbf2c30..fa2f6707923 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -17,6 +17,9 @@ package com.cloud.hypervisor.kvm.storage; import java.io.File; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -37,6 +40,12 @@ import org.libvirt.StoragePool; import org.libvirt.StoragePoolInfo; import org.libvirt.StorageVol; import org.libvirt.StoragePoolInfo.StoragePoolState; +import com.ceph.rados.Rados; +import com.ceph.rados.RadosException; +import com.ceph.rados.IoCTX; +import com.ceph.rbd.Rbd; +import com.ceph.rbd.RbdImage; +import com.ceph.rbd.RbdException; import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.hypervisor.kvm.resource.LibvirtConnection; @@ -63,6 +72,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { private String _mountPoint = "/mnt"; private String _manageSnapshotPath; + private String rbdTemplateSnapName = "cloudstack-base-snap"; + public LibvirtStorageAdaptor(StorageLayer storage) { _storageLayer = storage; _manageSnapshotPath = Script.findScript("scripts/storage/qcow2/", @@ -638,6 +649,15 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } } + /** + * This function copies a physical disk from Secondary Storage to Primary Storage + * or from Primary to Primary Storage + * + * The first time a template is deployed in Primary Storage it will be copied from + * Secondary to Primary. + * + * If it has been created on Primary Storage, it will be copied on the Primary Storage + */ @Override public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) { @@ -690,21 +710,118 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { if (srcPool.getType() != StoragePoolType.RBD) { srcFile = new QemuImgFile(template.getPath(), template.getFormat()); + qemu.convert(srcFile, destFile); } else { - template.setFormat(PhysicalDiskFormat.RAW); - srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), - srcPool.getSourcePort(), - srcPool.getAuthUserName(), - srcPool.getAuthSecret(), - template.getPath())); - srcFile.setFormat(template.getFormat()); + + /** + * We have to find out if the source file is in the same RBD pool and has + * RBD format 2 before we can do a layering/clone operation on the RBD image + * + * This will be the case when the template is already on Primary Storage and + * we want to copy it + */ + + /* Feature 1<<0 means layering in RBD format 2 */ + int rbdFeatures = (1<<0); + /* Order 0 means 4MB blocks (the default) */ + int rbdOrder = 0; + + try { + if ((srcPool.getSourceHost().equals(destPool.getSourceHost())) && (srcPool.getSourceDir().equals(destPool.getSourceDir()))) { + /* We are on the same Ceph cluster, but we require RBD format 2 on the source image */ + s_logger.debug("Trying to perform a RBD clone (layering) since we are operating in the same storage pool"); + + Rados r = new Rados(srcPool.getAuthUserName()); + r.confSet("mon_host", srcPool.getSourceHost() + ":" + srcPool.getSourcePort()); + r.confSet("key", srcPool.getAuthSecret()); + r.connect(); + s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host")); + + IoCTX io = r.ioCtxCreate(srcPool.getSourceDir()); + Rbd rbd = new Rbd(io); + RbdImage srcImage = rbd.open(template.getName()); + + if (srcImage.isOldFormat()) { + /* The source image is RBD format 1, we have to do a regular copy */ + s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() + + " is RBD format 1. We have to perform a regular copy (" + template.getVirtualSize() + " bytes)"); + + rbd.create(disk.getName(), template.getVirtualSize(), rbdFeatures, rbdOrder); + RbdImage destImage = rbd.open(disk.getName()); + + s_logger.debug("Starting to copy " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir()); + rbd.copy(srcImage, destImage); + + s_logger.debug("Finished copying " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir()); + rbd.close(destImage); + } else { + s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() + + " is RBD format 2. We will perform a RBD clone using snapshot " + + this.rbdTemplateSnapName); + /* The source image is format 2, we can do a RBD snapshot+clone (layering) */ + rbd.clone(template.getName(), this.rbdTemplateSnapName, io, disk.getName(), rbdFeatures, rbdOrder); + s_logger.debug("Succesfully cloned " + template.getName() + "@" + this.rbdTemplateSnapName + " to " + disk.getName()); + } + + rbd.close(srcImage); + r.ioCtxDestroy(io); + } else { + /* The source pool or host is not the same Ceph cluster, we do a simple copy with Qemu-Img */ + s_logger.debug("Both the source and destination are RBD, but not the same Ceph cluster. Performing a copy"); + + Rados rSrc = new Rados(srcPool.getAuthUserName()); + rSrc.confSet("mon_host", srcPool.getSourceHost() + ":" + srcPool.getSourcePort()); + rSrc.confSet("key", srcPool.getAuthSecret()); + rSrc.connect(); + s_logger.debug("Succesfully connected to source Ceph cluster at " + rSrc.confGet("mon_host")); + + Rados rDest = new Rados(destPool.getAuthUserName()); + rDest.confSet("mon_host", destPool.getSourceHost() + ":" + destPool.getSourcePort()); + rDest.confSet("key", destPool.getAuthSecret()); + rDest.connect(); + s_logger.debug("Succesfully connected to source Ceph cluster at " + rDest.confGet("mon_host")); + + IoCTX sIO = rSrc.ioCtxCreate(srcPool.getSourceDir()); + Rbd sRbd = new Rbd(sIO); + + IoCTX dIO = rDest.ioCtxCreate(destPool.getSourceDir()); + Rbd dRbd = new Rbd(dIO); + + s_logger.debug("Creating " + disk.getName() + " on the destination cluster " + rDest.confGet("mon_host") + + " in pool " + destPool.getSourceDir()); + dRbd.create(disk.getName(), template.getVirtualSize(), rbdFeatures, rbdOrder); + + RbdImage srcImage = sRbd.open(template.getName()); + RbdImage destImage = dRbd.open(disk.getName()); + + s_logger.debug("Copying " + template.getName() + " from Ceph cluster " + rSrc.confGet("mon_host") + " to " + disk.getName() + + " on cluster " + rDest.confGet("mon_host")); + sRbd.copy(srcImage, destImage); + + sRbd.close(srcImage); + dRbd.close(destImage); + + rSrc.ioCtxDestroy(sIO); + rDest.ioCtxDestroy(dIO); + } + } catch (RadosException e) { + s_logger.error("Failed to perform a RADOS action on the Ceph cluster, the error was: " + e.getMessage()); + disk = null; + } catch (RbdException e) { + s_logger.error("Failed to perform a RBD action on the Ceph cluster, the error was: " + e.getMessage()); + disk = null; + } } - qemu.convert(srcFile, destFile); } } catch (QemuImgException e) { s_logger.error("Failed to create " + disk.getPath() + " due to a failed executing of qemu-img: " + e.getMessage()); } + + if (disk == null) { + throw new CloudRuntimeException("Failed to create " + disk.getPath() + " from template " + template.getName()); + } + return disk; } @@ -733,17 +850,26 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } } + /** + * This copies a volume from Primary Storage to Secondary Storage + * + * In theory it could also do it the other way around, but the current implementation + * in ManagementServerImpl shows that the destPool is always a Secondary Storage Pool + */ @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool) { - /* + /** With RBD you can't run qemu-img convert with an existing RBD image as destination qemu-img will exit with the error that the destination already exists. So for RBD we don't create the image, but let qemu-img do that for us. We then create a KVMPhysicalDisk object that we can return - */ + + It is however very unlikely that the destPool will be RBD, since it isn't supported + for Secondary Storage + */ KVMPhysicalDisk newDisk; if (destPool.getType() != StoragePoolType.RBD) { @@ -791,15 +917,97 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { + srcFile.getFileName() + " the error was: " + e.getMessage()); } } + + try { + qemu.convert(srcFile, destFile); + } catch (QemuImgException e) { + s_logger.error("Failed to convert " + srcFile.getFileName() + " to " + + destFile.getFileName() + " the error was: " + e.getMessage()); + } + } else if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() == StoragePoolType.RBD)) { - srcFile = new QemuImgFile(sourcePath, sourceFormat); - destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), - destPool.getSourcePort(), - destPool.getAuthUserName(), - destPool.getAuthSecret(), - destPath)); - destFile.setFormat(destFormat); + /** + * Qemu doesn't support writing to RBD format 2 directly, so we have to write to a temporary RAW file first + * which we then convert to RBD format 2. + * + * A HUGE performance gain can be achieved here if QCOW2 -> RBD format 2 can be done in one step + */ + s_logger.debug("The source image is not RBD, but the destination is. We will convert into RBD format 2"); + String tmpFile = "/tmp/" + name; + int rbdFeatures = (1<<0); + int rbdOrder = 0; + + try { + srcFile = new QemuImgFile(sourcePath, sourceFormat); + destFile = new QemuImgFile(tmpFile); + s_logger.debug("Converting " + srcFile.getFileName() + " to " + tmpFile + " as a temporary file for RBD conversion"); + qemu.convert(srcFile, destFile); + + // We now convert the temporary file to a RBD image with format 2 + Rados r = new Rados(destPool.getAuthUserName()); + r.confSet("mon_host", destPool.getSourceHost() + ":" + destPool.getSourcePort()); + r.confSet("key", destPool.getAuthSecret()); + r.connect(); + s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host")); + + IoCTX io = r.ioCtxCreate(destPool.getSourceDir()); + Rbd rbd = new Rbd(io); + + s_logger.debug("Creating RBD image " + name + " in Ceph pool " + destPool.getSourceDir() + " with RBD format 2"); + rbd.create(name, disk.getVirtualSize(), rbdFeatures, rbdOrder); + + RbdImage image = rbd.open(name); + + // We now read the temporary file and write it to the RBD image + File fh = new File(tmpFile); + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fh)); + + int chunkSize = 4194304; + long offset = 0; + s_logger.debug("Reading temporary file " + tmpFile + " (" + fh.length() + " bytes) into RBD image " + name + " in chunks of " + chunkSize + " bytes"); + while(true) { + byte[] buf = new byte[chunkSize]; + + int bytes = bis.read(buf); + if (bytes <= 0) { + break; + } + image.write(buf, offset, bytes); + offset += bytes; + } + s_logger.debug("Completed writing " + tmpFile + " to RBD image " + name + ". Bytes written: " + offset); + bis.close(); + s_logger.debug("Removing temporary file " + tmpFile); + fh.delete(); + + /* Snapshot the image and protect that snapshot so we can clone (layer) from it */ + s_logger.debug("Creating RBD snapshot " + this.rbdTemplateSnapName + " on image " + name); + image.snapCreate(this.rbdTemplateSnapName); + s_logger.debug("Protecting RBD snapshot " + this.rbdTemplateSnapName + " on image " + name); + image.snapProtect(this.rbdTemplateSnapName); + + rbd.close(image); + r.ioCtxDestroy(io); + } catch (QemuImgException e) { + s_logger.error("Failed to do a temp convert from " + srcFile.getFileName() + " to " + + destFile.getFileName() + " the error was: " + e.getMessage()); + newDisk = null; + } catch (RadosException e) { + s_logger.error("A Ceph RADOS operation failed (" + e.getReturnValue() + "). The error was: " + e.getMessage()); + newDisk = null; + } catch (RbdException e) { + s_logger.error("A Ceph RBD operation failed (" + e.getReturnValue() + "). The error was: " + e.getMessage()); + newDisk = null; + } catch (IOException e) { + s_logger.error("Failed reading the temporary file during the conversion to RBD: " + e.getMessage()); + newDisk = null; + } + } else { + /** + We let Qemu-Img do the work here. Although we could work with librbd and have that do the cloning + it doesn't benefit us. It's better to keep the current code in place which works + */ srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), srcPool.getSourcePort(), srcPool.getAuthUserName(), @@ -812,17 +1020,19 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { destPool.getAuthSecret(), destPath)); destFile.setFormat(destFormat); - } - if (srcFile != null && destFile != null) { try { qemu.convert(srcFile, destFile); } catch (QemuImgException e) { s_logger.error("Failed to convert " + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " + e.getMessage()); + newDisk = null; } } + if (newDisk == null) { + throw new CloudRuntimeException("Failed to copy " + disk.getPath() + " to " + name); + } return newDisk; } diff --git a/pom.xml b/pom.xml index d7e80d64548..67d9576715c 100644 --- a/pom.xml +++ b/pom.xml @@ -83,6 +83,7 @@ 0.10 build/replace.properties 0.4.9 + 0.1.1 target 1.0.10 From 09d2fe6a7ddc208f77edf931ed3d57b0afcc2055 Mon Sep 17 00:00:00 2001 From: Chip Childers Date: Fri, 24 May 2013 17:06:02 +0100 Subject: [PATCH 102/412] CLOUDSTACK-528: Adding a corrective update to remove the extra space characters that exist after running older versions of schema-302to40.sql Signed-off-by: Chip Childers --- setup/db/db/schema-40to410.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index acc29a2eb7f..f0e8cf31846 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -1653,3 +1653,7 @@ CREATE TABLE `cloud`.`netscaler_pod_ref` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'eip.use.multiple.netscalers' , 'false', 'Should be set to true, if there will be multiple NetScaler devices providing EIP service in a zone'); + +UPDATE `cloud`.`configuration` set category='Advanced' where category='Advanced '; +UPDATE `cloud`.`configuration` set category='Hidden' where category='Hidden '; + From 90a40b0806bf586bca6653023d32bff270944251 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 24 May 2013 21:41:46 +0530 Subject: [PATCH 103/412] Move changeOffering test to service offerings This will reduce the time (>30m) on the vm lifecycle. Signed-off-by: Prasanna Santhanam --- .../smoke/test_service_offerings.py | 366 +++++++++++++----- test/integration/smoke/test_vm_life_cycle.py | 239 ------------ 2 files changed, 276 insertions(+), 329 deletions(-) diff --git a/test/integration/smoke/test_service_offerings.py b/test/integration/smoke/test_service_offerings.py index 7f4d130ee80..3d3b94684e8 100644 --- a/test/integration/smoke/test_service_offerings.py +++ b/test/integration/smoke/test_service_offerings.py @@ -27,24 +27,80 @@ from nose.plugins.attrib import attr _multiprocess_shared_ = True + class Services: """Test Service offerings Services """ def __init__(self): self.services = { - "off": - { - "name": "Service Offering", - "displaytext": "Service Offering", - "cpunumber": 1, - "cpuspeed": 100, # MHz - "memory": 128, # in MBs - }, - } + "off": + { + "name": "Service Offering", + "displaytext": "Service Offering", + "cpunumber": 1, + "cpuspeed": 100, # MHz + "memory": 128, # in MBs + }, + "small": + # Create a small virtual machine instance with disk offering + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "medium": # Create a medium virtual machine instance + { + "displayname": "testserver", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": + { + "tiny": + { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "small": + { + # Small service offering ID to for change VM + # service offering from medium to small + "name": "Small Instance", + "displaytext": "Small Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 256, + }, + "medium": + { + # Medium service offering ID to for + # change VM service offering from small to medium + "name": "Medium Instance", + "displaytext": "Medium Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 256, + } + }, + "ostype": 'CentOS 5.3 (64-bit)', + } + class TestCreateServiceOffering(cloudstackTestCase): - def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() @@ -60,8 +116,8 @@ class TestCreateServiceOffering(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - - @attr(tags = ["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) + + @attr(tags=["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) def test_01_create_service_offering(self): """Test to create service offering""" @@ -70,67 +126,65 @@ class TestCreateServiceOffering(cloudstackTestCase): # 2. The Cloud Database contains the valid information service_offering = ServiceOffering.create( - self.apiclient, - self.services["off"] - ) + self.apiclient, + self.services["off"] + ) self.cleanup.append(service_offering) self.debug("Created service offering with ID: %s" % service_offering.id) list_service_response = list_service_offering( - self.apiclient, - id=service_offering.id - ) + self.apiclient, + id=service_offering.id + ) self.assertEqual( - isinstance(list_service_response, list), - True, - "Check list response returns a valid list" - ) - + isinstance(list_service_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( - len(list_service_response), - 0, - "Check Service offering is created" - ) + len(list_service_response), + 0, + "Check Service offering is created" + ) service_response = list_service_response[0] self.assertEqual( - list_service_response[0].cpunumber, - self.services["off"]["cpunumber"], - "Check server id in createServiceOffering" - ) + list_service_response[0].cpunumber, + self.services["off"]["cpunumber"], + "Check server id in createServiceOffering" + ) self.assertEqual( - list_service_response[0].cpuspeed, - self.services["off"]["cpuspeed"], - "Check cpuspeed in createServiceOffering" - ) + list_service_response[0].cpuspeed, + self.services["off"]["cpuspeed"], + "Check cpuspeed in createServiceOffering" + ) self.assertEqual( - list_service_response[0].displaytext, - self.services["off"]["displaytext"], - "Check server displaytext in createServiceOfferings" - ) + list_service_response[0].displaytext, + self.services["off"]["displaytext"], + "Check server displaytext in createServiceOfferings" + ) self.assertEqual( - list_service_response[0].memory, - self.services["off"]["memory"], - "Check memory in createServiceOffering" - ) + list_service_response[0].memory, + self.services["off"]["memory"], + "Check memory in createServiceOffering" + ) self.assertEqual( - list_service_response[0].name, - self.services["off"]["name"], - "Check name in createServiceOffering" - ) + list_service_response[0].name, + self.services["off"]["name"], + "Check name in createServiceOffering" + ) return class TestServiceOfferings(cloudstackTestCase): - def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() self.cleanup = [] def tearDown(self): - try: #Clean up, terminate the created templates cleanup_resources(self.apiclient, self.cleanup) @@ -142,17 +196,61 @@ class TestServiceOfferings(cloudstackTestCase): @classmethod def setUpClass(cls): - cls.services = Services().services cls.api_client = super(TestServiceOfferings, cls).getClsTestClient().getApiClient() + cls.services = Services().services + domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services['mode'] = cls.zone.networktype + cls.service_offering_1 = ServiceOffering.create( - cls.api_client, - cls.services["off"] - ) + cls.api_client, + cls.services["off"] + ) cls.service_offering_2 = ServiceOffering.create( - cls.api_client, - cls.services["off"] - ) - cls._cleanup = [cls.service_offering_1] + cls.api_client, + cls.services["off"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = template.id + + cls.services["medium"]["zoneid"] = cls.zone.id + cls.services["medium"]["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=domain.id + ) + + cls.small_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["small"] + ) + + cls.medium_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["medium"] + ) + cls.medium_virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["medium"], + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.medium_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.small_offering, + cls.medium_offering, + cls.account + ] return @classmethod @@ -166,7 +264,7 @@ class TestServiceOfferings(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @attr(tags = ["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) + @attr(tags=["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) def test_02_edit_service_offering(self): """Test to update existing service offering""" @@ -178,8 +276,8 @@ class TestServiceOfferings(cloudstackTestCase): random_displaytext = random_gen() random_name = random_gen() - self.debug("Updating service offering with ID: %s" % - self.service_offering_1.id) + self.debug("Updating service offering with ID: %s" % + self.service_offering_1.id) cmd = updateServiceOffering.updateServiceOfferingCmd() #Add parameters for API call @@ -189,35 +287,35 @@ class TestServiceOfferings(cloudstackTestCase): self.apiclient.updateServiceOffering(cmd) list_service_response = list_service_offering( - self.apiclient, - id=self.service_offering_1.id - ) + self.apiclient, + id=self.service_offering_1.id + ) self.assertEqual( - isinstance(list_service_response, list), - True, - "Check list response returns a valid list" - ) - + isinstance(list_service_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( - len(list_service_response), - 0, - "Check Service offering is updated" - ) + len(list_service_response), + 0, + "Check Service offering is updated" + ) self.assertEqual( - list_service_response[0].displaytext, - random_displaytext, - "Check server displaytext in updateServiceOffering" - ) + list_service_response[0].displaytext, + random_displaytext, + "Check server displaytext in updateServiceOffering" + ) self.assertEqual( - list_service_response[0].name, - random_name, - "Check server name in updateServiceOffering" - ) + list_service_response[0].name, + random_name, + "Check server name in updateServiceOffering" + ) return - @attr(tags = ["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) + @attr(tags=["advanced", "advancedns", "smoke", "basic", "eip", "sg"]) def test_03_delete_service_offering(self): """Test to delete service offering""" @@ -225,20 +323,108 @@ class TestServiceOfferings(cloudstackTestCase): # 1. deleteServiceOffering should return # a valid information for newly created offering - self.debug("Deleting service offering with ID: %s" % - self.service_offering_2.id) + self.debug("Deleting service offering with ID: %s" % + self.service_offering_2.id) self.service_offering_2.delete(self.apiclient) list_service_response = list_service_offering( - self.apiclient, - id=self.service_offering_2.id - ) + self.apiclient, + id=self.service_offering_2.id + ) self.assertEqual( - list_service_response, - None, - "Check if service offering exists in listDiskOfferings" - ) + list_service_response, + None, + "Check if service offering exists in listDiskOfferings" + ) return + + @attr(tags=["advanced", "advancedns", "smoke"]) + def test_04_change_offering_small(self): + """Test to change service to a small capacity + """ + # Validate the following + # 1. Log in to the Vm .We should see that the CPU and memory Info of + # this Vm matches the one specified for "Small" service offering. + # 2. Using listVM command verify that this Vm + # has Small service offering Id. + + self.debug("Stopping VM - ID: %s" % self.medium_virtual_machine.id) + self.medium_virtual_machine.stop(self.apiclient) + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.medium_virtual_machine.id + ) + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Stopped': + self.debug("VM state: %s" % vm.state) + else: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % vm.id) + + self.debug("Change Service offering VM - ID: %s" % + self.medium_virtual_machine.id) + + cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() + cmd.id = self.medium_virtual_machine.id + cmd.serviceofferingid = self.small_offering.id + self.apiclient.changeServiceForVirtualMachine(cmd) + + self.debug("Starting VM - ID: %s" % self.medium_virtual_machine.id) + self.medium_virtual_machine.start(self.apiclient) + # Ensure that VM is in running state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.medium_virtual_machine.id + ) + + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + else: + raise Exception( + "Failed to start VM (ID: %s) after changing service offering" % vm.id) + + try: + ssh = self.medium_virtual_machine.get_ssh_client() + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" %\ + (self.medium_virtual_machine.ipaddress, e) + ) + + cpuinfo = ssh.execute("cat /proc/cpuinfo") + cpu_cnt = len([i for i in cpuinfo if "processor" in i]) + #'cpu MHz\t\t: 2660.499' + cpu_speed = [i for i in cpuinfo if "cpu MHz" in i][0].split()[3] + meminfo = ssh.execute("cat /proc/meminfo") + #MemTotal: 1017464 kB + total_mem = [i for i in meminfo if "MemTotal" in i][0].split()[1] + + self.debug( + "CPU count: %s, CPU Speed: %s, Mem Info: %s" % ( + cpu_cnt, + cpu_speed, + total_mem + )) + self.assertAlmostEqual( + int(cpu_cnt), + self.small_offering.cpunumber, + "Check CPU Count for small offering" + ) + self.assertAlmostEqual( + list_vm_response[0].cpuspeed, + self.small_offering.cpuspeed, + "Check CPU Speed for small offering" + ) + self.assertAlmostEqual( + int(total_mem) / 1024, # In MBs + self.small_offering.memory, + "Check Memory(kb) for small offering" + ) + return diff --git a/test/integration/smoke/test_vm_life_cycle.py b/test/integration/smoke/test_vm_life_cycle.py index d52ed9b8df6..8c78149addf 100644 --- a/test/integration/smoke/test_vm_life_cycle.py +++ b/test/integration/smoke/test_vm_life_cycle.py @@ -489,245 +489,6 @@ class TestVMLifeCycle(cloudstackTestCase): ) return - @attr(tags = ["advanced", "advancedns", "smoke"]) - def test_04_change_offering_small(self): - """Change Offering to a small capacity - """ - - # Validate the following - # 1. Log in to the Vm .We should see that the CPU and memory Info of - # this Vm matches the one specified for "Small" service offering. - # 2. Using listVM command verify that this Vm - # has Small service offering Id. - - self.debug("Stopping VM - ID: %s" % self.medium_virtual_machine.id) - - self.medium_virtual_machine.stop(self.apiclient) - - # Poll listVM to ensure VM is stopped properly - timeout = self.services["timeout"] - - while True: - time.sleep(self.services["sleep"]) - - # Ensure that VM is in stopped state - list_vm_response = list_virtual_machines( - self.apiclient, - id=self.medium_virtual_machine.id - ) - - if isinstance(list_vm_response, list): - - vm = list_vm_response[0] - if vm.state == 'Stopped': - self.debug("VM state: %s" % vm.state) - break - - if timeout == 0: - raise Exception( - "Failed to stop VM (ID: %s) in change service offering" % vm.id) - - timeout = timeout - 1 - - self.debug("Change Service offering VM - ID: %s" % - self.medium_virtual_machine.id) - - cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() - cmd.id = self.medium_virtual_machine.id - cmd.serviceofferingid = self.small_offering.id - self.apiclient.changeServiceForVirtualMachine(cmd) - - self.debug("Starting VM - ID: %s" % self.medium_virtual_machine.id) - self.medium_virtual_machine.start(self.apiclient) - - # Poll listVM to ensure VM is started properly - timeout = self.services["timeout"] - - while True: - time.sleep(self.services["sleep"]) - - # Ensure that VM is in running state - list_vm_response = list_virtual_machines( - self.apiclient, - id=self.medium_virtual_machine.id - ) - - if isinstance(list_vm_response, list): - - vm = list_vm_response[0] - if vm.state == 'Running': - self.debug("VM state: %s" % vm.state) - break - - if timeout == 0: - raise Exception( - "Failed to start VM (ID: %s) after changing service offering" % vm.id) - - timeout = timeout - 1 - - try: - ssh = self.medium_virtual_machine.get_ssh_client() - except Exception as e: - self.fail( - "SSH Access failed for %s: %s" % \ - (self.medium_virtual_machine.ipaddress, e) - ) - - cpuinfo = ssh.execute("cat /proc/cpuinfo") - - cpu_cnt = len([i for i in cpuinfo if "processor" in i]) - #'cpu MHz\t\t: 2660.499' - cpu_speed = [i for i in cpuinfo if "cpu MHz" in i ][0].split()[3] - - meminfo = ssh.execute("cat /proc/meminfo") - #MemTotal: 1017464 kB - total_mem = [i for i in meminfo if "MemTotal" in i][0].split()[1] - - self.debug( - "CPU count: %s, CPU Speed: %s, Mem Info: %s" % ( - cpu_cnt, - cpu_speed, - total_mem - )) - self.assertAlmostEqual( - int(cpu_cnt), - self.small_offering.cpunumber, - "Check CPU Count for small offering" - ) - - self.assertAlmostEqual( - list_vm_response[0].cpuspeed, - self.small_offering.cpuspeed, - "Check CPU Speed for small offering" - ) - self.assertAlmostEqual( - int(total_mem) / 1024, # In MBs - self.small_offering.memory, - "Check Memory(kb) for small offering" - ) - return - - @attr(tags = ["advanced", "advancedns", "smoke"]) - def test_05_change_offering_medium(self): - """Change Offering to a medium capacity - """ - # Validate the following - # 1. Log in to the Vm .We should see that the CPU and memory Info of - # this Vm matches the one specified for "Medium" service offering. - # 2. Using listVM command verify that this Vm - # has Medium service offering Id. - - self.debug("Stopping VM - ID: %s" % self.small_virtual_machine.id) - self.small_virtual_machine.stop(self.apiclient) - - # Poll listVM to ensure VM is stopped properly - timeout = self.services["timeout"] - - while True: - time.sleep(self.services["sleep"]) - - # Ensure that VM is in stopped state - list_vm_response = list_virtual_machines( - self.apiclient, - id=self.small_virtual_machine.id - ) - - if isinstance(list_vm_response, list): - - vm = list_vm_response[0] - if vm.state == 'Stopped': - self.debug("VM state: %s" % vm.state) - break - - if timeout == 0: - raise Exception( - "Failed to stop VM (ID: %s) in change service offering" % vm.id) - - timeout = timeout - 1 - - self.debug("Change service offering VM - ID: %s" % - self.small_virtual_machine.id) - - cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() - cmd.id = self.small_virtual_machine.id - cmd.serviceofferingid = self.medium_offering.id - self.apiclient.changeServiceForVirtualMachine(cmd) - - self.debug("Starting VM - ID: %s" % self.small_virtual_machine.id) - self.small_virtual_machine.start(self.apiclient) - - # Poll listVM to ensure VM is started properly - timeout = self.services["timeout"] - - while True: - time.sleep(self.services["sleep"]) - - # Ensure that VM is in running state - list_vm_response = list_virtual_machines( - self.apiclient, - id=self.small_virtual_machine.id - ) - - if isinstance(list_vm_response, list): - - vm = list_vm_response[0] - if vm.state == 'Running': - self.debug("VM state: %s" % vm.state) - break - - if timeout == 0: - raise Exception( - "Failed to start VM (ID: %s) after changing service offering" % vm.id) - - timeout = timeout - 1 - - list_vm_response = list_virtual_machines( - self.apiclient, - id=self.small_virtual_machine.id - ) - - try: - ssh_client = self.small_virtual_machine.get_ssh_client() - except Exception as e: - self.fail( - "SSH Access failed for %s: %s" % \ - (self.small_virtual_machine.ipaddress, e) - ) - - cpuinfo = ssh_client.execute("cat /proc/cpuinfo") - - cpu_cnt = len([i for i in cpuinfo if "processor" in i]) - #'cpu MHz\t\t: 2660.499' - cpu_speed = [i for i in cpuinfo if "cpu MHz" in i][0].split()[3] - - meminfo = ssh_client.execute("cat /proc/meminfo") - #MemTotal: 1017464 kB - total_mem = [i for i in meminfo if "MemTotal" in i][0].split()[1] - - self.debug( - "CPU count: %s, CPU Speed: %s, Mem Info: %s" % ( - cpu_cnt, - cpu_speed, - total_mem - )) - self.assertAlmostEqual( - int(cpu_cnt), - self.medium_offering.cpunumber, - "Check CPU Count for medium offering" - ) - - self.assertAlmostEqual( - list_vm_response[0].cpuspeed, - self.medium_offering.cpuspeed, - "Check CPU Speed for medium offering" - ) - - self.assertAlmostEqual( - int(total_mem) / 1024, # In MBs - self.medium_offering.memory, - "Check Memory(kb) for medium offering" - ) - return @attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"]) def test_06_destroy_vm(self): From 6975f6bac6b33d208bd68750457369ef0178c50f Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 24 May 2013 10:08:55 -0700 Subject: [PATCH 104/412] CLOUDSTACK-2377: UI - network menu - add guest network dialog only allows to add Isolated network, but not shared network. Change label to make it clear. --- ui/scripts/network.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 3eef1367e97..0ffaa1dce2f 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -321,7 +321,7 @@ listView: { actions: { add: { //add Isolated guest network (can't add Shared guest network here) - label: 'label.add.guest.network', + label: 'Add Isolated Guest Network', preFilter: function(args) { //Isolated networks is only supported in Advanced (SG-disabled) zone if(args.context.zoneType != 'Basic') @@ -331,8 +331,8 @@ }, createForm: { - title: 'label.add.guest.network', - desc: 'message.add.guest.network', + title: 'Add Isolated Guest Network', + desc: 'Add Isolated Guest Network with SourceNat', fields: { name: { label: 'label.name', validation: { required: true }, docID: 'helpGuestNetworkName' }, displayText: { label: 'label.display.text', validation: { required: true }, docID: 'helpGuestNetworkDisplayText'}, @@ -497,7 +497,7 @@ }); }, messages: { - notification: function() { return 'label.add.guest.network'; } + notification: function() { return 'Add Isolated Guest Network'; } } } }, From 3d16e82547c311c1f0cd22065d587556d70d511a Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 24 May 2013 10:33:28 -0700 Subject: [PATCH 105/412] CLOUDSTACK-2678: portable IP ranges - regions detailView - add viewAll path to Portable IP Ranges listView. --- ui/scripts/regions.js | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/ui/scripts/regions.js b/ui/scripts/regions.js index 2bbbbeea672..e86fd283aad 100644 --- a/ui/scripts/regions.js +++ b/ui/scripts/regions.js @@ -116,7 +116,10 @@ }, detailView: { name: 'Region details', - viewAll: { path: 'regions.GSLB', label: 'GSLB' }, + viewAll: [ + { path: 'regions.GSLB', label: 'GSLB' }, + { path: 'regions.portableIpRanges', label: 'Portable IP' } + ], actions: { edit: { label: 'label.edit.region', @@ -451,8 +454,40 @@ } } }, - - lbUnderGSLB: { + + portableIpRanges: { + id: 'portableIpRanges', + type: 'select', + title: 'Portable IP Ranges', + listView: { + id: 'portableIpRanges', + label: 'Portable IP Ranges', + fields: { + name: { label: 'label.name' }, + gslbdomainname: { label: 'GSLB Domain Name' }, + gslblbmethod: { label: 'Algorithm' } + }, + dataProvider: function(args) { + $.ajax({ + url: createURL('listPortableIpRanges'), + data: { + regionid: args.context.regions[0].id + }, + success: function(json) { + var items = json.listportableipresponse.portableip; + args.response.success({ + data: items + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } + }, + + lbUnderGSLB: { id: 'lbUnderGSLB', type: 'select', title: 'assigned load balancing', From 52a4962f4d3d83fc0eb3c60dbd6b5817a05eae9a Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 24 May 2013 12:06:09 -0700 Subject: [PATCH 106/412] If no tiers are present, show add tier dialog on load --- ui/modules/vpc/vpc.js | 141 +++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 71 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index e313b8ab2b5..302ab6cb388 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -15,6 +15,53 @@ // specific language governing permissions and limitations // under the License. (function($, cloudStack) { + var addTierDialog = function(args) { + var $placeholder = args.$placeholder; + var context = args.context; + var addAction = cloudStack.vpc.tiers.actions.add; + + cloudStack.dialog.createForm({ + context: context, + form: addAction.createForm, + after: function(args) { + var $loading = $('
    ').addClass('loading-overlay') + .prependTo($placeholder); + + addAction.action({ + context: context, + data: args.data, + response: { + success: function(args) { + cloudStack.ui.notifications.add( + // Notification + { + desc: addAction.label + }, + + // Success + function(args) { + $loading.remove(); + $placeholder.closest('.vpc-network-chart').trigger('reload'); + }, + + {}, + + // Error + function(args) { + $loading.remove(); + } + ); + }, + error: function(errorMsg) { + cloudStack.dialog.notice({ message: _s(errorMsg) }); + $loading.remove(); + } + } + }); + } + }); + }; + var elems = { tier: function(args) { var tier = args.tier; @@ -74,10 +121,10 @@ }, dashboardItems: args.dashboardItems }).addClass('router'); - + $router.find('.info, .detail-link').remove(); $router.find('.header').prepend($('').addClass('icon').html(' ')); - + return $router; }, @@ -87,66 +134,12 @@ $placeholder.append($('').append('Create network')); $placeholder.click(function() { - var addAction = cloudStack.vpc.tiers.actions.add; - var form = cloudStack.dialog.createForm({ + addTierDialog({ context: context, - form: addAction.createForm, - after: function(args) { - var $loading = $('
    ').addClass('loading-overlay') - .prependTo($placeholder); - - addAction.action({ - context: context, - data: args.data, - response: { - success: function(args) { - var tier = args.data; - - cloudStack.ui.notifications.add( - // Notification - { - desc: addAction.label - }, - - // Success - function(args) { - $loading.remove(); - $placeholder.closest('.vpc-network-chart').trigger('reload'); - - // addNewTier({ - // ipAddresses: ipAddresses, - // $browser: $browser, - // tierDetailView: tierDetailView, - // context: $.extend(true, {}, context, { - // networks: [tier] - // }), - // tier: tier, - // acl: acl, - // $tiers: $tiers, - // actions: actions, - // actionPreFilter: actionPreFilter, - // vmListView: vmListView - // }); - }, - - {}, - - // Error - function(args) { - $loading.remove(); - } - ); - }, - error: function(errorMsg) { - cloudStack.dialog.notice({ message: _s(errorMsg) }); - $loading.remove(); - } - } - }); - } + $placeholder: $placeholder }); }); - + return $placeholder; }, @@ -170,7 +163,7 @@ } else { $total.find('span').html(dashboardItem.total ? dashboardItem.total : 0); } - + $dashboardItem.append($total, $name); $dashboardItem.appendTo($dashboard); @@ -214,7 +207,7 @@ var vpcChart = function(args) { var context = args.context; var vpcItem = context.vpc[0]; - + var chart = function(args) { args = args ? args : {}; @@ -232,6 +225,9 @@ response: { success: function(data) { var tiers = data.tiers; + var $placeholder = elems.tierPlaceholder({ + context: context + }); $(tiers).map(function(index, tier) { var $tier = elems.tier({ @@ -239,34 +235,37 @@ tier: tier, dashboardItems: tier._dashboardItems }); - $tier.appendTo($tiers); }); // Add placeholder tier - $tiers.append(elems.tierPlaceholder({ - context: context - })); - + $tiers.append($placeholder); $loading.remove(); - + + if (!tiers.length) { + addTierDialog({ + context: context, + $placeholder: $placeholder + }); + } + if (args.complete) { args.complete($chart); } - + // Router - $router = elems.router({ + elems.router({ context: context, dashboardItems: data.routerDashboard }).appendTo($chart); } - } + } }); $chart.bind('reload', function() { chart({ complete: function($newChart) { - $chart.replaceWith($newChart); + $chart.replaceWith($newChart); } }); }); From 489396940e2487fa6254c837873dfd71bdd3de67 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 24 May 2013 12:06:58 -0700 Subject: [PATCH 107/412] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 016554a5f75..929227d8604 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ debian/*.debhelper replace.properties.tmp build-indep-stamp configure-stamp +*_flymake.js \ No newline at end of file From 6f65a5bbe7635e0b0b81143bcb61fd2a428231f8 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 24 May 2013 12:06:58 -0700 Subject: [PATCH 108/412] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 016554a5f75..929227d8604 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,4 @@ debian/*.debhelper replace.properties.tmp build-indep-stamp configure-stamp +*_flymake.js \ No newline at end of file From cd7287a4e17c605be71910b260540dfa685f4ef8 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 24 May 2013 13:06:51 -0700 Subject: [PATCH 109/412] Site-to-site VPN UI: Show confirm dialog if no gateways exist --- ui/modules/vpc/vpc.js | 67 ++++++++++++++++++++++++++++++++----------- ui/scripts/vpc.js | 43 ++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 302ab6cb388..6eb721a9a2b 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -168,27 +168,60 @@ $dashboardItem.appendTo($dashboard); $dashboardItem.click(function() { - $('#browser .container').cloudBrowser('addPanel', { - title: tier.name + ' - ' + dashboardItem.name, - maximizeIfSelected: true, - complete: function($panel) { - var section = cloudStack.vpc.sections[id]; - var $section = $('
    '); + var section = cloudStack.vpc.sections[id]; + var $section = $('
    '); + var $loading = $('
    ').addClass('loading-overlay'); + + if ($.isFunction(section)) { + section = cloudStack.vpc.sections[id](); + } - if ($.isFunction(section)) { - section = cloudStack.vpc.sections[id](); + var before = section.before; + var load = function() { + $('#browser .container').cloudBrowser('addPanel', { + title: tier.name + ' - ' + dashboardItem.name, + maximizeIfSelected: true, + complete: function($panel) { + if (section.listView) { + $section.listView($.extend(true, {}, section, { + onActionComplete: function() { + $dashboardItem.closest('.vpc-network-chart').trigger('reload'); + }, + context: context + })); + } + + $section.appendTo($panel); } + }); + }; - if (section.listView) { - $section.listView($.extend(true, {}, section, { - onActionComplete: function() { - $dashboardItem.closest('.vpc-network-chart').trigger('reload'); - }, - context: context - })); + before.check({ + context: context, + response: { + success: function(result) { + // true means content exists + if (result) { + load(); + } else { + cloudStack.dialog.confirm({ + message: before.messages.confirm, + action: function() { + $loading.appendTo($dashboardItem.closest('.vpc-network-chart')); + before.action({ + context: context, + response: { + success: function() { + $loading.remove(); + $dashboardItem.closest('.vpc-network-chart').trigger('reload'); + load(); + } + } + }); + } + }) + } } - - $section.appendTo($panel); } }); }); diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 2183aba55fd..3581b88ea34 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -801,7 +801,48 @@ // 'listView' block // // -- use this as a flag for VPC chart to render as a list view - listView: true + listView: true, + before: { + messages: { + confirm: 'Please confirm that you would like to create a site-to-site VPN gateway for this VPC.', + notification: 'Create site-to-site VPN gateway' + }, + check: function(args) { + var items; + + $.ajax({ + url: createURL('listVpnGateways&listAll=true'), + data: { + vpcid: args.context.vpc[0].id + }, + success: function(json) { + var items = json.listvpngatewaysresponse.vpngateway; + + args.response.success(items && items.length); + } + }); + }, + action: function(args) { + $.ajax({ + url: createURL("createVpnGateway"), + data: { + vpcid: args.context.vpc[0].id + }, + success: function(json) { + var jid = json.createvpngatewayresponse.jobid; + var pollTimer = setInterval(function() { + pollAsyncJobResult({ + _custom: { jobId: jid }, + complete: function() { + clearInterval(pollTimer); + args.response.success(); + } + }); + }, g_queryAsyncJobResultInterval); + } + }); + } + } }); } }, From 72fa89eb7a326582abac2554a25786f339ef58ca Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 24 May 2013 13:07:42 -0700 Subject: [PATCH 110/412] Fix for dashboard items without before: {} block --- ui/modules/vpc/vpc.js | 52 ++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 6eb721a9a2b..1a3b3e82001 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -196,34 +196,36 @@ }); }; - before.check({ - context: context, - response: { - success: function(result) { - // true means content exists - if (result) { - load(); - } else { - cloudStack.dialog.confirm({ - message: before.messages.confirm, - action: function() { - $loading.appendTo($dashboardItem.closest('.vpc-network-chart')); - before.action({ - context: context, - response: { - success: function() { - $loading.remove(); - $dashboardItem.closest('.vpc-network-chart').trigger('reload'); - load(); + if (before) { + before.check({ + context: context, + response: { + success: function(result) { + // true means content exists + if (result) { + load(); + } else { + cloudStack.dialog.confirm({ + message: before.messages.confirm, + action: function() { + $loading.appendTo($dashboardItem.closest('.vpc-network-chart')); + before.action({ + context: context, + response: { + success: function() { + $loading.remove(); + $dashboardItem.closest('.vpc-network-chart').trigger('reload'); + load(); + } } - } - }); - } - }) + }); + } + }) + } } } - } - }); + }); + } }); }); From 2a966f35ff05e458e1d4df230920cdd926309cac Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 24 May 2013 13:10:31 -0700 Subject: [PATCH 111/412] Another fix for before: {} block --- ui/modules/vpc/vpc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/modules/vpc/vpc.js b/ui/modules/vpc/vpc.js index 1a3b3e82001..a6037c00fb7 100644 --- a/ui/modules/vpc/vpc.js +++ b/ui/modules/vpc/vpc.js @@ -225,6 +225,8 @@ } } }); + } else { + load(); } }); }); From 2ecf9e3293d9b5f1ccffe0736bc8ef0cbf3b1529 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Fri, 24 May 2013 14:08:28 -0700 Subject: [PATCH 112/412] CLOUDSTACK-2680: Async job expunge thread - expunge only: 1) Unfinished jobs that are yet to be processed. 2) Completed jobs The jobs that are in process, will be skipped by the expunge thread Conflicts: server/src/com/cloud/async/dao/AsyncJobDao.java server/src/com/cloud/async/dao/AsyncJobDaoImpl.java server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java --- .../com/cloud/async/AsyncJobManagerImpl.java | 17 ++-- .../src/com/cloud/async/dao/AsyncJobDao.java | 5 +- .../com/cloud/async/dao/AsyncJobDaoImpl.java | 88 ++++++++++++------- 3 files changed, 70 insertions(+), 40 deletions(-) diff --git a/server/src/com/cloud/async/AsyncJobManagerImpl.java b/server/src/com/cloud/async/AsyncJobManagerImpl.java index 47d793fd0b0..0101a8a0abf 100644 --- a/server/src/com/cloud/async/AsyncJobManagerImpl.java +++ b/server/src/com/cloud/async/AsyncJobManagerImpl.java @@ -621,11 +621,18 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, // limit to 100 jobs per turn, this gives cleanup throughput as 600 jobs per minute // hopefully this will be fast enough to balance potential growth of job table - List l = _jobDao.getExpiredJobs(cutTime, 100); - if(l != null && l.size() > 0) { - for(AsyncJobVO job : l) { - expungeAsyncJob(job); - } + //1) Expire unfinished jobs that weren't processed yet + List l = _jobDao.getExpiredUnfinishedJobs(cutTime, 100); + for(AsyncJobVO job : l) { + s_logger.trace("Expunging unfinished job " + job); + expungeAsyncJob(job); + } + + //2) Expunge finished jobs + List completedJobs = _jobDao.getExpiredCompletedJobs(cutTime, 100); + for(AsyncJobVO job : completedJobs) { + s_logger.trace("Expunging completed job " + job); + expungeAsyncJob(job); } // forcefully cancel blocking queue items if they've been staying there for too long diff --git a/server/src/com/cloud/async/dao/AsyncJobDao.java b/server/src/com/cloud/async/dao/AsyncJobDao.java index 9d207593574..9ab9b224c10 100644 --- a/server/src/com/cloud/async/dao/AsyncJobDao.java +++ b/server/src/com/cloud/async/dao/AsyncJobDao.java @@ -26,6 +26,7 @@ import com.cloud.utils.db.GenericDao; public interface AsyncJobDao extends GenericDao { AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId); List findInstancePendingAsyncJobs(AsyncJob.Type instanceType, Long accountId); - List getExpiredJobs(Date cutTime, int limit); + List getExpiredUnfinishedJobs(Date cutTime, int limit); void resetJobProcess(long msid, int jobResultCode, String jobResultMessage); -} + List getExpiredCompletedJobs(Date cutTime, int limit); +} \ No newline at end of file diff --git a/server/src/com/cloud/async/dao/AsyncJobDaoImpl.java b/server/src/com/cloud/async/dao/AsyncJobDaoImpl.java index 4793a6edc12..b2c0d9cc4e0 100644 --- a/server/src/com/cloud/async/dao/AsyncJobDaoImpl.java +++ b/server/src/com/cloud/async/dao/AsyncJobDaoImpl.java @@ -42,17 +42,19 @@ public class AsyncJobDaoImpl extends GenericDaoBase implements private static final Logger s_logger = Logger.getLogger(AsyncJobDaoImpl.class.getName()); private final SearchBuilder pendingAsyncJobSearch; - private final SearchBuilder pendingAsyncJobsSearch; - private final SearchBuilder expiringAsyncJobSearch; - - public AsyncJobDaoImpl() { - pendingAsyncJobSearch = createSearchBuilder(); - pendingAsyncJobSearch.and("instanceType", pendingAsyncJobSearch.entity().getInstanceType(), - SearchCriteria.Op.EQ); - pendingAsyncJobSearch.and("instanceId", pendingAsyncJobSearch.entity().getInstanceId(), - SearchCriteria.Op.EQ); - pendingAsyncJobSearch.and("status", pendingAsyncJobSearch.entity().getStatus(), - SearchCriteria.Op.EQ); + private final SearchBuilder pendingAsyncJobsSearch; + private final SearchBuilder expiringUnfinishedAsyncJobSearch; + private final SearchBuilder expiringCompletedAsyncJobSearch; + + + public AsyncJobDaoImpl() { + pendingAsyncJobSearch = createSearchBuilder(); + pendingAsyncJobSearch.and("instanceType", pendingAsyncJobSearch.entity().getInstanceType(), + SearchCriteria.Op.EQ); + pendingAsyncJobSearch.and("instanceId", pendingAsyncJobSearch.entity().getInstanceId(), + SearchCriteria.Op.EQ); + pendingAsyncJobSearch.and("status", pendingAsyncJobSearch.entity().getStatus(), + SearchCriteria.Op.EQ); pendingAsyncJobSearch.done(); pendingAsyncJobsSearch = createSearchBuilder(); @@ -64,27 +66,36 @@ public class AsyncJobDaoImpl extends GenericDaoBase implements SearchCriteria.Op.EQ); pendingAsyncJobsSearch.done(); - expiringAsyncJobSearch = createSearchBuilder(); - expiringAsyncJobSearch.and("created", expiringAsyncJobSearch.entity().getCreated(), + expiringUnfinishedAsyncJobSearch = createSearchBuilder(); + expiringUnfinishedAsyncJobSearch.and("created", expiringUnfinishedAsyncJobSearch.entity().getCreated(), SearchCriteria.Op.LTEQ); - expiringAsyncJobSearch.done(); - } - - public AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId) { - SearchCriteria sc = pendingAsyncJobSearch.create(); - sc.setParameters("instanceType", instanceType); - sc.setParameters("instanceId", instanceId); - sc.setParameters("status", AsyncJobResult.STATUS_IN_PROGRESS); - - List l = listIncludingRemovedBy(sc); - if(l != null && l.size() > 0) { - if(l.size() > 1) { - s_logger.warn("Instance " + instanceType + "-" + instanceId + " has multiple pending async-job"); - } - - return l.get(0); - } - return null; + expiringUnfinishedAsyncJobSearch.and("completeMsId", expiringUnfinishedAsyncJobSearch.entity().getCompleteMsid(), SearchCriteria.Op.NULL); + expiringUnfinishedAsyncJobSearch.and("jobStatus", expiringUnfinishedAsyncJobSearch.entity().getStatus(), SearchCriteria.Op.EQ); + expiringUnfinishedAsyncJobSearch.done(); + + expiringCompletedAsyncJobSearch = createSearchBuilder(); + expiringCompletedAsyncJobSearch.and("created", expiringCompletedAsyncJobSearch.entity().getCreated(), + SearchCriteria.Op.LTEQ); + expiringCompletedAsyncJobSearch.and("completeMsId", expiringCompletedAsyncJobSearch.entity().getCompleteMsid(), SearchCriteria.Op.NNULL); + expiringCompletedAsyncJobSearch.and("jobStatus", expiringCompletedAsyncJobSearch.entity().getStatus(), SearchCriteria.Op.NEQ); + expiringCompletedAsyncJobSearch.done(); + } + + public AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId) { + SearchCriteria sc = pendingAsyncJobSearch.create(); + sc.setParameters("instanceType", instanceType); + sc.setParameters("instanceId", instanceId); + sc.setParameters("status", AsyncJobResult.STATUS_IN_PROGRESS); + + List l = listIncludingRemovedBy(sc); + if(l != null && l.size() > 0) { + if(l.size() > 1) { + s_logger.warn("Instance " + instanceType + "-" + instanceId + " has multiple pending async-job"); + } + + return l.get(0); + } + return null; } public List findInstancePendingAsyncJobs(AsyncJob.Type instanceType, Long accountId) { @@ -99,9 +110,20 @@ public class AsyncJobDaoImpl extends GenericDaoBase implements return listBy(sc); } - public List getExpiredJobs(Date cutTime, int limit) { - SearchCriteria sc = expiringAsyncJobSearch.create(); + @Override + public List getExpiredUnfinishedJobs(Date cutTime, int limit) { + SearchCriteria sc = expiringUnfinishedAsyncJobSearch.create(); sc.setParameters("created", cutTime); + sc.setParameters("jobStatus", 0); + Filter filter = new Filter(AsyncJobVO.class, "created", true, 0L, (long)limit); + return listIncludingRemovedBy(sc, filter); + } + + @Override + public List getExpiredCompletedJobs(Date cutTime, int limit) { + SearchCriteria sc = expiringCompletedAsyncJobSearch.create(); + sc.setParameters("created", cutTime); + sc.setParameters("jobStatus", 0); Filter filter = new Filter(AsyncJobVO.class, "created", true, 0L, (long)limit); return listIncludingRemovedBy(sc, filter); } From b247ba6b4fc3516c87d193ef5bae34c619db2134 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 24 May 2013 15:21:32 -0700 Subject: [PATCH 113/412] CLOUDSTACK-1974: Dedicate Guest VLAN Range - UI - Infrastructure menu - physical network - Guest - devailView - (1) add new tab "Dedicated VLAN Ranges" that list dedicated Guest VLAN Ranges under the physical network. (2) implement "Dedicate Guest VLAN Range" action. --- ui/scripts/system.js | 92 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 2e23fc40202..32303d3ed0f 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -1085,10 +1085,13 @@ tabFilter: function(args) { var hiddenTabs = []; - if (selectedZoneObj.networktype == 'Basic') + if (selectedZoneObj.networktype == 'Basic') { hiddenTabs.push("network"); - else //selectedZoneObj.networktype == 'Advanced' + hiddenTabs.push("dedicatedGuestVlanRanges"); + } + else { //selectedZoneObj.networktype == 'Advanced' hiddenTabs.push("ipAddresses"); + } return hiddenTabs; }, @@ -2115,7 +2118,90 @@ } } } - } + }, + + dedicatedGuestVlanRanges : { + title: 'Dedicated VLAN Ranges', + listView: { + section: 'dedicatedGuestVlanRanges', + id: 'dedicatedGuestVlanRanges', + fields: { + guestvlanrange: { label: 'VLAN Range(s)' }, + domain: { label: 'label.domain' }, + account: { label: 'label.account' } + }, + dataProvider: function(args) { + $.ajax({ + url: createURL('listDedicatedGuestVlanRanges'), + data: { + physicalnetworkid: args.context.physicalNetworks[0].id + }, + success: function(json) { + var items = json.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange; + args.response.success({ data: items }) + } + }); + }, + actions: { + add: { + label: 'Dedicate VLAN Range', + messages: { + notification: function(args) { + return 'Dedicate VLAN Range'; + } + }, + createForm: { + title: 'Dedicate VLAN Range', + fields: { + vlanrange: { label: 'VLAN Range(s)', validation: { required: true } }, + account: { label: 'label.account', validation: { required: true } }, + domainid: { + label: 'label.domain', + validation: { required: true }, + select: function(args) { + $.ajax({ + url: createURL('listDomains'), + data: { listAll: true }, + success: function(json) { + args.response.success({ + data: $.map(json.listdomainsresponse.domain, function(domain) { + return { + id: domain.id, + description: domain.path + }; + }) + }); + } + }); + } + } + } + }, + action: function(args) { + var data = { + physicalnetworkid: args.context.physicalNetworks[0].id, + vlanrange: args.data.vlanrange, + domainid: args.data.domainid, + account: args.data.account + }; + $.ajax({ + url: createURL('dedicateGuestVlanRange'), + data: data, + success: function(json) { + var item = json.dedicateguestvlanrangeresponse.dedicatedguestvlanrange; + args.response.success({ data: item }); + } + }); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + } + } + } + } } } } From 11a7e46dfb8123b5ca96d34c41f401d91df332fa Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 24 May 2013 15:45:10 -0700 Subject: [PATCH 114/412] CLOUDSTACK-1974: Dedicate Guest VLAN Range - UI - Infrastructure menu - physical network - Guest - Dedicated Guest VLAN Range tab - implement Release Dedicated VLAN Range action. --- ui/scripts/system.js | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 32303d3ed0f..8b56bda08e5 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -2199,6 +2199,73 @@ } } } + }, + + //??? + detailView: { + name: 'VLAN Range details', + actions: { + remove: { + label: 'Delete VLAN Range', + messages: { + confirm: function(args) { + return 'Please confirm you want to delete VLAN Range'; + }, + notification: function(args) { + return 'Delete VLAN Range'; + } + }, + action: function(args) { + var data = { + id: args.context.dedicatedGuestVlanRanges[0].id + }; + $.ajax({ + url: createURL('releaseDedicatedGuestVlanRange'), + data: data, + async: true, + success: function(json) { + var jid = json.releasededicatedguestvlanrangeresponse.jobid; + args.response.success( + { + _custom: { jobId: jid } + } + ); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + + tabs: { + details: { + title: 'label.details', + fields: [ + { + guestvlanrange: { label: 'VLAN Range(s)' }, + }, + { + domain: { label: 'label.domain' }, + account: { label: 'label.account' }, + id: { label: 'label.id' } + } + ], + dataProvider: function(args) { + $.ajax({ + url: createURL('listDedicatedGuestVlanRanges'), + data: { + id: args.context.dedicatedGuestVlanRanges[0].id + }, + success: function(json) { + var item = json.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange[0]; + args.response.success({ data: item }) + } + }); + } + } + } } } } From 7cc1e82210f12a615f1f3b425e8076a33ab471e1 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 24 May 2013 16:17:39 -0700 Subject: [PATCH 115/412] CLOUDSTACK-1974: Dedicate Guest VLAN Range - UI - Infrastructure menu - physical network - Guest - Release Dedicated VLAN Range action - change label and message. --- ui/scripts/system.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 8b56bda08e5..ebf173b6ed5 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -2200,19 +2200,18 @@ } } }, - - //??? + detailView: { name: 'VLAN Range details', actions: { remove: { - label: 'Delete VLAN Range', + label: 'Release dedicated VLAN range', messages: { confirm: function(args) { - return 'Please confirm you want to delete VLAN Range'; + return 'Please confirm you want to release dedicated VLAN range'; }, notification: function(args) { - return 'Delete VLAN Range'; + return 'Release dedicated VLAN range'; } }, action: function(args) { From d99fae07c7ee750452e346e69f582f0342280f2a Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Fri, 24 May 2013 16:36:45 -0700 Subject: [PATCH 116/412] CLOUDSTACK-747: Infrastructure menu - service providers - change display name of internalLbVm to be Internal LB VM. --- ui/scripts/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index ebf173b6ed5..9ac3b3083db 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -12706,7 +12706,7 @@ nspHardcodingArray.push( { id: 'InternalLbVm', - name: 'InternalLbVm', + name: 'Internal LB VM', state: nspMap.InternalLbVm ? nspMap.InternalLbVm.state : 'Disabled' } ); From a1437ae82e9c686139c85ac9d3eb039bf968c014 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 24 May 2013 16:45:15 -0700 Subject: [PATCH 117/412] CLOUDSTACK-2563: Network ACL: Allow protocol 1(icmp) paired with icmp type/code --- .../api/command/user/network/CreateNetworkACLCmd.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java index 275fa1866b6..3a56fe4642a 100644 --- a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.cloud.async.AsyncJob; @@ -98,7 +99,15 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd { // /////////////////////////////////////////////////// public String getProtocol() { - return protocol.trim(); + String p = protocol.trim(); + // Deal with ICMP(protocol number 1) specially because it need to be paired with icmp type and code + if(StringUtils.isNumeric(p)){ + int protoNumber = Integer.parseInt(p); + if (protoNumber == 1) { + p = "icmp"; + } + } + return p; } public List getSourceCidrList() { From e3a32867586bbcebe04b009755bfcc116eb1a296 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 24 May 2013 17:08:31 -0700 Subject: [PATCH 118/412] CLOUDSTACK-2681: Suppress failed to apply port forwarding rule error when rollback --- .../command/user/firewall/CreatePortForwardingRuleCmd.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java index 40128526ce0..549de76f2c6 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java @@ -198,7 +198,11 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P _firewallService.revokeRelatedFirewallRule(getEntityId(), true); } - _rulesService.revokePortForwardingRule(getEntityId(), true); + try { + _rulesService.revokePortForwardingRule(getEntityId(), true); + } catch (Exception ex){ + //Ignore e.g. failed to apply rules to device error + } throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to apply port forwarding rule"); } From a21a4e5ac43af0f225b6e56e3e73ea7e570510d9 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 24 May 2013 17:10:55 -0700 Subject: [PATCH 119/412] CLOUDSTACK-2682: Sleep 1 second before switch to BACKUP To prevent backup.sh preempt master.sh when keepalived switch to MASTER then back to BACKUP immediately. Since it would take at least 3 seconds for BACKUP to switch to MASTER(vrrp timeout), the fix won't cause chaos when keepalive try to switch to MASTER. --- .../debian/config/root/redundant_router/backup.sh.templ | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patches/systemvm/debian/config/root/redundant_router/backup.sh.templ b/patches/systemvm/debian/config/root/redundant_router/backup.sh.templ index 7a1bd44584a..32c811b26d4 100644 --- a/patches/systemvm/debian/config/root/redundant_router/backup.sh.templ +++ b/patches/systemvm/debian/config/root/redundant_router/backup.sh.templ @@ -16,6 +16,8 @@ # specific language governing permissions and limitations # under the License. +sleep 1 + source /root/func.sh lock="biglock" From a4356b559cb1401c790e1aa62606c1f256e75229 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sat, 25 May 2013 15:03:56 +0530 Subject: [PATCH 120/412] More fixes to regression tests: - Remove extraneous non-ascii chars - cloudconnection is auto GCed. No explicit cleanup required. - Add environment tags for shared network tests - Invalid account.account references - Indentation errors - Skip the storage motion test on unsuitable host environment - remove copy template test. covered in the smoke tests - invalid keys in service dictionary - Unskipping tests skipped tests - Signed-off-by: Prasanna Santhanam --- .../component/test_custom_hostname.py | 2 +- .../component/test_high_availability.py | 1 - .../component/test_host_high_availability.py | 2 +- .../component/test_project_limits.py | 8 +- .../component/test_project_resources.py | 4 +- .../component/test_project_usage.py | 2 +- .../component/test_shared_networks.py | 13 +++ .../component/test_storage_motion.py | 2 - test/integration/component/test_tags.py | 2 +- test/integration/component/test_templates.py | 89 ------------------- .../component/test_vm_passwdenabled.py | 4 +- .../component/test_vpc_network_lbrules.py | 15 ---- .../component/test_vpc_network_pfrules.py | 8 -- .../test_vpc_network_staticnatrule.py | 4 - 14 files changed, 25 insertions(+), 131 deletions(-) diff --git a/test/integration/component/test_custom_hostname.py b/test/integration/component/test_custom_hostname.py index e5452141d9c..a85f619fc00 100644 --- a/test/integration/component/test_custom_hostname.py +++ b/test/integration/component/test_custom_hostname.py @@ -104,7 +104,7 @@ class TestInstanceNameFlagTrue(cloudstackTestCase): cls.services = Services().services # Get Zone, default template cls.zone = get_zone(cls.api_client, cls.services) - cls.services["mode"] = cls.zone.networktype + cls.services["mode"] = cls.zone.networktype cls.template = get_template( cls.api_client, cls.zone.id, diff --git a/test/integration/component/test_high_availability.py b/test/integration/component/test_high_availability.py index cd2dfcea559..7b0f78e2446 100644 --- a/test/integration/component/test_high_availability.py +++ b/test/integration/component/test_high_availability.py @@ -169,7 +169,6 @@ class TestHighAvailability(cloudstackTestCase): try: #Clean up, terminate the created accounts, domains etc cleanup_resources(self.apiclient, self.cleanup) - self.testClient.close() except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return diff --git a/test/integration/component/test_host_high_availability.py b/test/integration/component/test_host_high_availability.py index 8c66d175dd7..7a1ad69c7f5 100644 --- a/test/integration/component/test_host_high_availability.py +++ b/test/integration/component/test_host_high_availability.py @@ -156,7 +156,7 @@ class TestHostHighAvailability(cloudstackTestCase): """ Test VM deployments (Create HA enabled Compute Service Offering and VM) """ # Steps, - #1. Create a Compute service offering with the “Offer HA” option selected. + #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. # Validations, #1. Ensure that the offering is created and that in the UI the “Offer HA” field is enabled (Yes) diff --git a/test/integration/component/test_project_limits.py b/test/integration/component/test_project_limits.py index 9184dca5202..af99717ad16 100644 --- a/test/integration/component/test_project_limits.py +++ b/test/integration/component/test_project_limits.py @@ -201,8 +201,8 @@ class TestProjectLimits(cloudstackTestCase): project = Project.create( self.apiclient, self.services["project"], - account=self.admin.account.name, - domainid=self.admin.account.domainid + account=self.admin.name, + domainid=self.admin.domainid ) # Cleanup created project at end of test self.cleanup.append(project) @@ -343,8 +343,8 @@ class TestProjectLimits(cloudstackTestCase): project = Project.create( self.apiclient, self.services["project"], - account=self.admin.account.name, - domainid=self.admin.account.domainid + account=self.admin.name, + domainid=self.admin.domainid ) # Cleanup created project at end of test self.cleanup.append(project) diff --git a/test/integration/component/test_project_resources.py b/test/integration/component/test_project_resources.py index 84141889f3f..378238e70fd 100644 --- a/test/integration/component/test_project_resources.py +++ b/test/integration/component/test_project_resources.py @@ -651,9 +651,9 @@ class TestTemplates(cloudstackTestCase): """Test use of private template in a project """ # 1. Create a project - # 2. Verify that in order to use somebody’s Private template for vm + # 2. Verify that in order to use somebody's Private template for vm # creation in the project, permission to use the template has to - # be granted to the Project (use API “updateTemplatePermissions” + # be granted to the Project (use API 'updateTemplatePermissions' # with project id to achieve that). self.debug("Deploying VM for with public template: %s" % diff --git a/test/integration/component/test_project_usage.py b/test/integration/component/test_project_usage.py index ab789e1c13d..bb253e1c04a 100644 --- a/test/integration/component/test_project_usage.py +++ b/test/integration/component/test_project_usage.py @@ -124,7 +124,7 @@ class TestVmUsage(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client, cls.services) cls.zone = get_zone(cls.api_client, cls.services) - cls.services['mode'] = cls.cls.zone.networktype + cls.services['mode'] = cls.zone.networktype template = get_template( cls.api_client, diff --git a/test/integration/component/test_shared_networks.py b/test/integration/component/test_shared_networks.py index 9845826bea6..5f964190356 100644 --- a/test/integration/component/test_shared_networks.py +++ b/test/integration/component/test_shared_networks.py @@ -235,6 +235,7 @@ class TestSharedNetworks(cloudstackTestCase): raise Exception("Warning: Exception during network cleanup : %s" % e) return + @attr(tags=["advanced", "advancedns"]) def test_sharedNetworkOffering_01(self): """ Test shared network Offering 01 """ @@ -372,6 +373,7 @@ class TestSharedNetworks(cloudstackTestCase): ) self.debug("NetworkOffering created and enabled: %s" % self.shared_network_offering.id) + @attr(tags=["advanced", "advancedns"]) def test_sharedNetworkOffering_02(self): """ Test Shared Network Offering 02 """ @@ -459,6 +461,7 @@ class TestSharedNetworks(cloudstackTestCase): except Exception as e: self.debug("Network Offering creation failed with vlan as False in advance mode and shared guest type.") + @attr(tags=["advanced", "advancedns"]) def test_sharedNetworkOffering_03(self): """ Test Shared Network Offering 03 """ @@ -547,6 +550,7 @@ class TestSharedNetworks(cloudstackTestCase): except Exception as e: self.debug("Network Offering creation failed with vlan as true and ip ranges as False in advance mode and with shared guest type.") + @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_All(self): """ Test Shared Network ALL """ @@ -829,6 +833,7 @@ class TestSharedNetworks(cloudstackTestCase): if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: self.fail("Virtual machine ip should be from the ip range assigned to network created.") + @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_accountSpecific(self): """ Test Shared Networm with scope account """ @@ -1092,6 +1097,7 @@ class TestSharedNetworks(cloudstackTestCase): if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: self.fail("Virtual machine ip should be from the ip range assigned to network created.") + @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_domainSpecific(self): """ Test Shared Network with scope domain """ @@ -1446,6 +1452,7 @@ class TestSharedNetworks(cloudstackTestCase): if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: self.fail("Virtual machine ip should be from the ip range assigned to network created.") + @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_projectSpecific(self): """ Test Shared Network with scope project """ @@ -1740,6 +1747,7 @@ class TestSharedNetworks(cloudstackTestCase): if netaddr.IPAddress(unicode(vms[0].nic[0].ipaddress)) not in ip_range: self.fail("Virtual machine ip should be from the ip range assigned to network created.") + @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_usedVlan(self): """ Test Shared Network with used vlan 01 """ @@ -1898,6 +1906,7 @@ class TestSharedNetworks(cloudstackTestCase): except Exception as e: self.debug("Network creation failed because the valn id being used by another network.") + @attr(tags=["advanced", "advancedns"]) def test_createSharedNetwork_usedVlan2(self): """ Test Shared Network with used vlan 02 """ @@ -2093,6 +2102,7 @@ class TestSharedNetworks(cloudstackTestCase): except Exception as e: self.debug("Network creation failed because the valn id being used by another network.") + @attr(tags=["advanced", "advancedns"]) def test_deployVM_multipleSharedNetwork(self): """ Test Vm deployment with multiple shared networks """ @@ -2350,6 +2360,7 @@ class TestSharedNetworks(cloudstackTestCase): self.assertTrue(self.network1_admin_account_virtual_machine.nic[0].ipaddress is not None, "ip should be assigned to running virtual machine") + @attr(tags=["advanced", "advancedns"]) def test_deployVM_isolatedAndShared(self): """ Test VM deployment in shared and isolated networks """ @@ -2697,6 +2708,7 @@ class TestSharedNetworks(cloudstackTestCase): except Exception as e: self.fail("SSH Access failed for %s: %s" % (self.isolated_network_admin_account_virtual_machine.ipaddress, e)) + @attr(tags=["advanced", "advancedns"]) def test_networkWithsubdomainaccessTrue(self): """ Test Shared Network with subdomainaccess=True """ @@ -2841,6 +2853,7 @@ class TestSharedNetworks(cloudstackTestCase): except: self.debug("Network creation failed because subdomainaccess parameter was passed when scope was account.") + @attr(tags=["advanced", "advancedns"]) def test_networkWithsubdomainaccessFalse(self): """ Test shared Network with subdomainaccess=False """ diff --git a/test/integration/component/test_storage_motion.py b/test/integration/component/test_storage_motion.py index cf110d34e61..0dcc7f8e8d1 100644 --- a/test/integration/component/test_storage_motion.py +++ b/test/integration/component/test_storage_motion.py @@ -178,8 +178,6 @@ class TestStorageMotion(cloudstackTestCase): # Migrate to a host that requires storage motion hosts[:] = [host for host in hosts if host.requiresStorageMotion] - self.assert_(hosts is not None, msg="No valid hosts for storage motion") - self.assert_(len(hosts)>0, msg="No valid hosts for storage motion. Skipping") if hosts is None or len(hosts) == 0: self.skipTest("No valid hosts for storage motion. Skipping") diff --git a/test/integration/component/test_tags.py b/test/integration/component/test_tags.py index 12a586313f1..514c2f7bc96 100644 --- a/test/integration/component/test_tags.py +++ b/test/integration/component/test_tags.py @@ -216,7 +216,7 @@ class TestResourceTags(cloudstackTestCase): cls.api_client, cls.services["virtual_machine"], accountid=cls.account.name, - domainid=cls.account.account.domainid, + domainid=cls.account.domainid, serviceofferingid=cls.service_offering.id, mode=cls.zone.networktype ) diff --git a/test/integration/component/test_templates.py b/test/integration/component/test_templates.py index 1a60123b820..65beabfc98f 100644 --- a/test/integration/component/test_templates.py +++ b/test/integration/component/test_templates.py @@ -93,7 +93,6 @@ class Services: "templatefilter": 'self', }, "templatefilter": 'self', - "destzoneid": 2, # For Copy template (Destination zone) "ostype": 'CentOS 5.3 (64-bit)', "sleep": 60, "timeout": 10, @@ -423,94 +422,6 @@ class TestTemplates(cloudstackTestCase): ) return - @attr(tags = ["advanced", "advancedns", "multizone"]) - def test_02_copy_template(self): - """Test for copy template from one zone to another""" - - # Validate the following - # 1. copy template should be successful and - # secondary storage should contain new copied template. - - self.debug( - "Copying template from zone: %s to %s" % ( - self.template.id, - self.services["destzoneid"] - )) - cmd = copyTemplate.copyTemplateCmd() - cmd.id = self.template.id - cmd.destzoneid = self.services["destzoneid"] - cmd.sourcezoneid = self.zone.id - self.apiclient.copyTemplate(cmd) - - # Verify template is copied to another zone using ListTemplates - list_template_response = list_templates( - self.apiclient, - templatefilter=\ - self.services["templatefilter"], - id=self.template.id, - zoneid=self.services["destzoneid"] - ) - self.assertEqual( - isinstance(list_template_response, list), - True, - "Check for list template response return valid list" - ) - - self.assertNotEqual( - len(list_template_response), - 0, - "Check template extracted in List Templates" - ) - - template_response = list_template_response[0] - self.assertEqual( - template_response.id, - self.template.id, - "Check ID of the downloaded template" - ) - self.assertEqual( - template_response.zoneid, - self.services["destzoneid"], - "Check zone ID of the copied template" - ) - - # Cleanup- Delete the copied template - timeout = self.services["timeout"] - while True: - time.sleep(self.services["sleep"]) - list_template_response = list_templates( - self.apiclient, - templatefilter=\ - self.services["templatefilter"], - id=self.template_2.id, - zoneid=self.services["destzoneid"] - ) - self.assertEqual( - isinstance(list_template_response, list), - True, - "Check list response returns a valid list" - ) - self.assertNotEqual( - len(list_template_response), - 0, - "Check template extracted in List Templates" - ) - - template_response = list_template_response[0] - if template_response.isready == True: - break - - if timeout == 0: - raise Exception( - "Failed to download copied template(ID: %s)" % template_response.id) - - timeout = timeout - 1 - cmd = deleteTemplate.deleteTemplateCmd() - cmd.id = self.template.id - cmd.zoneid = self.services["destzoneid"] - self.apiclient.deleteTemplate(cmd) - return - @attr(tags = ["advanced", "advancedns"]) def test_03_delete_template(self): """Test Delete template diff --git a/test/integration/component/test_vm_passwdenabled.py b/test/integration/component/test_vm_passwdenabled.py index e89253c407a..e22a1a0a75a 100644 --- a/test/integration/component/test_vm_passwdenabled.py +++ b/test/integration/component/test_vm_passwdenabled.py @@ -90,8 +90,8 @@ class TestVMPasswordEnabled(cloudstackTestCase): cls.services["ostype"] ) # Set Zones and disk offerings - cls.services["small"]["zoneid"] = zone.id - cls.services["small"]["template"] = template.id + cls.services["service_offerings"]["small"]["zoneid"] = zone.id + cls.services["service_offerings"]["small"]["template"] = template.id # Create VMs, NAT Rules etc cls.account = Account.create( diff --git a/test/integration/component/test_vpc_network_lbrules.py b/test/integration/component/test_vpc_network_lbrules.py index b4a66070d5b..66d6c4d4018 100644 --- a/test/integration/component/test_vpc_network_lbrules.py +++ b/test/integration/component/test_vpc_network_lbrules.py @@ -505,7 +505,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return nwacl_internet_1 @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_01_VPC_LBRulesListing(self): """ Test case no 210 and 227: List Load Balancing Rules belonging to a VPC """ @@ -551,7 +550,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_02_VPC_LBRulesAndVMListing(self): """ Test case no 211 and 228: List only VMs suitable for the Virtual Network on VPC for LB Rule """ @@ -595,7 +593,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_03_VPC_CreateLBRuleInMultipleNetworks(self): """ Test case no 212 : Create LB rules for 1 network which is part of a two/multiple virtual networks of a VPC using a new Public IP Address available with the VPC when the Virtual Router is in Running State @@ -623,7 +620,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_04_VPC_CreateLBRuleInMultipleNetworksVRStoppedState(self): """ Test case no 222 : Create LB rules for a two/multiple virtual networks of a VPC using a new Public IP Address available with the VPC when the Virtual Router is in Stopped State @@ -651,7 +647,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_05_VPC_CreateAndDeleteLBRule(self): """ Test case no 214 : Delete few(not all) LB rules for a single virtual network of a VPC belonging to a single Public IP Address when the Virtual Router is in Running State @@ -683,7 +678,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_06_VPC_CreateAndDeleteLBRuleVRStopppedState(self): """ Test case no 224 : Delete few(not all) LB rules for a single virtual network of a VPC belonging to a single Public IP Address when the Virtual Router is in Stopped State @@ -715,7 +709,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_07_VPC_CreateAndDeleteAllLBRule(self): """ Test case no 215 : Delete all LB rules for a single virtual network of a VPC belonging to a single Public IP Address when the Virtual Router is in Running State @@ -749,7 +742,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_08_VPC_CreateAndDeleteAllLBRuleVRStoppedState(self): """ Test case no 225 and 226 : Delete all LB rules for a single virtual network of a VPC belonging to a single Public IP Address when the Virtual Router is in Stopped State @@ -783,7 +775,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_09_VPC_LBRuleCreateFailMultipleVPC(self): """ Test case no 234 : User should not be allowed to create a LB rule for a VM that belongs to a different VPC. """ @@ -822,7 +813,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_10_VPC_FailedToCreateLBRuleNonVPCNetwork(self): """ Test case no 216 and 235: User should not be allowed to create a LB rule for a VM that does not belong to any VPC. """ @@ -860,7 +850,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_11_VPC_LBRuleCreateNotAllowed(self): """ Test case no 217 and 236: User should not be allowed to create a LB rule for a VM that does not belong to the same network but belongs to the same VPC. @@ -899,7 +888,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_12_VPC_LBRuleCreateFailForRouterIP(self): """ Test case no 218 and 237: User should not be allowed to create a LB rule on an Ipaddress that Source Nat enabled. """ @@ -928,7 +916,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_13_VPC_LBRuleCreateFailForPFSourceNATIP(self): """ Test case no 219 : User should not be allowed to create a LB rule on an Ipaddress that already has a PF rule. """ @@ -959,7 +946,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_14_VPC_LBRuleCreateFailForStaticNatRule(self): """ Test case no 220 : User should not be allowed to create a LB rule on an Ipaddress that already has a Static Nat rule. """ @@ -990,7 +976,6 @@ class TestVPCNetworkLBRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_15_VPC_RleaseIPForLBRuleCreated(self): """ Test case no 221 : Release Ip address that has a LB rule assigned to it. """ diff --git a/test/integration/component/test_vpc_network_pfrules.py b/test/integration/component/test_vpc_network_pfrules.py index 56792f49d00..c0c2b86426c 100644 --- a/test/integration/component/test_vpc_network_pfrules.py +++ b/test/integration/component/test_vpc_network_pfrules.py @@ -553,7 +553,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_03_network_services_VPC_StopCreateMultiplePF(self): """ Test case no 205 : Create PF rules for a two/multiple virtual networks of a VPC using a new Public IP Address available with the VPC when Virtual Router is in Stopped State @@ -587,7 +586,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_04_network_services_VPC_CreateMultiplePF(self): """ Test case no 191 : Create PF rules for a two/multiple virtual networks of a VPC using a new Public IP Address available with the VPC when Virtual Router is in Running State @@ -620,7 +618,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_05_network_services_VPC_StopDeletePF(self): """ Test case no 207 : Delete few(not all) PF rules for a single virtual network of a VPC belonging to a single Public IP Address when Virtual Router is in Stopped State @@ -654,7 +651,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_06_network_services_VPC_DeletePF(self): """ Test case no 193 : Delete few(not all) PF rules for a single virtual network of a VPC belonging to a single Public IP Address when Virtual Router is in Running State @@ -684,7 +680,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_07_network_services_VPC_StopDeleteAllPF(self): """ Test case no 208 : Delete all PF rules for a single virtual network of a VPC belonging to a single Public IP Address when Virtual Router is in Stopped State @@ -721,7 +716,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_08_network_services_VPC_DeleteAllPF(self): """ Test case no 194 : Delete all PF rules for a single virtual network of a VPC belonging to a single Public IP Address when Virtual Router is in Running State @@ -754,7 +748,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_09_network_services_VPC_StopDeleteAllMultiplePF(self): """ Test case no 209 : Delete all PF rules for two/multiple virtual networks of a VPC. Observe the status of the Public IP Addresses of the rules when Virtual Router is in Stopped State @@ -817,7 +810,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_10_network_services_VPC_DeleteAllMultiplePF(self): """ Test case no 195: Delete all PF rules for two/multiple virtual networks of a VPC. Observe the status of the Public IP Addresses of the rules when Virtual Router is in Running State diff --git a/test/integration/component/test_vpc_network_staticnatrule.py b/test/integration/component/test_vpc_network_staticnatrule.py index aceca62d1fb..bed1b5298b3 100644 --- a/test/integration/component/test_vpc_network_staticnatrule.py +++ b/test/integration/component/test_vpc_network_staticnatrule.py @@ -552,7 +552,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_03_VPC_StopCreateMultipleStaticNatRuleStopppedState(self): """ Test case no extra : Create Static Nat Rule rules for a two/multiple virtual networks of a VPC using a new Public IP Address available with the VPC when Virtual Router is in Stopped State @@ -586,7 +585,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_04_VPC_CreateMultipleStaticNatRule(self): """ Test case no 230 : Create Static NAT Rules for a two/multiple virtual networks of a VPC using a new Public IP Address available with the VPC when the Virtual Router is in Running State @@ -619,7 +617,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_05_network_services_VPC_DeleteAllPF(self): """ Test case no 232: Delete all Static NAT Rules for a single virtual network of a VPC belonging to a single Public IP Address when the Virtual Router is in Running State @@ -651,7 +648,6 @@ class TestVPCNetworkPFRules(cloudstackTestCase): return @attr(tags=["advanced", "intervlan"]) - @unittest.skip("Implemented but not executed: VPC with multiple network fails to set PF rule.") def test_06_network_services_VPC_DeleteAllMultiplePF(self): """ Test case no 233: Delete all Static NAT rules for two/multiple virtual networks of a VPC. Observe the status of the Public IP Addresses of the rules when the Virtual Router is in Running State. From 056e8fb533db72cc052dd67fbd0389f921b38471 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 25 May 2013 20:32:10 +0530 Subject: [PATCH 121/412] cli: Make cachemaker.py pep8 compliant Fixes object comparision to use a "is" b instead of == Signed-off-by: Rohit Yadav --- tools/cli/cloudmonkey/cachemaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/cloudmonkey/cachemaker.py b/tools/cli/cloudmonkey/cachemaker.py index a625b014d38..47749e5ae74 100644 --- a/tools/cli/cloudmonkey/cachemaker.py +++ b/tools/cli/cloudmonkey/cachemaker.py @@ -101,7 +101,7 @@ def monkeycache(apis): cache['asyncapis'] = [] apilist = getvalue(apis[responsekey], 'api') - if apilist == None: + if apilist is None: print "[monkeycache] Server response issue, no apis found" for api in apilist: From 51879dd9585f548c2c42b5a7bcc87e88c1caf0fa Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 25 May 2013 20:44:05 +0530 Subject: [PATCH 122/412] INSTALL: Update INSTALL.md with newer links - Fix Maven 3.0.5 link - Remove references of incubating, we are TLP now :) - Fix github mirror link and git clone url Signed-off-by: Rohit Yadav --- INSTALL.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 4f93900ddfe..b0e1a7617b0 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,5 +1,5 @@ -This document describes how to develop, build, package and install Apache CloudStack -(Incubating). For more information please refer to the project's website: +This document describes how to develop, build, package and install Apache +CloudStack. For more information please refer to: http://cloudstack.apache.org @@ -10,7 +10,7 @@ Refer to the [wiki](http://cwiki.apache.org/confluence/display/CLOUDSTACK/Index) for the latest information, especially: - [Setting up development environment](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Setting+up+CloudStack+Development+Environment) for Apache CloudStack. - - [Building](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Building) Apache CloudStack. + - [Building](https://cwiki.apache.org/confluence/display/CLOUDSTACK/How+to+build+on+master+branch) Apache CloudStack. ## Setting up Development Environment @@ -21,12 +21,12 @@ Install tools and dependencies used for development: $ yum install git ant ant-devel java-1.6.0-openjdk java-1.6.0-openjdk-devel mysql mysql-server tomcat6 mkisofs gcc python MySQL-python openssh-clients wget -Set up Maven (3.0.4): +Set up Maven (3.0.5): - $ wget http://www.us.apache.org/dist/maven/maven-3/3.0.4/binaries/apache-maven-3.0.4-bin.tar.gz + $ wget http://www.us.apache.org/dist/maven/maven-3/3.0.5/binaries/apache-maven-3.0.5-bin.tar.gz $ cd /usr/local/ # or any path - $ tar -zxvf apache-maven-3.0.4-bin.tar.gz - $ echo export M2_HOME=/usr/local/apache-maven-3.0.4 >> ~/.bashrc # or .zshrc or .profile + $ tar -zxvf apache-maven-3.0.5-bin.tar.gz + $ echo export M2_HOME=/usr/local/apache-maven-3.0.5 >> ~/.bashrc # or .zshrc or .profile $ echo export PATH=${M2_HOME}/bin:${PATH} >> ~/.bashrc # or .zshrc or .profile Note: Tomcat 6.0.35 has some known issue with Apache CloudStack, please use Tomcat @@ -77,10 +77,10 @@ Start the MySQL service: You may get the source code from the repository hosted on Apache: - $ git clone https://git-wip-us.apache.org/repos/asf/cloudstack.git + $ git clone git://git.apache.org/cloudstack.git Or, you may fork a repository from the official Apache CloudStack mirror by -Apache on [Github](https://github.com/apache/incubator-cloudstack) +Apache on [Github](https://github.com/apache/cloudstack) To keep yourself updated on a branch, do: @@ -92,7 +92,6 @@ For example, for master: ## Building - Clean and build: $ mvn clean install -P systemvm,developer From dc822a83d77830281402175b4a57b25b7e3b180a Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Sun, 26 May 2013 00:37:16 +0900 Subject: [PATCH 123/412] fix debian packaging *.in files must be processed by ReplaceTokens fliter. Signed-off-by: Hiroaki KAWAI --- debian/rules | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/rules b/debian/rules index 48485bb9d9b..e5ff5484fe5 100755 --- a/debian/rules +++ b/debian/rules @@ -69,8 +69,8 @@ install: install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-$(VERSION)-SNAPSHOT.jar $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D plugins/hypervisors/kvm/target/dependencies/* $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D packaging/debian/init/cloud-agent $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-agent - install -D agent/bindir/cloud-setup-agent.in $(DESTDIR)/usr/bin/cloudstack-setup-agent - install -D agent/bindir/cloud-ssh.in $(DESTDIR)/usr/bin/cloudstack-ssh + install -D agent/target/transformed/cloud-setup-agent $(DESTDIR)/usr/bin/cloudstack-setup-agent + install -D agent/target/transformed/cloud-ssh $(DESTDIR)/usr/bin/cloudstack-ssh install -D agent/target/transformed/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/agent # cloudstack-management @@ -102,7 +102,7 @@ install: ln -s tomcat6-nonssl.conf $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/management/tomcat6.conf ln -s server-nonssl.xml $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/management/server.xml install -D packaging/debian/init/cloud-management $(DESTDIR)/$(SYSCONFDIR)/init.d/$(PACKAGE)-management - install -D client/bindir/cloud-update-xenserver-licenses.in $(DESTDIR)/usr/bin/cloudstack-update-xenserver-licenses + install -D client/target/utilities/bin/cloud-update-xenserver-licenses $(DESTDIR)/usr/bin/cloudstack-update-xenserver-licenses ln -s /usr/share/tomcat6/bin $(DESTDIR)/usr/share/$(PACKAGE)-management/bin # Remove configuration in /ur/share/cloudstack-management/webapps/client/WEB-INF # This should all be in /etc/cloudstack/management From 5b902c700513423921da48161acd7f8cff8a0b56 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sun, 26 May 2013 14:46:27 +0530 Subject: [PATCH 124/412] CLOUDSTACK-2683: DevCloud systemVMs fail to launch DevCloud is a XCP Kronos based xen. For this we use the XcpOssResource whose memory limits were not set explicitly. Instead the base CitrixResourceBase would set the limits. static_min was set to 128MB failing the cpvm and ssvm start fails whose offerings have 100MB set to the max limits. Signed-off-by: Prasanna Santhanam --- .../xen/resource/XcpOssResource.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XcpOssResource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XcpOssResource.java index 357b4333678..870049c8111 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XcpOssResource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XcpOssResource.java @@ -17,17 +17,6 @@ package com.cloud.hypervisor.xen.resource; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.ejb.Local; - -import org.apache.log4j.Logger; -import org.apache.xmlrpc.XmlRpcException; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.NetworkUsageAnswer; @@ -46,14 +35,24 @@ import com.cloud.utils.script.Script; import com.cloud.vm.VirtualMachine; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Types; +import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VBD; import com.xensource.xenapi.VDI; import com.xensource.xenapi.VM; -import com.xensource.xenapi.Types.XenAPIException; +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; + +import javax.ejb.Local; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; @Local(value=ServerResource.class) public class XcpOssResource extends CitrixResourceBase { - private final static Logger s_logger = Logger.getLogger(XcpServerResource.class); + private final static Logger s_logger = Logger.getLogger(XcpOssResource.class); + private static final long mem_32m = 33554432L; + @Override protected List getPatchFiles() { List files = new ArrayList(); @@ -167,4 +166,9 @@ public class XcpOssResource extends CitrixResourceBase { } return answer; } + + @Override + protected void setMemory(Connection conn, VM vm, long minMemsize, long maxMemsize) throws XmlRpcException, XenAPIException { + vm.setMemoryLimits(conn, mem_32m, maxMemsize, minMemsize, maxMemsize); + } } From 322db71eedb6b52fca6f92b46b613607083c4d88 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Sun, 26 May 2013 11:38:00 +0200 Subject: [PATCH 125/412] agent: Only probe running VMs for the current Hypervisor Type Otherwise we try to probe for LXC VMs on a KVM hypervisor and vise versa. --- agent/conf/agent.properties | 5 ++++ .../resource/LibvirtComputingResource.java | 24 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index 7dc4ba8a18c..60030ae4f11 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -84,6 +84,11 @@ domr.scripts.dir=scripts/network/domr/kvm # set the hypervisor type, values are: kvm, lxc # hypervisor.type=kvm +# set the hypervisor URI. Usually there is no need for changing this +# For KVM: qemu:///system +# For LXC: lxc:/// +# hypervisor.uri=qemu:///system + # settings to enable direct networking in libvirt, should not be used # on hosts that run system vms, values for mode are: private, bridge, vepa # libvirt.vif.driver=com.cloud.hypervisor.kvm.resource.DirectVifDriver diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index c34d1eb1821..f979cfe00e4 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3943,18 +3943,22 @@ ServerResource { final HashMap vmStates = new HashMap(); Connect conn = null; - try { - conn = LibvirtConnection.getConnectionByType(HypervisorType.LXC.toString()); - vmStates.putAll(getAllVms(conn)); - } catch (LibvirtException e) { - s_logger.debug("Failed to get connection: " + e.getMessage()); + if (_hypervisorType == HypervisorType.LXC) { + try { + conn = LibvirtConnection.getConnectionByType(HypervisorType.LXC.toString()); + vmStates.putAll(getAllVms(conn)); + } catch (LibvirtException e) { + s_logger.debug("Failed to get connection: " + e.getMessage()); + } } - try { - conn = LibvirtConnection.getConnectionByType(HypervisorType.KVM.toString()); - vmStates.putAll(getAllVms(conn)); - } catch (LibvirtException e) { - s_logger.debug("Failed to get connection: " + e.getMessage()); + if (_hypervisorType == HypervisorType.KVM) { + try { + conn = LibvirtConnection.getConnectionByType(HypervisorType.KVM.toString()); + vmStates.putAll(getAllVms(conn)); + } catch (LibvirtException e) { + s_logger.debug("Failed to get connection: " + e.getMessage()); + } } return vmStates; From 1df3aad9abf4c64d8d64e569389144374a1d4f27 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sun, 26 May 2013 15:25:33 +0530 Subject: [PATCH 126/412] Use a dummy ISO and skip SSH check - Using the dummy.iso to speed up the test - Removing ssh that is not required. Signed-off-by: Prasanna Santhanam --- test/integration/component/test_stopped_vm.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/test/integration/component/test_stopped_vm.py b/test/integration/component/test_stopped_vm.py index f1096919824..0527b3d7ebf 100644 --- a/test/integration/component/test_stopped_vm.py +++ b/test/integration/component/test_stopped_vm.py @@ -78,7 +78,7 @@ class Services: { "displaytext": "Test ISO", "name": "testISO", - "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + "url": "http://people.apache.org/~tsp/dummy.iso", # Source URL where ISO is located "ostype": 'CentOS 5.3 (64-bit)', "mode": 'HTTP_DOWNLOAD', # Downloading existing ISO @@ -219,10 +219,6 @@ class TestDeployVM(cloudstackTestCase): "Running", "VM should be in Running state after deployment" ) - try: - ssh = self.virtual_machine.get_ssh_client() - except Exception as e: - self.fail("SSH to VM instance failed!") return @attr(tags = ["advanced", "eip", "advancedns", "basic", "sg"]) @@ -274,10 +270,6 @@ class TestDeployVM(cloudstackTestCase): "Running", "VM should be in Running state after deployment" ) - try: - ssh = self.virtual_machine.get_ssh_client() - except Exception as e: - self.fail("SSH to VM instance failed!") return @attr(tags = ["advanced", "eip", "advancedns", "basic", "sg"]) From f13a3c5d32de020e3da6456b2dbb79b9112b24b5 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sun, 26 May 2013 15:29:45 +0530 Subject: [PATCH 127/412] More tests using the dsl iso Replace dsl iso with dummy iso to speed up test Signed-off-by: Prasanna Santhanam --- test/integration/component/test_tags.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/component/test_tags.py b/test/integration/component/test_tags.py index 514c2f7bc96..992ca1daf58 100644 --- a/test/integration/component/test_tags.py +++ b/test/integration/component/test_tags.py @@ -102,9 +102,9 @@ class Services: }, "iso": { - "displaytext": "DSL ISO", - "name": "DSL ISO", - "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + "displaytext": "Dummy ISO", + "name": "Dummy ISO", + "url": "http://people.apache.org/~tsp/dummy.iso", # Source URL where ISO is located "isextractable": True, "isfeatured": True, From 2db7a4559e64e34b5c707157db240a1be322cb69 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Sun, 26 May 2013 11:55:53 +0200 Subject: [PATCH 128/412] debian: Only package specified configuration files for management Otherwise we will have duplicate / conflicting files with the AWSAPI package --- debian/cloudstack-management.install | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index 5a682d45862..2cc3e925970 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -15,7 +15,26 @@ # specific language governing permissions and limitations # under the License. -/etc/cloudstack/management/* +/etc/cloudstack/management/server-ssl.xml +/etc/cloudstack/management/catalina.policy +/etc/cloudstack/management/catalina.properties +/etc/cloudstack/management/cloudmanagementserver.keystore +/etc/cloudstack/management/logging.properties +/etc/cloudstack/management/commands.properties +/etc/cloudstack/management/ehcache.xml +/etc/cloudstack/management/tomcat6-nonssl.conf +/etc/cloudstack/management/componentContext.xml +/etc/cloudstack/management/applicationContext.xml +/etc/cloudstack/management/server-nonssl.xml +/etc/cloudstack/management/classpath.conf +/etc/cloudstack/management/db.properties +/etc/cloudstack/management/tomcat6-ssl.conf +/etc/cloudstack/management/web.xml +/etc/cloudstack/management/environment.properties +/etc/cloudstack/management/nonossComponentContext.xml +/etc/cloudstack/management/log4j-cloud.xml +/etc/cloudstack/management/tomcat-users.xml +/etc/cloudstack/management/context.xml /etc/init.d/cloudstack-management /etc/security/limits.d/cloudstack-limits.conf /etc/sudoers.d/cloudstack From a4a50a0b538ba2fcb71deed26a6d22d527ac457d Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sun, 26 May 2013 15:31:33 +0530 Subject: [PATCH 129/412] Remove unused setup script - All the setup is now done by the tests and there is no need for a setup script to seed test data. Signed-off-by: Prasanna Santhanam --- test/setup-test-data.sh | 102 ---------------------------------------- 1 file changed, 102 deletions(-) delete mode 100755 test/setup-test-data.sh diff --git a/test/setup-test-data.sh b/test/setup-test-data.sh deleted file mode 100755 index 844c275da7c..00000000000 --- a/test/setup-test-data.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with 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. - -usage() { - printf "Usage: %s:\n - [-t path to tests ] \n - [-m mgmt-server ] \n - [-h hypervisor (xen|kvm) ] \n - [-p hypervisor root password ] \n - [-d db node url ]\n" $(basename $0) >&2 -} - -failed() { - exit $1 -} - -#defaults -TESTDIR="/root/cloudstack/test/" -MGMT_SVR="localhost" -DB_SVR="localhost" -HV_PASSWD="password" - -while getopts 't:d:m:p:h:' OPTION -do - case $OPTION in - d) dflag=1 - DB_SVR="$OPTARG" - ;; - t) tflag=1 - TESTDIR="$OPTARG" - ;; - m) mflag=1 - MGMT_SVR="$OPTARG" - ;; - h) hflag=1 - HV="$OPTARG" - ;; - p) pflag=1 - HV_PASSWD="$OPTARG" - ;; - ?) usage - failed 2 - ;; - esac -done - -#Damn Small Linux ISO type -if [[ $HV == "kvm" ]]; then - ostypeid=$(mysql -ucloud -Dcloud -pcloud -h$DB_SVR -s -N -r -e"select uuid from guest_os where display_name='CentOS 5.5 (64-bit)'") -else - ostypeid=$(mysql -ucloud -Dcloud -pcloud -h$DB_SVR -s -N -r -e"select uuid from guest_os where display_name='CentOS 5.3 (64-bit)'") -fi -if [[ $ostypeid == "" ]]; then - echo "Unable to contact DB server @ $DB_SVR" - exit 2 -fi - -nc -z $MGMT_SVR 8096 -if [[ $? -ne 0 ]]; then - echo "$MGMT_SVR doesn't have port 8096 open" - exit 2 -fi - -if [[ ! -d $TESTDIR ]]; then - echo "No directory $TESTDIR found" - exit 2 -fi -for file in `find $TESTDIR -name *.py -type f` -do - old_ostypeid=$(grep ostypeid $file | head -1 | cut -d: -f2 | tr -d " ,'") - if [[ $old_ostypeid != "" ]] - then - echo "replacing:" $old_ostypeid, "with:" $ostypeid,"in " $file - sed -i "s/$old_ostypeid/$ostypeid/g" $file - #sed -i "s/http:\/\/iso.linuxquestions.org\/download\/504\/1819\/http\/gd4.tuwien.ac.at\/dsl-4.4.10.iso/http:\/\/nfs1.lab.vmops.com\/isos_32bit\/dsl-4.4.10.iso/g" $file - sed -i "s/fr3sca/$HV_PASSWD/g" $file - fi -done - -#Python version check -version_tuple=$(python -c 'import sys; print(sys.version_info[:2])') -if [[ $version_tuple == "(2, 7)" ]] -then - echo "Done" -else - echo "WARN: Python version 2.7 not detected on system." -fi From 61e150fd58547e5a02522c7e40e1091b59fea10c Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Sun, 26 May 2013 20:10:46 +0530 Subject: [PATCH 130/412] More non-ascii encoded characters in commented text Signed-off-by: Prasanna Santhanam --- .../component/test_host_high_availability.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/integration/component/test_host_high_availability.py b/test/integration/component/test_host_high_availability.py index 7a1ad69c7f5..ec297d43427 100644 --- a/test/integration/component/test_host_high_availability.py +++ b/test/integration/component/test_host_high_availability.py @@ -159,10 +159,10 @@ class TestHostHighAvailability(cloudstackTestCase): #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. # Validations, - #1. Ensure that the offering is created and that in the UI the “Offer HA” field is enabled (Yes) - #The listServiceOffering API should list “offerha” as true. + #1. Ensure that the offering is created and that in the UI the 'Offer HA' field is enabled (Yes) + #The listServiceOffering API should list 'offerha' as true. #2. Select the newly created VM and ensure that the Compute offering field value lists the compute service offering that was selected. - # Also, check that the HA Enabled field is enabled “Yes”. + # Also, check that the HA Enabled field is enabled 'Yes'. #list and validate above created service offering with Ha enabled list_service_response = list_service_offering( @@ -346,12 +346,12 @@ class TestHostHighAvailability(cloudstackTestCase): """ Verify you can not migrate VMs to hosts with an ha.tag (positive) """ # Steps, - #1. Create a Compute service offering with the “Offer HA” option selected. + #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. - #3. Select the VM and migrate VM to another host. Choose a “Suitable” host (i.e. host2) + #3. Select the VM and migrate VM to another host. Choose a 'Suitable' host (i.e. host2) # Validations - #The option from the “Migrate instance to another host” dialog box” should list host3 as “Not Suitable” for migration. - #Confirm that the VM is migrated to the “Suitable” host you selected (i.e. host2) + #The option from the 'Migrate instance to another host' dialog box' should list host3 as 'Not Suitable' for migration. + #Confirm that the VM is migrated to the 'Suitable' host you selected (i.e. host2) #create and verify the virtual machine with HA enabled service offering virtual_machine_with_ha = VirtualMachine.create( @@ -452,11 +452,11 @@ class TestHostHighAvailability(cloudstackTestCase): """ Verify you can not migrate VMs to hosts with an ha.tag (negative) """ # Steps, - #1. Create a Compute service offering with the “Offer HA” option selected. + #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. - #3. Select the VM and migrate VM to another host. Choose a “Not Suitable” host. + #3. Select the VM and migrate VM to another host. Choose a 'Not Suitable' host. # Validations, - #The option from the “Migrate instance to another host” dialog box” should list host3 as “Not Suitable” for migration. + #The option from the 'Migrate instance to another host' dialog box should list host3 as 'Not Suitable' for migration. #By design, The Guest VM can STILL can be migrated to host3 if the admin chooses to do so. #create and verify virtual machine with HA enabled service offering @@ -558,11 +558,11 @@ class TestHostHighAvailability(cloudstackTestCase): """ Verify that none of the VMs with HA enabled migrate to an ha tagged host during live migration """ # Steps, - #1. Fresh install CS (Bonita) that supports this feature + #1. Fresh install CS that supports this feature #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage #3. When adding host3, assign the HA host tag. #4. Create VMs with and without the Compute Service Offering with the HA tag. - #5. Note the VMs on host1 and whether any of the VMs have their “HA enabled” flags enabled. + #5. Note the VMs on host1 and whether any of the VMs have their 'HA enabled' flags enabled. #6. Put host1 into maintenance mode. # Validations, #1. Make sure the VMs are created on either host1 or host2 and not on host3 @@ -690,11 +690,11 @@ class TestHostHighAvailability(cloudstackTestCase): """ Verify that none of the VMs without HA enabled migrate to an ha tagged host during live migration """ # Steps, - #1. Fresh install CS (Bonita) that supports this feature + #1. Fresh install CS that supports this feature #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage #3. When adding host3, assign the HA host tag. #4. Create VMs with and without the Compute Service Offering with the HA tag. - #5. Note the VMs on host1 and whether any of the VMs have their “HA enabled” flags enabled. + #5. Note the VMs on host1 and whether any of the VMs have their 'HA enabled' flags enabled. #6. Put host1 into maintenance mode. # Validations, #1. Make sure the VMs are created on either host1 or host2 and not on host3 From 16e9eaca80bb4c7fe2baa2ce05fc03a9032277cd Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Mon, 27 May 2013 10:10:01 +0530 Subject: [PATCH 131/412] unskipping skipped tests and indentation fixes Signed-off-by: Prasanna Santhanam --- .../component/test_host_high_availability.py | 677 +++++++++--------- .../component/test_vpc_host_maintenance.py | 2 - 2 files changed, 337 insertions(+), 342 deletions(-) diff --git a/test/integration/component/test_host_high_availability.py b/test/integration/component/test_host_high_availability.py index ec297d43427..57eb5edede9 100644 --- a/test/integration/component/test_host_high_availability.py +++ b/test/integration/component/test_host_high_availability.py @@ -18,15 +18,12 @@ """ P1 tests for dedicated Host high availability """ #Import Local Modules -import marvin from nose.plugins.attrib import attr from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin import remoteSSHClient -import datetime class Services: @@ -34,90 +31,90 @@ class Services: def __init__(self): self.services = { - "account": { - "email": "test@test.com", - "firstname": "HA", - "lastname": "HA", - "username": "HA", - # Random characters are appended for unique - # username - "password": "password", - }, - "service_offering_with_ha": { - "name": "Tiny Instance With HA Enabled", - "displaytext": "Tiny Instance", - "cpunumber": 1, - "cpuspeed": 100, # in MHz - "memory": 128, # In MBs - }, - "service_offering_without_ha": { - "name": "Tiny Instance Without HA", - "displaytext": "Tiny Instance", - "cpunumber": 1, - "cpuspeed": 100, # in MHz - "memory": 128, # In MBs - }, - "virtual_machine": { - "displayname": "VM", - "username": "root", - "password": "password", - "ssh_port": 22, - "hypervisor": 'XenServer', - # Hypervisor type should be same as - # hypervisor type of cluster - "privateport": 22, - "publicport": 22, - "protocol": 'TCP', - }, - "ostype": 'CentOS 5.3 (64-bit)', - "timeout": 100, - } + "account": { + "email": "test@test.com", + "firstname": "HA", + "lastname": "HA", + "username": "HA", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering_with_ha": { + "name": "Tiny Instance With HA Enabled", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "service_offering_without_ha": { + "name": "Tiny Instance Without HA", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + "virtual_machine": { + "displayname": "VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostype": 'CentOS 5.3 (64-bit)', + "timeout": 100, + } + class TestHostHighAvailability(cloudstackTestCase): """ Dedicated host HA test cases """ - + @classmethod def setUpClass(cls): - cls.api_client = super( - TestHostHighAvailability, - cls - ).getClsTestClient().getApiClient() + TestHostHighAvailability, + cls + ).getClsTestClient().getApiClient() cls.services = Services().services # Get Zone, Domain and templates cls.domain = get_domain( - cls.api_client, - cls.services - ) + cls.api_client, + cls.services + ) cls.zone = get_zone( - cls.api_client, - cls.services - ) + cls.api_client, + cls.services + ) cls.template = get_template( - cls.api_client, - cls.zone.id, - cls.services["ostype"] - ) + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) cls.services["virtual_machine"]["zoneid"] = cls.zone.id cls.services["virtual_machine"]["template"] = cls.template.id cls.service_offering_with_ha = ServiceOffering.create( - cls.api_client, - cls.services["service_offering_with_ha"], - offerha=True - ) - + cls.api_client, + cls.services["service_offering_with_ha"], + offerha=True + ) + cls.service_offering_without_ha = ServiceOffering.create( - cls.api_client, - cls.services["service_offering_without_ha"], - offerha=False - ) - + cls.api_client, + cls.services["service_offering_without_ha"], + offerha=False + ) + cls._cleanup = [ - cls.service_offering_with_ha, - cls.service_offering_without_ha, - ] + cls.service_offering_with_ha, + cls.service_offering_without_ha, + ] return @classmethod @@ -133,11 +130,11 @@ class TestHostHighAvailability(cloudstackTestCase): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() self.account = Account.create( - self.apiclient, - self.services["account"], - admin=True, - domainid=self.domain.id - ) + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) self.cleanup = [self.account] return @@ -150,11 +147,11 @@ class TestHostHighAvailability(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @attr(configuration = "ha.tag") - @attr(tags = ["advanced", "advancedns", "sg", "basic", "eip", "simulator"]) + @attr(configuration="ha.tag") + @attr(tags=["advanced", "advancedns", "sg", "basic", "eip", "simulator"]) def test_01_vm_deployment_with_compute_offering_with_ha_enabled(self): """ Test VM deployments (Create HA enabled Compute Service Offering and VM) """ - + # Steps, #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. @@ -163,63 +160,63 @@ class TestHostHighAvailability(cloudstackTestCase): #The listServiceOffering API should list 'offerha' as true. #2. Select the newly created VM and ensure that the Compute offering field value lists the compute service offering that was selected. # Also, check that the HA Enabled field is enabled 'Yes'. - + #list and validate above created service offering with Ha enabled list_service_response = list_service_offering( - self.apiclient, - id=self.service_offering_with_ha.id - ) + self.apiclient, + id=self.service_offering_with_ha.id + ) self.assertEqual( isinstance(list_service_response, list), True, "listServiceOfferings returned invalid object in response." - ) + ) self.assertNotEqual( len(list_service_response), 0, "listServiceOfferings returned empty list." - ) + ) self.assertEqual( list_service_response[0].offerha, True, "The service offering is not HA enabled" - ) - + ) + #create virtual machine with the service offering with Ha enabled virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_with_ha.id - ) + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, - listall=True - ) + self.apiclient, + id=virtual_machine.id, + listall=True + ) self.assertEqual( isinstance(vms, list), True, "listVirtualMachines returned invalid object in response." - ) + ) self.assertNotEqual( len(vms), 0, "listVirtualMachines returned empty list." - ) + ) self.debug("Deployed VM on host: %s" % vms[0].hostid) self.assertEqual( vms[0].haenable, True, "VM not created with HA enable tag" - ) + ) - @attr(configuration = "ha.tag") - @attr(tags = ["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) + @attr(configuration="ha.tag") + @attr(tags=["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) def test_02_no_vm_creation_on_host_with_haenabled(self): """ Verify you can not create new VMs on hosts with an ha.tag """ - + # Steps, #1. Fresh install CS (Bonita) that supports this feature #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage @@ -229,122 +226,122 @@ class TestHostHighAvailability(cloudstackTestCase): # Validations, #Check to make sure the newly created VM is not on any HA enabled hosts #The VM should be created only on host1 or host2 and never host3 (HA enabled) - + #create and verify virtual machine with HA enabled service offering virtual_machine_with_ha = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_with_ha.id - ) - + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine_with_ha.id, - listall=True - ) - + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True + ) + self.assertEqual( isinstance(vms, list), True, "listVirtualMachines returned invalid object in response." - ) - + ) + self.assertNotEqual( len(vms), 0, "listVirtualMachines returned empty list." - ) - + ) + vm = vms[0] - + self.debug("Deployed VM on host: %s" % vm.hostid) - + #validate the virtual machine created is host Ha enabled list_hosts_response = list_hosts( - self.apiclient, - id=vm.hostid - ) + self.apiclient, + id=vm.hostid + ) self.assertEqual( isinstance(list_hosts_response, list), True, "listHosts returned invalid object in response." - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "listHosts retuned empty list in response." - ) - + ) + self.assertEqual( list_hosts_response[0].hahost, False, "VM created on HA enabled host." - ) - + ) + #create and verify virtual machine with Ha disabled service offering virtual_machine_without_ha = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_without_ha.id - ) - + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_without_ha.id + ) + vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine_without_ha.id, - listall=True - ) - + self.apiclient, + id=virtual_machine_without_ha.id, + listall=True + ) + self.assertEqual( isinstance(vms, list), True, "listVirtualMachines returned invalid object in response." - ) - + ) + self.assertNotEqual( len(vms), 0, "listVirtualMachines returned empty list." - ) - + ) + vm = vms[0] - + self.debug("Deployed VM on host: %s" % vm.hostid) - + #verify that the virtual machine created on the host is Ha disabled list_hosts_response = list_hosts( - self.apiclient, - id=vm.hostid - ) + self.apiclient, + id=vm.hostid + ) self.assertEqual( isinstance(list_hosts_response, list), True, "listHosts returned invalid object in response." - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "listHosts returned empty list." - ) - + ) + host = list_hosts_response[0] - + self.assertEqual( host.hahost, False, "VM migrated to HA enabled host." - ) + ) - @attr(configuration = "ha.tag") - @attr(tags = ["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) + @attr(configuration="ha.tag") + @attr(tags=["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) def test_03_cant_migrate_vm_to_host_with_ha_positive(self): """ Verify you can not migrate VMs to hosts with an ha.tag (positive) """ - + # Steps, #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. @@ -352,61 +349,61 @@ class TestHostHighAvailability(cloudstackTestCase): # Validations #The option from the 'Migrate instance to another host' dialog box' should list host3 as 'Not Suitable' for migration. #Confirm that the VM is migrated to the 'Suitable' host you selected (i.e. host2) - + #create and verify the virtual machine with HA enabled service offering virtual_machine_with_ha = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_with_ha.id - ) - + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine_with_ha.id, - listall=True, - ) - + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True, + ) + self.assertEqual( isinstance(vms, list), True, "List VMs should return valid response for deployed VM" - ) - + ) + self.assertNotEqual( len(vms), 0, "List VMs should return valid response for deployed VM" - ) - + ) + vm = vms[0] - + self.debug("Deployed VM on host: %s" % vm.hostid) - + #Find out a Suitable host for VM migration list_hosts_response = list_hosts( - self.apiclient, - ) + self.apiclient, + ) self.assertEqual( isinstance(list_hosts_response, list), True, "The listHosts API returned the invalid list" - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "The listHosts returned nothing." - ) + ) suitableHost = None for host in list_hosts_response: if host.suitableformigration == True and host.hostid != vm.hostid: suitableHost = host break - + self.assertTrue(suitableHost is not None, "suitablehost should not be None") - + #Migration of the VM to a suitable host self.debug("Migrating VM-ID: %s to Host: %s" % (self.vm.id, suitableHost.id)) @@ -417,20 +414,20 @@ class TestHostHighAvailability(cloudstackTestCase): #Verify that the VM migrated to a targeted Suitable host list_vm_response = list_virtual_machines( - self.apiclient, - id=vm.id - ) + self.apiclient, + id=vm.id + ) self.assertEqual( isinstance(list_vm_response, list), True, "The listVirtualMachines returned the invalid list." - ) + ) self.assertNotEqual( list_vm_response, None, "The listVirtualMachines API returned nothing." - ) + ) vm_response = list_vm_response[0] @@ -438,19 +435,19 @@ class TestHostHighAvailability(cloudstackTestCase): vm_response.id, vm.id, "The virtual machine id and the the virtual machine from listVirtualMachines is not matching." - ) + ) self.assertEqual( vm_response.hostid, suitableHost.id, "The VM is not migrated to targeted suitable host." - ) - - @attr(configuration = "ha.tag") - @attr(tags = ["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) + ) + + @attr(configuration="ha.tag") + @attr(tags=["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) def test_04_cant_migrate_vm_to_host_with_ha_negative(self): """ Verify you can not migrate VMs to hosts with an ha.tag (negative) """ - + # Steps, #1. Create a Compute service offering with the 'Offer HA' option selected. #2. Create a Guest VM with the compute service offering created above. @@ -458,62 +455,62 @@ class TestHostHighAvailability(cloudstackTestCase): # Validations, #The option from the 'Migrate instance to another host' dialog box should list host3 as 'Not Suitable' for migration. #By design, The Guest VM can STILL can be migrated to host3 if the admin chooses to do so. - + #create and verify virtual machine with HA enabled service offering virtual_machine_with_ha = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_with_ha.id - ) - + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine_with_ha.id, - listall=True - ) - + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True + ) + self.assertEqual( isinstance(vms, list), True, "The listVirtualMachines returned invalid object in response." - ) - + ) + self.assertNotEqual( len(vms), 0, "The listVirtualMachines returned empty response." - ) - + ) + vm = vms[0] - + self.debug("Deployed VM on host: %s" % vm.hostid) - + #Find out Non-Suitable host for VM migration list_hosts_response = list_hosts( - self.apiclient, - ) + self.apiclient, + ) self.assertEqual( isinstance(list_hosts_response, list), True, "listHosts returned invalid object in response." - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "listHosts returned empty response." - ) - - notSuitableHost = None + ) + + notSuitableHost = None for host in list_hosts_response: if not host.suitableformigration and host.hostid != vm.hostid: notSuitableHost = host - break - + break + self.assertTrue(notSuitableHost is not None, "notsuitablehost should not be None") - + #Migrate VM to Non-Suitable host self.debug("Migrating VM-ID: %s to Host: %s" % (vm.id, notSuitableHost.id)) @@ -524,39 +521,39 @@ class TestHostHighAvailability(cloudstackTestCase): #Verify that the virtual machine got migrated to targeted Non-Suitable host list_vm_response = list_virtual_machines( - self.apiclient, - id=vm.id - ) + self.apiclient, + id=vm.id + ) self.assertEqual( isinstance(list_vm_response, list), True, "listVirtualMachine returned invalid object in response." - ) + ) self.assertNotEqual( len(list_vm_response), 0, "listVirtualMachines returned empty response." - ) + ) self.assertEqual( list_vm_response[0].id, vm.id, "Virtual machine id with the virtual machine from listVirtualMachine is not matching." - ) + ) self.assertEqual( list_vm_response[0].hostid, notSuitableHost.id, "The detination host id of migrated VM is not matching." - ) + ) - @attr(configuration = "ha.tag") - @attr(speed = "slow") - @attr(tags = ["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) + @attr(configuration="ha.tag") + @attr(speed="slow") + @attr(tags=["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) def test_05_no_vm_with_ha_gets_migrated_to_ha_host_in_live_migration(self): """ Verify that none of the VMs with HA enabled migrate to an ha tagged host during live migration """ - + # Steps, #1. Fresh install CS that supports this feature #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage @@ -567,128 +564,128 @@ class TestHostHighAvailability(cloudstackTestCase): # Validations, #1. Make sure the VMs are created on either host1 or host2 and not on host3 #2. Putting host1 into maintenance mode should trigger a live migration. Make sure the VMs are not migrated to HA enabled host3. - + # create and verify virtual machine with HA disabled service offering virtual_machine_with_ha = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_with_ha.id - ) - + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine_with_ha.id, - listall=True - ) - + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True + ) + self.assertEqual( isinstance(vms, list), True, "List VMs should return valid response for deployed VM" - ) - + ) + self.assertNotEqual( len(vms), 0, "List VMs should return valid response for deployed VM" - ) - + ) + vm_with_ha_enabled = vms[0] - + #Verify the virtual machine got created on non HA host list_hosts_response = list_hosts( - self.apiclient, - id=vm_with_ha_enabled.hostid - ) + self.apiclient, + id=vm_with_ha_enabled.hostid + ) self.assertEqual( isinstance(list_hosts_response, list), True, "Check list response returns a valid list" - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "Check Host is available" - ) - + ) + self.assertEqual( list_hosts_response[0].hahost, False, "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" - ) - + ) + #put the Host in maintainance mode self.debug("Enabling maintenance mode for host %s" % vm_with_ha_enabled.hostid) cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = vm_with_ha_enabled.hostid self.apiclient.prepareHostForMaintenance(cmd) - + timeout = self.services["timeout"] - + #verify the VM live migration happened to another running host self.debug("Waiting for VM to come up") wait_for_vm( self.apiclient, virtualmachineid=vm_with_ha_enabled.id, interval=timeout - ) - + ) + vms = VirtualMachine.list( - self.apiclient, - id=vm_with_ha_enabled.id, - listall=True, - ) - + self.apiclient, + id=vm_with_ha_enabled.id, + listall=True, + ) + self.assertEqual( isinstance(vms, list), True, "List VMs should return valid response for deployed VM" - ) - + ) + self.assertNotEqual( len(vms), 0, "List VMs should return valid response for deployed VM" - ) - + ) + vm_with_ha_enabled1 = vms[0] - + list_hosts_response = list_hosts( - self.apiclient, - id=vm_with_ha_enabled1.hostid - ) + self.apiclient, + id=vm_with_ha_enabled1.hostid + ) self.assertEqual( isinstance(list_hosts_response, list), True, "Check list response returns a valid list" - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "Check Host is available" - ) - + ) + self.assertEqual( list_hosts_response[0].hahost, False, "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" - ) - + ) + self.debug("Disabling the maintenance mode for host %s" % vm_with_ha_enabled.hostid) cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() cmd.id = vm_with_ha_enabled.hostid self.apiclient.cancelHostMaintenance(cmd) - - @attr(configuration = "ha.tag") - @attr(speed = "slow") - @attr(tags = ["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) + + @attr(configuration="ha.tag") + @attr(speed="slow") + @attr(tags=["advanced", "advancedns", "sg", "basic", "eip", "simulator", "multihost"]) def test_06_no_vm_without_ha_gets_migrated_to_ha_host_in_live_migration(self): """ Verify that none of the VMs without HA enabled migrate to an ha tagged host during live migration """ - + # Steps, #1. Fresh install CS that supports this feature #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage @@ -699,115 +696,115 @@ class TestHostHighAvailability(cloudstackTestCase): # Validations, #1. Make sure the VMs are created on either host1 or host2 and not on host3 #2. Putting host1 into maintenance mode should trigger a live migration. Make sure the VMs are not migrated to HA enabled host3. - + # create and verify virtual machine with HA disabled service offering virtual_machine_without_ha = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering_without_ha.id - ) - + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_without_ha.id + ) + vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine_without_ha.id, - listall=True - ) - + self.apiclient, + id=virtual_machine_without_ha.id, + listall=True + ) + self.assertEqual( isinstance(vms, list), True, "List VMs should return valid response for deployed VM" - ) - + ) + self.assertNotEqual( len(vms), 0, "List VMs should return valid response for deployed VM" - ) - + ) + vm_with_ha_disabled = vms[0] - + #Verify the virtual machine got created on non HA host list_hosts_response = list_hosts( - self.apiclient, - id=vm_with_ha_disabled.hostid - ) + self.apiclient, + id=vm_with_ha_disabled.hostid + ) self.assertEqual( isinstance(list_hosts_response, list), True, "Check list response returns a valid list" - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "Check Host is available" - ) - + ) + self.assertEqual( list_hosts_response[0].hahost, False, "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" - ) - + ) + #put the Host in maintainance mode self.debug("Enabling maintenance mode for host %s" % vm_with_ha_disabled.hostid) cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() cmd.id = vm_with_ha_disabled.hostid self.apiclient.prepareHostForMaintenance(cmd) - + timeout = self.services["timeout"] - + #verify the VM live migration happened to another running host self.debug("Waiting for VM to come up") wait_for_vm( self.apiclient, virtualmachineid=vm_with_ha_disabled.id, interval=timeout - ) - + ) + vms = VirtualMachine.list( - self.apiclient, - id=vm_with_ha_disabled.id, - listall=True - ) - + self.apiclient, + id=vm_with_ha_disabled.id, + listall=True + ) + self.assertEqual( isinstance(vms, list), True, "List VMs should return valid response for deployed VM" - ) - + ) + self.assertNotEqual( len(vms), 0, "List VMs should return valid response for deployed VM" - ) - + ) + list_hosts_response = list_hosts( - self.apiclient, - id=vms[0].hostid - ) + self.apiclient, + id=vms[0].hostid + ) self.assertEqual( isinstance(list_hosts_response, list), True, "Check list response returns a valid list" - ) - + ) + self.assertNotEqual( len(list_hosts_response), 0, "Check Host is available" - ) - + ) + self.assertEqual( list_hosts_response[0].hahost, False, "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" - ) - + ) + self.debug("Disabling the maintenance mode for host %s" % vm_with_ha_disabled.hostid) cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() cmd.id = vm_with_ha_disabled.hostid diff --git a/test/integration/component/test_vpc_host_maintenance.py b/test/integration/component/test_vpc_host_maintenance.py index 1cce2764fe8..d28b7985b9b 100644 --- a/test/integration/component/test_vpc_host_maintenance.py +++ b/test/integration/component/test_vpc_host_maintenance.py @@ -186,7 +186,6 @@ class Services: } -@unittest.skip("No suitable setup available for testing") class TestVMLifeCycleHostmaintenance(cloudstackTestCase): @classmethod @@ -562,7 +561,6 @@ class TestVMLifeCycleHostmaintenance(cloudstackTestCase): return -@unittest.skip("No suitable setup available for testing") class TestVPCNetworkRules(cloudstackTestCase): @classmethod From f854f5df87ee8c16ac231d2ae53dd7063ed970d5 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Mon, 27 May 2013 14:50:55 +0900 Subject: [PATCH 132/412] fix debian packaging nfs-common is required for a basic NFS installation both in management server and agent. Signed-off-by: Hiroaki KAWAI --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index eec9ca25c7b..46dd50536b0 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Homepage: http://www.cloudstack.org/ Package: cloudstack-common Architecture: all -Depends: bash, genisoimage +Depends: bash, genisoimage, nfs-common Conflicts: cloud-scripts, cloud-utils, cloud-system-iso, cloud-console-proxy, cloud-daemonize, cloud-deps, cloud-python, cloud-setup Description: A common package which contains files which are shared by several CloudStack packages From 77c52f5ebb3c7d8c61be4a8ac0142a21d47e0535 Mon Sep 17 00:00:00 2001 From: radhikap Date: Mon, 27 May 2013 11:47:48 +0530 Subject: [PATCH 133/412] remove the 256 limit (multiple ip per nic- now unlimited) --- docs/en-US/multiple-ip-nic.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en-US/multiple-ip-nic.xml b/docs/en-US/multiple-ip-nic.xml index 561ba0757b5..926235c3518 100644 --- a/docs/en-US/multiple-ip-nic.xml +++ b/docs/en-US/multiple-ip-nic.xml @@ -24,8 +24,7 @@ &PRODUCT; now provides you the ability to associate multiple private IP addresses per guest VM NIC. This feature is supported on all the network configurations—Basic, Advanced, and VPC. Security Groups, Static NAT and Port forwarding services are supported on these additional - IPs. In addition to the primary IP, you can assign additional IPs to the guest VM NIC. Up to 256 - IP addresses are allowed per NIC. + IPs. In addition to the primary IP, you can assign additional IPs to the guest VM NIC. As always, you can specify an IP from the guest subnet; if not specified, an IP is automatically picked up from the guest VM subnet. You can view the IPs associated with for each guest VM NICs on the UI. You can apply NAT on these additional guest IPs by using firewall From 8744d1cdf62a8642c2f1903162634e7093879cba Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Mon, 27 May 2013 15:52:23 +0900 Subject: [PATCH 134/412] CLOUDSTACK-2406: fix UI strings Japanese properties file was broken, fixed manually. We need to let the servlet container the encoding to use. Signed-off-by: Hiroaki KAWAI --- .../classes/resources/messages_ja.properties | 108 +++++++++--------- ui/index.jsp | 2 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/client/WEB-INF/classes/resources/messages_ja.properties b/client/WEB-INF/classes/resources/messages_ja.properties index e483a97804b..a6075958d6a 100644 --- a/client/WEB-INF/classes/resources/messages_ja.properties +++ b/client/WEB-INF/classes/resources/messages_ja.properties @@ -23,7 +23,7 @@ error.could.not.enable.zone=\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3067\u30 error.installWizard.message=\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u623b\u3063\u3066\u30a8\u30e9\u30fc\u3092\u4fee\u6b63\u3067\u304d\u307e\u3059\u3002 error.invalid.username.password=\u7121\u52b9\u306a\u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9 error.login=\u30e6\u30fc\u30b6\u30fc\u540d/\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u8a18\u9332\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002 -error.menu.select=\u00e3\u0082\u00a2\u00e3\u0082\u00a4\u00e3\u0083\u0086\u00e3\u0083\u00a0\u00e3\u0081\u008c\u00e9\u0081\u00b8\u00e6\u008a\u009e\u00e3\u0081\u0095\u00e3\u0082\u008c\u00e3\u0081\u00a6\u00e3\u0081\u0084\u00e3\u0081\u00aa\u00e3\u0081\u0084\u00e3\u0081\u009f\u00e3\u0082\u0081\u00e3\u0082\u00a2\u00e3\u0082\u00af\u00e3\u0082\u00b7\u00e3\u0083\u00a7\u00e3\u0083\u00b3\u00e3\u0082\u0092\u00e5\u00ae\u009f\u00e8\u00a1\u008c\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u0093\u00e3\u0081\u00a8\u00e3\u0081\u008c\u00e3\u0081\u00a7\u00e3\u0081\u008d\u00e3\u0081\u00be\u00e3\u0081\u009b\u00e3\u0082\u0093 +error.menu.select=\u9805\u76ee\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u305f\u3081\u64cd\u4f5c\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3002 error.mgmt.server.inaccessible=\u7ba1\u7406\u30b5\u30fc\u30d0\u30fc\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093\u3002\u5f8c\u3067\u518d\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 error.password.not.match=\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u4e00\u81f4\u3057\u307e\u305b\u3093 error.please.specify.physical.network.tags=\u3053\u306e\u7269\u7406\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u30bf\u30b0\u3092\u6307\u5b9a\u3057\u306a\u3051\u308c\u3070\u3001\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002 @@ -32,11 +32,11 @@ error.something.went.wrong.please.correct.the.following=\u554f\u984c\u304c\u767a error.unable.to.reach.management.server=\u7ba1\u7406\u30b5\u30fc\u30d0\u30fc\u3068\u901a\u4fe1\u3067\u304d\u307e\u305b\u3093 error.unresolved.internet.name=\u3042\u306a\u305f\u306e\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u540d\u306f\u89e3\u6c7a\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002 extractable=\u62bd\u51fa\u53ef\u80fd -force.delete.domain.warning=\u8b66\u544a\: \u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u3059\u3079\u3066\u306e\u5b50\u30c9\u30e1\u30a4\u30f3\u304a\u3088\u3073\u95a2\u9023\u3059\u308b\u3059\u3079\u3066\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3068\u305d\u306e\u30ea\u30bd\u30fc\u30b9\u304c\u524a\u9664\u3055\u308c\u307e\u3059\u3002 +force.delete.domain.warning=\u8b66\u544a: \u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u3059\u3079\u3066\u306e\u5b50\u30c9\u30e1\u30a4\u30f3\u304a\u3088\u3073\u95a2\u9023\u3059\u308b\u3059\u3079\u3066\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3068\u305d\u306e\u30ea\u30bd\u30fc\u30b9\u304c\u524a\u9664\u3055\u308c\u307e\u3059\u3002 force.delete=\u5f37\u5236\u524a\u9664 -force.remove.host.warning=\u8b66\u544a\: \u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u5b9f\u884c\u4e2d\u306e\u3059\u3079\u3066\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u304c\u5f37\u5236\u7684\u306b\u505c\u6b62\u3055\u308c\u3001\u30af\u30e9\u30b9\u30bf\u30fc\u304b\u3089\u3053\u306e\u30db\u30b9\u30c8\u304c\u5f37\u5236\u7684\u306b\u89e3\u9664\u3055\u308c\u307e\u3059\u3002 +force.remove.host.warning=\u8b66\u544a: \u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3059\u308b\u3068\u3001\u5b9f\u884c\u4e2d\u306e\u3059\u3079\u3066\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u304c\u5f37\u5236\u7684\u306b\u505c\u6b62\u3055\u308c\u3001\u30af\u30e9\u30b9\u30bf\u30fc\u304b\u3089\u3053\u306e\u30db\u30b9\u30c8\u304c\u5f37\u5236\u7684\u306b\u89e3\u9664\u3055\u308c\u307e\u3059\u3002 force.remove=\u5f37\u5236\u89e3\u9664 -force.stop.instance.warning=\u8b66\u544a\: \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u5f37\u5236\u505c\u6b62\u306f\u3001\u6700\u7d42\u624b\u6bb5\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30c7\u30fc\u30bf\u3092\u640d\u5931\u3059\u308b\u3060\u3051\u3067\u306a\u304f\u3001\u4eee\u60f3\u30de\u30b7\u30f3\u306e\u52d5\u4f5c\u304c\u4e00\u8cab\u3057\u306a\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +force.stop.instance.warning=\u8b66\u544a: \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u5f37\u5236\u505c\u6b62\u306f\u3001\u6700\u7d42\u624b\u6bb5\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30c7\u30fc\u30bf\u3092\u640d\u5931\u3059\u308b\u3060\u3051\u3067\u306a\u304f\u3001\u4eee\u60f3\u30de\u30b7\u30f3\u306e\u52d5\u4f5c\u304c\u4e00\u8cab\u3057\u306a\u304f\u306a\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 force.stop=\u5f37\u5236\u505c\u6b62 ICMP.code=ICMP \u30b3\u30fc\u30c9 ICMP.type=ICMP \u306e\u7a2e\u985e @@ -230,7 +230,7 @@ label.action.update.resource.count.processing=\u30ea\u30bd\u30fc\u30b9\u6570\u30 label.action.update.resource.count=\u30ea\u30bd\u30fc\u30b9\u6570\u306e\u66f4\u65b0 label.activate.project=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u30a2\u30af\u30c6\u30a3\u30d6\u5316 label.active.sessions=\u30a2\u30af\u30c6\u30a3\u30d6\u306a\u30bb\u30c3\u30b7\u30e7\u30f3 -label.add.accounts.to=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u8ffd\u52a0\u5148\: +label.add.accounts.to=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u8ffd\u52a0\u5148: label.add.accounts=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u8ffd\u52a0 label.add.account.to.project=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u8ffd\u52a0 label.add.account=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u8ffd\u52a0 @@ -456,7 +456,7 @@ label.domain.admin=\u30c9\u30e1\u30a4\u30f3\u7ba1\u7406\u8005 label.domain.id=\u30c9\u30e1\u30a4\u30f3 ID label.domain.name=\u30c9\u30e1\u30a4\u30f3\u540d label.domain.router=\u30c9\u30e1\u30a4\u30f3 \u30eb\u30fc\u30bf\u30fc -label.domain.suffix=DNS \u30c9\u30e1\u30a4\u30f3 \u30b5\u30d5\u30a3\u30c3\u30af\u30b9 (\u4f8b\: xyz.com) +label.domain.suffix=DNS \u30c9\u30e1\u30a4\u30f3 \u30b5\u30d5\u30a3\u30c3\u30af\u30b9 (\u4f8b: xyz.com) label.domain=\u30c9\u30e1\u30a4\u30f3 label.done=\u5b8c\u4e86 label.double.quotes.are.not.allowed=\u4e8c\u91cd\u5f15\u7528\u7b26\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093 @@ -534,7 +534,7 @@ label.host.alerts=\u30db\u30b9\u30c8 \u30a2\u30e9\u30fc\u30c8 label.host.MAC=\u30db\u30b9\u30c8\u306e MAC label.host.name=\u30db\u30b9\u30c8\u540d label.hosts=\u30db\u30b9\u30c8 -label.host.tags=\u00e3\u0083\u009b\u00e3\u0082\u00b9\u00e3\u0083\u0088\u00e3\u0082\u00bf\u00e3\u0082\u00b0 +label.host.tags=\u30db\u30b9\u30c8 \u30bf\u30b0 label.host=\u30db\u30b9\u30c8 label.hourly=\u6bce\u6642 label.hypervisor.capabilities=\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u306e\u6a5f\u80fd @@ -563,8 +563,8 @@ label.installWizard.addZoneIntro.subtitle=\u30be\u30fc\u30f3\u306b\u3064\u3044\u label.installWizard.addZoneIntro.title=\u30be\u30fc\u30f3\u3092\u8ffd\u52a0\u3057\u307e\u3057\u3087\u3046 label.installWizard.addZone.title=\u30be\u30fc\u30f3\u306e\u8ffd\u52a0 label.installWizard.click.launch=[\u8d77\u52d5] \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -label.installWizard.subtitle=\u3053\u306e\u30ac\u30a4\u30c9 \u30c4\u30a2\u30fc\u306f CloudStack&\#8482; \u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306b\u5f79\u7acb\u3061\u307e\u3059 -label.installWizard.title=CloudStack&\#8482; \u3078\u3088\u3046\u3053\u305d +label.installWizard.subtitle=\u3053\u306e\u30ac\u30a4\u30c9 \u30c4\u30a2\u30fc\u306f CloudStack™ \u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306b\u5f79\u7acb\u3061\u307e\u3059 +label.installWizard.title=CloudStack™ \u3078\u3088\u3046\u3053\u305d label.instance.limits=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5236\u9650 label.instance.name=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u540d label.instances=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9 @@ -573,12 +573,12 @@ label.internal.dns.1=\u5185\u90e8 DNS 1 label.internal.dns.2=\u5185\u90e8 DNS 2 label.internal.name=\u5185\u90e8\u540d label.interval.type=\u9593\u9694\u306e\u7a2e\u985e -label.introduction.to.cloudstack=CloudStack&\#8482; \u306e\u7d39\u4ecb +label.introduction.to.cloudstack=CloudStack™ \u306e\u7d39\u4ecb label.invalid.integer=\u7121\u52b9\u306a\u6574\u6570 label.invalid.number=\u7121\u52b9\u306a\u6570 label.invitations=\u62db\u5f85\u72b6 label.invited.accounts=\u62db\u5f85\u6e08\u307f\u30a2\u30ab\u30a6\u30f3\u30c8 -label.invite.to=\u62db\u5f85\u3059\u308b\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\: +label.invite.to=\u62db\u5f85\u3059\u308b\u30d7\u30ed\u30b8\u30a7\u30af\u30c8: label.invite=\u62db\u5f85 label.ip.address=IP \u30a2\u30c9\u30ec\u30b9 label.ipaddress=IP \u30a2\u30c9\u30ec\u30b9 @@ -695,9 +695,9 @@ label.menu.virtual.resources=\u4eee\u60f3\u30ea\u30bd\u30fc\u30b9 label.menu.volumes=\u30dc\u30ea\u30e5\u30fc\u30e0 label.migrate.instance.to.host=\u5225\u306e\u30db\u30b9\u30c8\u3078\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u79fb\u884c label.migrate.instance.to.ps=\u5225\u306e\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3078\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u79fb\u884c -label.migrate.instance.to=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u79fb\u884c\u5148\: -label.migrate.router.to=\u30eb\u30fc\u30bf\u30fc\u306e\u79fb\u884c\u5148\: -label.migrate.systemvm.to=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u79fb\u884c\u5148\: +label.migrate.instance.to=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u79fb\u884c\u5148: +label.migrate.router.to=\u30eb\u30fc\u30bf\u30fc\u306e\u79fb\u884c\u5148: +label.migrate.systemvm.to=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u79fb\u884c\u5148: label.migrate.to.host=\u30db\u30b9\u30c8\u3078\u79fb\u884c label.migrate.to.storage=\u30b9\u30c8\u30ec\u30fc\u30b8\u3078\u79fb\u884c label.migrate.volume=\u5225\u306e\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3078\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u79fb\u884c @@ -785,7 +785,7 @@ label.os.preference=OS \u57fa\u672c\u8a2d\u5b9a label.os.type=OS \u306e\u7a2e\u985e label.owned.public.ips=\u6240\u6709\u3059\u308b\u30d1\u30d6\u30ea\u30c3\u30af IP \u30a2\u30c9\u30ec\u30b9 label.owner.account=\u6240\u6709\u8005\u30a2\u30ab\u30a6\u30f3\u30c8 -label.owner.domain=\u00e6\u0089\u0080\u00e6\u009c\u0089\u00e8\u0080 +label.owner.domain=\u6240\u6709\u8005\u30c9\u30e1\u30a4\u30f3 label.parent.domain=\u89aa\u30c9\u30e1\u30a4\u30f3 label.password.enabled=\u30d1\u30b9\u30ef\u30fc\u30c9\u7ba1\u7406\u6709\u52b9 label.password=\u30d1\u30b9\u30ef\u30fc\u30c9 @@ -817,7 +817,7 @@ label.private.interface=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 \u30a4\u30f3\u30bf\ label.private.ip.range=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2 label.private.ips=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 IP \u30a2\u30c9\u30ec\u30b9 label.private.ip=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 IP \u30a2\u30c9\u30ec\u30b9 -label.privatekey=PKC\#8 \u79d8\u5bc6\u30ad\u30fc +label.privatekey=PKC#8 \u79d8\u5bc6\u30ad\u30fc label.private.network=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af label.private.port=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 \u30dd\u30fc\u30c8 label.private.zone=\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 \u30be\u30fc\u30f3 @@ -951,7 +951,7 @@ label.start.reserved.system.IP=\u4e88\u7d04\u6e08\u307f\u958b\u59cb\u30b7\u30b9\ label.start.vlan=\u958b\u59cb VLAN label.state=\u72b6\u614b label.static.nat.enabled=\u9759\u7684 NAT \u6709\u52b9 -label.static.nat.to=\u9759\u7684 NAT \u306e\u8a2d\u5b9a\u5148\: +label.static.nat.to=\u9759\u7684 NAT \u306e\u8a2d\u5b9a\u5148: label.static.nat=\u9759\u7684 NAT label.static.nat.vm.details=\u9759\u7684 NAT VM \u306e\u8a73\u7d30 label.statistics=\u7d71\u8a08 @@ -960,7 +960,7 @@ label.step.1.title=\u624b\u9806 1. \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\ label.step.1=\u624b\u9806 1 label.step.2.title=\u624b\u9806 2. \u30b5\u30fc\u30d3\u30b9 \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0 label.step.2=\u624b\u9806 2 -label.step.3.title=\u624b\u9806 3. \u30c7\u30a3\u30b9\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u306e\u9078\u629e +label.step.3.title=\u624b\u9806 3. \u30c7\u30a3\u30b9\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u306e\u9078\u629e label.step.3=\u624b\u9806 3 label.step.4.title=\u624b\u9806 4. \u30cd\u30c3\u30c8\u30ef\u30fc\u30af label.step.4=\u624b\u9806 4 @@ -986,7 +986,7 @@ label.storage.traffic=\u30b9\u30c8\u30ec\u30fc\u30b8 \u30c8\u30e9\u30d5\u30a3\u3 label.storage.type=\u30b9\u30c8\u30ec\u30fc\u30b8\u306e\u7a2e\u985e label.storage=\u30b9\u30c8\u30ec\u30fc\u30b8 label.subdomain.access=\u30b5\u30d6\u30c9\u30e1\u30a4\u30f3 \u30a2\u30af\u30bb\u30b9 -label.submitted.by=[\u9001\u4fe1\u30e6\u30fc\u30b6\u30fc\: ] +label.submitted.by=[\u9001\u4fe1\u30e6\u30fc\u30b6\u30fc: ] label.submit=\u9001\u4fe1 label.succeeded=\u6210\u529f label.sunday=\u65e5\u66dc\u65e5 @@ -1061,7 +1061,7 @@ label.vcipaddress=vCenter IP \u30a2\u30c9\u30ec\u30b9 label.version=\u30d0\u30fc\u30b8\u30e7\u30f3 label.view.all=\u3059\u3079\u3066\u8868\u793a label.view.console=\u30b3\u30f3\u30bd\u30fc\u30eb\u306e\u8868\u793a -label.viewing=\u8868\u793a\u9805\u76ee\: +label.viewing=\u8868\u793a\u9805\u76ee: label.view.more=\u8a73\u7d30\u8868\u793a label.view=\u8868\u793a - label.virtual.appliances=\u4eee\u60f3\u30a2\u30d7\u30e9\u30a4\u30a2\u30f3\u30b9 @@ -1110,7 +1110,7 @@ label.wednesday=\u6c34\u66dc\u65e5 label.weekly=\u6bce\u9031 label.welcome.cloud.console=\u7ba1\u7406\u30b3\u30f3\u30bd\u30fc\u30eb\u3078\u3088\u3046\u3053\u305d label.welcome=\u3088\u3046\u3053\u305d -label.what.is.cloudstack=CloudStack&\#8482; \u306b\u3064\u3044\u3066 +label.what.is.cloudstack=CloudStack™ \u306b\u3064\u3044\u3066 label.xen.traffic.label=XenServer \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306e\u30e9\u30d9\u30eb label.yes=\u306f\u3044 label.zone.details=\u30be\u30fc\u30f3\u306e\u8a73\u7d30 @@ -1124,9 +1124,9 @@ label.zones=\u30be\u30fc\u30f3 label.zone.type=\u30be\u30fc\u30f3\u306e\u7a2e\u985e label.zone=\u30be\u30fc\u30f3 label.zone.wide=\u30be\u30fc\u30f3\u5168\u4f53 -label.zoneWizard.trafficType.guest=\u30b2\u30b9\u30c8\: \u30a8\u30f3\u30c9\u30e6\u30fc\u30b6\u30fc\u4eee\u60f3\u30de\u30b7\u30f3\u9593\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af -label.zoneWizard.trafficType.public=\u30d1\u30d6\u30ea\u30c3\u30af\: \u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u3068\u30af\u30e9\u30a6\u30c9\u5185\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u306e\u9593\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af -label.zoneWizard.trafficType.storage=\u30b9\u30c8\u30ec\u30fc\u30b8\: VM\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3068\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u306e\u3088\u3046\u306a\u3001\u30d7\u30e9\u30a4\u30de\u30ea\u3068\u30bb\u30ab\u30f3\u30c0\u30ea\u306e\u30b9\u30c8\u30ec\u30fc\u30b8\u30b5\u30fc\u30d0\u30fc\u9593\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u3002 +label.zoneWizard.trafficType.guest=\u30b2\u30b9\u30c8: \u30a8\u30f3\u30c9\u30e6\u30fc\u30b6\u30fc\u4eee\u60f3\u30de\u30b7\u30f3\u9593\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af +label.zoneWizard.trafficType.public=\u30d1\u30d6\u30ea\u30c3\u30af: \u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u3068\u30af\u30e9\u30a6\u30c9\u5185\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u306e\u9593\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af +label.zoneWizard.trafficType.storage=\u30b9\u30c8\u30ec\u30fc\u30b8: VM\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3068\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u306e\u3088\u3046\u306a\u3001\u30d7\u30e9\u30a4\u30de\u30ea\u3068\u30bb\u30ab\u30f3\u30c0\u30ea\u306e\u30b9\u30c8\u30ec\u30fc\u30b8\u30b5\u30fc\u30d0\u30fc\u9593\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u3002 managed.state=\u7ba1\u7406\u5bfe\u8c61\u72b6\u614b message.acquire.new.ip=\u3053\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u65b0\u3057\u3044 IP \u30a2\u30c9\u30ec\u30b9\u3092\u53d6\u5f97\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.acquire.new.ip.vpc=VPC\u306e\u65b0\u3057\u3044IP\u3092\u53d6\u5f97\u3059\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 @@ -1138,8 +1138,8 @@ message.action.change.service.warning.for.router=\u73fe\u5728\u306e\u30b5\u30fc\ message.action.delete.cluster=\u3053\u306e\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.delete.disk.offering=\u3053\u306e\u30c7\u30a3\u30b9\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.delete.domain=\u3053\u306e\u30c9\u30e1\u30a4\u30f3\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.action.delete.external.firewall=\u3053\u306e\u5916\u90e8\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? \u8b66\u544a\: \u540c\u3058\u5916\u90e8\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u3092\u518d\u5ea6\u8ffd\u52a0\u3059\u308b\u4e88\u5b9a\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u306e\u4f7f\u7528\u72b6\u6cc1\u30c7\u30fc\u30bf\u3092\u30ea\u30bb\u30c3\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 -message.action.delete.external.load.balancer=\u3053\u306e\u5916\u90e8\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? \u8b66\u544a\: \u540c\u3058\u5916\u90e8\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u3092\u518d\u5ea6\u8ffd\u52a0\u3059\u308b\u4e88\u5b9a\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u306e\u4f7f\u7528\u72b6\u6cc1\u30c7\u30fc\u30bf\u3092\u30ea\u30bb\u30c3\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +message.action.delete.external.firewall=\u3053\u306e\u5916\u90e8\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? \u8b66\u544a: \u540c\u3058\u5916\u90e8\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u3092\u518d\u5ea6\u8ffd\u52a0\u3059\u308b\u4e88\u5b9a\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u306e\u4f7f\u7528\u72b6\u6cc1\u30c7\u30fc\u30bf\u3092\u30ea\u30bb\u30c3\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 +message.action.delete.external.load.balancer=\u3053\u306e\u5916\u90e8\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? \u8b66\u544a: \u540c\u3058\u5916\u90e8\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u3092\u518d\u5ea6\u8ffd\u52a0\u3059\u308b\u4e88\u5b9a\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u306e\u4f7f\u7528\u72b6\u6cc1\u30c7\u30fc\u30bf\u3092\u30ea\u30bb\u30c3\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 message.action.delete.ingress.rule=\u3053\u306e\u53d7\u4fe1\u898f\u5247\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.delete.ISO.for.all.zones=\u305d\u306e ISO \u306f\u3059\u3079\u3066\u306e\u30be\u30fc\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u3059\u3079\u3066\u306e\u30be\u30fc\u30f3\u304b\u3089\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.delete.ISO=\u3053\u306e ISO \u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? @@ -1175,9 +1175,9 @@ message.action.enable.pod=\u3053\u306e\u30dd\u30c3\u30c9\u3092\u6709\u52b9\u306b message.action.enable.zone=\u3053\u306e\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.force.reconnect=\u30db\u30b9\u30c8\u306f\u5f37\u5236\u7684\u306b\u518d\u63a5\u7d9a\u3057\u307e\u3057\u305f\u3002\u3053\u306e\u51e6\u7406\u306b\u306f\u6570\u5206\u304b\u304b\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 message.action.host.enable.maintenance.mode=\u4fdd\u5b88\u30e2\u30fc\u30c9\u3092\u6709\u52b9\u306b\u3059\u308b\u3068\u3001\u3053\u306e\u30db\u30b9\u30c8\u3067\u5b9f\u884c\u4e2d\u306e\u3059\u3079\u3066\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u304c\u307b\u304b\u306e\u4f7f\u7528\u3067\u304d\u308b\u30db\u30b9\u30c8\u306b\u30e9\u30a4\u30d6 \u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3055\u308c\u307e\u3059\u3002 -message.action.instance.reset.password=\u00e3\u0081\u0093\u00e3\u0081\u00ae\u00e4\u00bb\u00ae\u00e6\u0083\u00b3\u00e3\u0083\u009e\u00e3\u0082\u00b7\u00e3\u0083\u00b3\u00e3\u0081\u00ae\u00e3\u0083\u00ab\u00e3\u0083\u00bc\u00e3\u0083\u0088\u00e3\u0083\u0091\u00e3\u0082\u00b9\u00e3\u0083\u00af\u00e3\u0083\u00bc\u00e3\u0083\u0089\u00e3\u0082\u0092\u00e5\u00a4\u0089\u00e6\u009b\u00b4\u00e3\u0081\u0097\u00e3\u0081\u00a6\u00e3\u0082\u0082\u00e3\u0082\u0088\u00e3\u0082\u008d\u00e3\u0081\u0097\u00e3\u0081\u0084\u00e3\u0081\u00a7\u00e3\u0081\u0099\u00e3\u0081\u008b? +message.action.instance.reset.password=\u3053\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u306e\u30eb\u30fc\u30c8 \u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5909\u66f4\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.manage.cluster=\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u7ba1\u7406\u5bfe\u8c61\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.action.primarystorage.enable.maintenance.mode=\u8b66\u544a\: \u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u4fdd\u5b88\u30e2\u30fc\u30c9\u306b\u3059\u308b\u3068\u3001\u305d\u306e\u30b9\u30c8\u30ec\u30fc\u30b8\u4e0a\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u4f7f\u7528\u3059\u308b\u3059\u3079\u3066\u306e VM \u304c\u505c\u6b62\u3057\u307e\u3059\u3002\u7d9a\u884c\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.action.primarystorage.enable.maintenance.mode=\u8b66\u544a: \u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u4fdd\u5b88\u30e2\u30fc\u30c9\u306b\u3059\u308b\u3068\u3001\u305d\u306e\u30b9\u30c8\u30ec\u30fc\u30b8\u4e0a\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u4f7f\u7528\u3059\u308b\u3059\u3079\u3066\u306e VM \u304c\u505c\u6b62\u3057\u307e\u3059\u3002\u7d9a\u884c\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.reboot.instance=\u3053\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u518d\u8d77\u52d5\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.reboot.router=\u3053\u306e\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc\u3067\u63d0\u4f9b\u3059\u308b\u3059\u3079\u3066\u306e\u30b5\u30fc\u30d3\u30b9\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3059\u3002\u3053\u306e\u30eb\u30fc\u30bf\u30fc\u3092\u518d\u8d77\u52d5\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.reboot.systemvm=\u3053\u306e\u30b7\u30b9\u30c6\u30e0 VM \u3092\u518d\u8d77\u52d5\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? @@ -1195,8 +1195,8 @@ message.action.stop.systemvm=\u3053\u306e\u30b7\u30b9\u30c6\u30e0 VM \u3092\u505 message.action.take.snapshot=\u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8\u3092\u4f5c\u6210\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.unmanage.cluster=\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u975e\u7ba1\u7406\u5bfe\u8c61\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.activate.project=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u30a2\u30af\u30c6\u30a3\u30d6\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.add.cluster=\u30be\u30fc\u30f3 \u306e\u30dd\u30c3\u30c9 \u306b\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u3067\u7ba1\u7406\u3055\u308c\u308b\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u8ffd\u52a0\u3057\u307e\u3059 -message.add.cluster.zone=\u30be\u30fc\u30f3 \u306b\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u3067\u7ba1\u7406\u3055\u308c\u308b\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.cluster=\u30be\u30fc\u30f3 \u306e\u30dd\u30c3\u30c9 \u306b\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u3067\u7ba1\u7406\u3055\u308c\u308b\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.cluster.zone=\u30be\u30fc\u30f3 \u306b\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u3067\u7ba1\u7406\u3055\u308c\u308b\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u8ffd\u52a0\u3057\u307e\u3059 message.add.disk.offering=\u65b0\u3057\u3044\u30c7\u30a3\u30b9\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.add.domain=\u3053\u306e\u30c9\u30e1\u30a4\u30f3\u306b\u4f5c\u6210\u3059\u308b\u30b5\u30d6\u30c9\u30e1\u30a4\u30f3\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.add.firewall=\u30be\u30fc\u30f3\u306b\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u3092\u8ffd\u52a0\u3057\u307e\u3059 @@ -1205,18 +1205,18 @@ message.add.host=\u65b0\u3057\u3044\u30db\u30b9\u30c8\u3092\u8ffd\u52a0\u3059\u3 message.adding.host=\u30db\u30b9\u30c8\u3092\u8ffd\u52a0\u3057\u3066\u3044\u307e\u3059 message.adding.Netscaler.device=Netscaler \u30c7\u30d0\u30a4\u30b9\u3092\u8ffd\u52a0\u3057\u3066\u3044\u307e\u3059 message.adding.Netscaler.provider=Netscaler \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u8ffd\u52a0\u3057\u3066\u3044\u307e\u3059 -message.add.ip.range.direct.network=\u30be\u30fc\u30f3 \u306e\u76f4\u63a5\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u306b IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u8ffd\u52a0\u3057\u307e\u3059 -message.add.ip.range.to.pod=

    \u30dd\u30c3\u30c9 \u306b IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u8ffd\u52a0\u3057\u307e\u3059

    +message.add.ip.range.direct.network=\u30be\u30fc\u30f3 \u306e\u76f4\u63a5\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u306b IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.ip.range.to.pod=

    \u30dd\u30c3\u30c9 \u306b IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u8ffd\u52a0\u3057\u307e\u3059

    message.add.ip.range=\u30be\u30fc\u30f3\u306e\u30d1\u30d6\u30ea\u30c3\u30af \u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306b IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u8ffd\u52a0\u3057\u307e\u3059 message.additional.networks.desc=\u4eee\u60f3\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u304c\u63a5\u7d9a\u3059\u308b\u8ffd\u52a0\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.add.load.balancer=\u30be\u30fc\u30f3\u306b\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u3092\u8ffd\u52a0\u3057\u307e\u3059 -message.add.load.balancer.under.ip=\u8ca0\u8377\u5206\u6563\u898f\u5247\u304c\u6b21\u306e IP \u30a2\u30c9\u30ec\u30b9\u306b\u5bfe\u3057\u3066\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\: -message.add.network=\u30be\u30fc\u30f3 \u306b\u65b0\u3057\u3044\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.load.balancer.under.ip=\u8ca0\u8377\u5206\u6563\u898f\u5247\u304c\u6b21\u306e IP \u30a2\u30c9\u30ec\u30b9\u306b\u5bfe\u3057\u3066\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f: +message.add.network=\u30be\u30fc\u30f3 \u306b\u65b0\u3057\u3044\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3092\u8ffd\u52a0\u3057\u307e\u3059 message.add.new.gateway.to.vpc=\u3053\u306e VPC \u306b\u65b0\u3057\u3044\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306e\u60c5\u5831\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -message.add.pod=\u30be\u30fc\u30f3 \u306b\u65b0\u3057\u3044\u30dd\u30c3\u30c9\u3092\u8ffd\u52a0\u3057\u307e\u3059 -message.add.primary.storage=\u30be\u30fc\u30f3 \u306e\u30dd\u30c3\u30c9 \u306b\u65b0\u3057\u3044\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.pod=\u30be\u30fc\u30f3 \u306b\u65b0\u3057\u3044\u30dd\u30c3\u30c9\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.primary.storage=\u30be\u30fc\u30f3 \u306e\u30dd\u30c3\u30c9 \u306b\u65b0\u3057\u3044\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u8ffd\u52a0\u3057\u307e\u3059 message.add.primary=\u65b0\u3057\u3044\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -message.add.secondary.storage=\u30be\u30fc\u30f3 \u306b\u65b0\u3057\u3044\u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u8ffd\u52a0\u3057\u307e\u3059 +message.add.secondary.storage=\u30be\u30fc\u30f3 \u306b\u65b0\u3057\u3044\u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u8ffd\u52a0\u3057\u307e\u3059 message.add.service.offering=\u65b0\u3057\u3044\u30b3\u30f3\u30d4\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0 \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u30c7\u30fc\u30bf\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.add.system.service.offering=\u65b0\u3057\u3044\u30b7\u30b9\u30c6\u30e0 \u30b5\u30fc\u30d3\u30b9 \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u30c7\u30fc\u30bf\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.add.template=\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f5c\u6210\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u30c7\u30fc\u30bf\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 @@ -1225,8 +1225,8 @@ message.add.VPN.gateway=VPN \u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3092\u8ffd\u52 message.advanced.mode.desc=VLAN \u30b5\u30dd\u30fc\u30c8\u3092\u6709\u52b9\u306b\u3059\u308b\u5834\u5408\u306f\u3001\u3053\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30e2\u30c7\u30eb\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3053\u306e\u30e2\u30c7\u30eb\u3067\u306f\u6700\u3082\u67d4\u8edf\u306b\u30ab\u30b9\u30bf\u30e0 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u63d0\u4f9b\u3067\u304d\u3001\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u3001VPN\u3001\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u306e\u30b5\u30dd\u30fc\u30c8\u306e\u307b\u304b\u306b\u3001\u76f4\u63a5\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3068\u4eee\u60f3\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3082\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002 message.advanced.security.group=\u30b2\u30b9\u30c8 VM \u3092\u5206\u96e2\u3059\u308b\u305f\u3081\u306b\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3 \u30b0\u30eb\u30fc\u30d7\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u3001\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.advanced.virtual=\u30b2\u30b9\u30c8 VM \u3092\u5206\u96e2\u3059\u308b\u305f\u3081\u306b\u30be\u30fc\u30f3\u5168\u4f53\u306e VLAN \u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u3001\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -message.after.enable.s3=S3\u57fa\u76e4\u30bb\u30ab\u30f3\u30c0\u30ea\u30b9\u30c8\u30ec\u30fc\u30b8\u304c\u8a2d\u5b9a\u3055\u308c\u307e\u3057\u305f\u3002 \u30ce\u30fc\u30c8\:\u3053\u306e\u30da\u30fc\u30b8\u3092\u9589\u3058\u308b\u3068S3\u3092\u518d\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093\u3002 -message.after.enable.swift=Swift \u304c\u69cb\u6210\u3055\u308c\u307e\u3057\u305f\u3002\u6ce8\: \u3053\u306e\u30da\u30fc\u30b8\u3092\u9589\u3058\u308b\u3068\u3001Swift \u3092\u518d\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002 +message.after.enable.s3=S3\u57fa\u76e4\u30bb\u30ab\u30f3\u30c0\u30ea\u30b9\u30c8\u30ec\u30fc\u30b8\u304c\u8a2d\u5b9a\u3055\u308c\u307e\u3057\u305f\u3002 \u30ce\u30fc\u30c8:\u3053\u306e\u30da\u30fc\u30b8\u3092\u9589\u3058\u308b\u3068S3\u3092\u518d\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093\u3002 +message.after.enable.swift=Swift \u304c\u69cb\u6210\u3055\u308c\u307e\u3057\u305f\u3002\u6ce8: \u3053\u306e\u30da\u30fc\u30b8\u3092\u9589\u3058\u308b\u3068\u3001Swift \u3092\u518d\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093\u3002 message.alert.state.detected=\u30a2\u30e9\u30fc\u30c8\u72b6\u614b\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f message.allow.vpn.access=VPN \u30a2\u30af\u30bb\u30b9\u3092\u8a31\u53ef\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u540d\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.apply.snapshot.policy=\u73fe\u5728\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8 \u30dd\u30ea\u30b7\u30fc\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f\u3002 @@ -1251,10 +1251,10 @@ message.confirm.join.project=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3 message.confirm.remove.IP.range=\u3053\u306e IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.confirm.shutdown.provider=\u3053\u306e\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.copy.iso.confirm=ISO \u3092\u6b21\u306e\u5834\u6240\u306b\u30b3\u30d4\u30fc\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.copy.template=\u30be\u30fc\u30f3 \u304b\u3089\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 XXX \u3092\u6b21\u306e\u5834\u6240\u306b\u30b3\u30d4\u30fc\u3057\u307e\u3059\: +message.copy.template=\u30be\u30fc\u30f3 \u304b\u3089\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 XXX \u3092\u6b21\u306e\u5834\u6240\u306b\u30b3\u30d4\u30fc\u3057\u307e\u3059: message.create.template=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f5c\u6210\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.create.template.vm=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 \u304b\u3089 VM \u3092\u4f5c\u6210\u3057\u307e\u3059 -message.create.template.volume=\u30c7\u30a3\u30b9\u30af \u30dc\u30ea\u30e5\u30fc\u30e0 \u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f5c\u6210\u3059\u308b\u524d\u306b\u3001\u6b21\u306e\u60c5\u5831\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30dc\u30ea\u30e5\u30fc\u30e0 \u30b5\u30a4\u30ba\u306b\u3088\u3063\u3066\u306f\u3001\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u4f5c\u6210\u306b\u306f\u6570\u5206\u4ee5\u4e0a\u304b\u304b\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 +message.create.template.vm=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 \u304b\u3089 VM \u3092\u4f5c\u6210\u3057\u307e\u3059 +message.create.template.volume=\u30c7\u30a3\u30b9\u30af \u30dc\u30ea\u30e5\u30fc\u30e0 \u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f5c\u6210\u3059\u308b\u524d\u306b\u3001\u6b21\u306e\u60c5\u5831\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30dc\u30ea\u30e5\u30fc\u30e0 \u30b5\u30a4\u30ba\u306b\u3088\u3063\u3066\u306f\u3001\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u4f5c\u6210\u306b\u306f\u6570\u5206\u4ee5\u4e0a\u304b\u304b\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 message.creating.cluster=\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u4f5c\u6210\u3057\u3066\u3044\u307e\u3059 message.creating.guest.network=\u30b2\u30b9\u30c8 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3092\u4f5c\u6210\u3057\u3066\u3044\u307e\u3059 message.creating.physical.networks=\u7269\u7406\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3092\u4f5c\u6210\u3057\u3066\u3044\u307e\u3059 @@ -1278,21 +1278,21 @@ message.desc.secondary.storage=\u5404\u30be\u30fc\u30f3\u306b\u306f\u5c11\u306a\ message.desc.zone=\u30be\u30fc\u30f3\u306f CloudStack \u74b0\u5883\u5185\u306e\u6700\u5927\u306e\u7d44\u7e54\u5358\u4f4d\u3067\u3001\u901a\u5e38\u3001\u5358\u4e00\u306e\u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u306b\u76f8\u5f53\u3057\u307e\u3059\u3002\u30be\u30fc\u30f3\u306b\u3088\u3063\u3066\u7269\u7406\u7684\u306a\u5206\u96e2\u3068\u5197\u9577\u6027\u304c\u63d0\u4f9b\u3055\u308c\u307e\u3059\u3002\u30be\u30fc\u30f3\u306f 1 \u3064\u4ee5\u4e0a\u306e\u30dd\u30c3\u30c9 (\u5404\u30dd\u30c3\u30c9\u306f\u30db\u30b9\u30c8\u3068\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8 \u30b5\u30fc\u30d0\u30fc\u304b\u3089\u69cb\u6210\u3055\u308c\u307e\u3059) \u3068\u3001\u30be\u30fc\u30f3\u5185\u306e\u3059\u3079\u3066\u306e\u30dd\u30c3\u30c9\u3067\u5171\u6709\u3055\u308c\u308b\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8 \u30b5\u30fc\u30d0\u30fc\u304b\u3089\u69cb\u6210\u3055\u308c\u307e\u3059\u3002 message.detach.disk=\u3053\u306e\u30c7\u30a3\u30b9\u30af\u3092\u30c7\u30bf\u30c3\u30c1\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.detach.iso.confirm=\u3053\u306e\u4eee\u60f3\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u304b\u3089 ISO \u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30bf\u30c3\u30c1\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.disable.account=\u00e3\u0081\u0093\u00e3\u0081\u00ae\u00e3\u0082\u00a2\u00e3\u0082\u00ab\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0083\u0088\u00e3\u0082\u0092\u00e7\u0084\u00a1\u00e5\u008a\u00b9\u00e3\u0081\u00ab\u00e3\u0081\u0097\u00e3\u0081\u00a6\u00e3\u0082\u0082\u00e3\u0082\u0088\u00e3\u0082\u008d\u00e3\u0081\u0097\u00e3\u0081\u0084\u00e3\u0081\u00a7\u00e3\u0081\u0099\u00e3\u0081\u008b? \u00e3\u0082\u00a2\u00e3\u0082\u00ab\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0083\u0088\u00e3\u0082\u0092\u00e7\u0084\u00a1\u00e5\u008a\u00b9\u00e3\u0081\u00ab\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u0093\u00e3\u0081\u00a8\u00e3\u0081\u00ab\u00e3\u0082\u0088\u00e3\u0082\u008a\u00e3\u0080\u0081\u00e3\u0081\u0093\u00e3\u0081\u00ae\u00e3\u0082\u00a2\u00e3\u0082\u00ab\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0083\u0088\u00e3\u0081\u00ae\u00e3\u0081\u0099\u00e3\u0081\u00b9\u00e3\u0081\u00a6\u00e3\u0081\u00ae\u00e3\u0083\u00a6\u00e3\u0083\u00bc\u00e3\u0082\u00b6\u00e3\u0083\u00bc\u00e3\u0081\u00af\u00e3\u0082\u00af\u00e3\u0083\u00a9\u00e3\u0082\u00a6\u00e3\u0083\u0089\u00e3\u0083\u00aa\u00e3\u0082\u00bd\u00e3\u0083\u00bc\u00e3\u0082\u00b9\u00e3\u0081\u00ab\u00e3\u0082\u00a2\u00e3\u0082\u00af\u00e3\u0082\u00bb\u00e3\u0082\u00b9\u00e3\u0081\u00a7\u00e3\u0081\u008d\u00e3\u0081\u00aa\u00e3\u0081\u008f\u00e3\u0081\u00aa\u00e3\u0082\u008a\u00e3\u0081\u00be\u00e3\u0081\u0099\u00e3\u0080\u0082\u00e5\u00ae\u009f\u00e8\u00a1\u008c\u00e4\u00b8\u00ad\u00e3\u0081\u00ae\u00e3\u0081\u0099\u00e3\u0081\u00b9\u00e3\u0081\u00a6\u00e3\u0081\u00ae\u00e4\u00bb\u00ae\u00e6\u0083\u00b3\u00e3\u0083\u009e\u00e3\u0082\u00b7\u00e3\u0083\u00b3\u00e3\u0081\u00af\u00e3\u0081\u0099\u00e3\u0081\u0090\u00e3\u0081\u00ab\u00e3\u0082\u00b7\u00e3\u0083\u00a3\u00e3\u0083\u0083\u00e3\u0083\u0088\u00e3\u0083\u0080\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0081\u0095\u00e3\u0082\u008c\u00e3\u0081\u00be\u00e3\u0081\u0099\u00e3\u0080\u0082 +message.disable.account=\u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u7121\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? \u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u3059\u3079\u3066\u306e\u30e6\u30fc\u30b6\u30fc\u304c\u30af\u30e9\u30a6\u30c9 \u30ea\u30bd\u30fc\u30b9\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002\u5b9f\u884c\u4e2d\u306e\u3059\u3079\u3066\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u306f\u4eca\u3059\u3050\u306b\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3\u3055\u308c\u307e\u3059\u3002 message.disable.snapshot.policy=\u73fe\u5728\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8 \u30dd\u30ea\u30b7\u30fc\u3092\u7121\u52b9\u306b\u3057\u307e\u3057\u305f\u3002 message.disable.user=\u3053\u306e\u30e6\u30fc\u30b6\u30fc\u3092\u7121\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.disable.vpn.access=VPN \u30a2\u30af\u30bb\u30b9\u3092\u7121\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.disable.vpn=VPN \u3092\u7121\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.download.ISO=ISO\u00e3\u0082\u0092\u00e3\u0083\u0080\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0083\u00ad\u00e3\u0083\u00bc\u00e3\u0083\u0089\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u009f\u00e3\u0082\u0081\u00e3\u0081\u00ab00000\u00e3\u0082\u0092\u00e3\u0082\u00af\u00e3\u0083\u00aa\u00e3\u0083\u0083\u00e3\u0082\u00af\u00e3\u0081\u0097\u00e3\u0081\u00a6\u00e3\u0081\u008f\u00e3\u0081\u00a0\u00e3\u0081\u0095\u00e3\u0081\u0084\u00e3\u0080\u0082 -message.download.template=\u00e3\u0083\u0086\u00e3\u0083\u00b3\u00e3\u0083\u0097\u00e3\u0083\u00ac\u00e3\u0083\u00bc\u00e3\u0083\u0088\u00e3\u0082\u0092\u00e3\u0083\u0080\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0083\u00ad\u00e3\u0083\u00bc\u00e3\u0083\u0089\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u009f\u00e3\u0082\u0081\u00e3\u0081\u00ab00000\u00e3\u0082\u0092\u00e3\u0082\u00af\u00e3\u0083\u00aa\u00e3\u0083\u0083\u00e3\u0082\u00af\u00e3\u0081\u0097\u00e3\u0081\u00a6\u00e3\u0081\u008f\u00e3\u0081\u00a0\u00e3\u0081\u0095\u00e3\u0081\u0084\u00e3\u0080\u0082 +message.download.ISO=ISO \u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f 00000 \u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059 +message.download.template=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f 00000 \u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059 message.download.volume.confirm=\u3053\u306e\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.download.volume=\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f 00000 \u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059 +message.download.volume=\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f 00000 \u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059 message.edit.account=\u7de8\u96c6 ("-1" \u306f\u3001\u30ea\u30bd\u30fc\u30b9\u4f5c\u6210\u306e\u91cf\u306b\u5236\u9650\u304c\u306a\u3044\u3053\u3068\u3092\u793a\u3057\u307e\u3059) message.edit.confirm=[\u4fdd\u5b58] \u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u524d\u306b\u5909\u66f4\u5185\u5bb9\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.edit.limits=\u6b21\u306e\u30ea\u30bd\u30fc\u30b9\u306b\u5236\u9650\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u300c-1\u300d\u306f\u3001\u30ea\u30bd\u30fc\u30b9\u4f5c\u6210\u306b\u5236\u9650\u304c\u306a\u3044\u3053\u3068\u3092\u793a\u3057\u307e\u3059\u3002 message.edit.traffic.type=\u3053\u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306e\u7a2e\u985e\u306b\u95a2\u9023\u4ed8\u3051\u308b\u30c8\u30e9\u30d5\u30a3\u30c3\u30af \u30e9\u30d9\u30eb\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.enable.account=\u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.enabled.vpn.ip.sec=IPSec \u4e8b\u524d\u5171\u6709\u30ad\u30fc\: +message.enabled.vpn.ip.sec=IPSec \u4e8b\u524d\u5171\u6709\u30ad\u30fc: message.enabled.vpn=\u73fe\u5728\u3001VPN \u30a2\u30af\u30bb\u30b9\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002\u6b21\u306e IP \u30a2\u30c9\u30ec\u30b9\u7d4c\u7531\u3067\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u3059\u3002 message.enable.user=\u3053\u306e\u30e6\u30fc\u30b6\u30fc\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.enable.vpn.access=\u73fe\u5728\u3053\u306e IP \u30a2\u30c9\u30ec\u30b9\u306b\u5bfe\u3059\u308b VPN \u306f\u7121\u52b9\u3067\u3059\u3002VPN \u30a2\u30af\u30bb\u30b9\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? @@ -1304,8 +1304,8 @@ message.generate.keys=\u3053\u306e\u30e6\u30fc\u30b6\u30fc\u306b\u65b0\u3057\u30 message.guest.traffic.in.advanced.zone=\u30b2\u30b9\u30c8 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306f\u3001\u30a8\u30f3\u30c9 \u30e6\u30fc\u30b6\u30fc\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u9593\u306e\u901a\u4fe1\u3067\u3059\u3002\u5404\u7269\u7406\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u30b2\u30b9\u30c8 \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u3092\u901a\u4fe1\u3059\u308b\u305f\u3081\u306e VLAN ID \u306e\u7bc4\u56f2\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.guest.traffic.in.basic.zone=\u30b2\u30b9\u30c8 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306f\u3001\u30a8\u30f3\u30c9 \u30e6\u30fc\u30b6\u30fc\u306e\u4eee\u60f3\u30de\u30b7\u30f3\u9593\u306e\u901a\u4fe1\u3067\u3059\u3002CloudStack \u3067\u30b2\u30b9\u30c8 VM \u306b\u5272\u308a\u5f53\u3066\u3089\u308c\u308b IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3053\u306e\u7bc4\u56f2\u304c\u4e88\u7d04\u6e08\u307f\u306e\u30b7\u30b9\u30c6\u30e0 IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u3068\u91cd\u8907\u3057\u306a\u3044\u3088\u3046\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.installWizard.click.retry=\u8d77\u52d5\u3092\u518d\u8a66\u884c\u3059\u308b\u306b\u306f\u30dc\u30bf\u30f3\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -message.installWizard.copy.whatIsAPod=\u901a\u5e38\u30011 \u3064\u306e\u30dd\u30c3\u30c9\u306f\u5358\u4e00\u306e\u30e9\u30c3\u30af\u3092\u8868\u3057\u307e\u3059\u3002\u540c\u3058\u30dd\u30c3\u30c9\u5185\u306e\u30db\u30b9\u30c8\u306f\u540c\u3058\u30b5\u30d6\u30cd\u30c3\u30c8\u306b\u542b\u307e\u308c\u307e\u3059\u3002

    \u30dd\u30c3\u30c9\u306f CloudStack&\#8482; \u74b0\u5883\u5185\u306e 2 \u756a\u76ee\u306b\u5927\u304d\u306a\u7d44\u7e54\u5358\u4f4d\u3067\u3059\u3002\u30dd\u30c3\u30c9\u306f\u30be\u30fc\u30f3\u306b\u542b\u307e\u308c\u307e\u3059\u3002\u5404\u30be\u30fc\u30f3\u306f 1 \u3064\u4ee5\u4e0a\u306e\u30dd\u30c3\u30c9\u3092\u542b\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u57fa\u672c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u306f\u3001\u30be\u30fc\u30f3\u5185\u306e\u30dd\u30c3\u30c9\u306f 1 \u3064\u3067\u3059\u3002 -message.installWizard.copy.whatIsAZone=\u30be\u30fc\u30f3\u306f CloudStack&\#8482; \u74b0\u5883\u5185\u306e\u6700\u5927\u306e\u7d44\u7e54\u5358\u4f4d\u3067\u3059\u30021 \u3064\u306e\u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u5185\u306b\u8907\u6570\u306e\u30be\u30fc\u30f3\u3092\u8a2d\u5b9a\u3067\u304d\u307e\u3059\u304c\u3001\u901a\u5e38\u3001\u30be\u30fc\u30f3\u306f\u5358\u4e00\u306e\u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u306b\u76f8\u5f53\u3057\u307e\u3059\u3002\u30a4\u30f3\u30d5\u30e9\u30b9\u30c8\u30e9\u30af\u30c1\u30e3\u3092\u30be\u30fc\u30f3\u306b\u7d44\u7e54\u5316\u3059\u308b\u3068\u3001\u30be\u30fc\u30f3\u3092\u7269\u7406\u7684\u306b\u5206\u96e2\u3057\u3066\u5197\u9577\u5316\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u5404\u30be\u30fc\u30f3\u306b\u96fb\u6e90\u3068\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30a2\u30c3\u30d7\u30ea\u30f3\u30af\u3092\u914d\u5099\u3057\u307e\u3059\u3002\u5fc5\u9808\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u304c\u3001\u30be\u30fc\u30f3\u306f\u9060\u9694\u5730\u306b\u5206\u6563\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002 +message.installWizard.copy.whatIsAPod=\u901a\u5e38\u30011 \u3064\u306e\u30dd\u30c3\u30c9\u306f\u5358\u4e00\u306e\u30e9\u30c3\u30af\u3092\u8868\u3057\u307e\u3059\u3002\u540c\u3058\u30dd\u30c3\u30c9\u5185\u306e\u30db\u30b9\u30c8\u306f\u540c\u3058\u30b5\u30d6\u30cd\u30c3\u30c8\u306b\u542b\u307e\u308c\u307e\u3059\u3002

    \u30dd\u30c3\u30c9\u306f CloudStack™ \u74b0\u5883\u5185\u306e 2 \u756a\u76ee\u306b\u5927\u304d\u306a\u7d44\u7e54\u5358\u4f4d\u3067\u3059\u3002\u30dd\u30c3\u30c9\u306f\u30be\u30fc\u30f3\u306b\u542b\u307e\u308c\u307e\u3059\u3002\u5404\u30be\u30fc\u30f3\u306f 1 \u3064\u4ee5\u4e0a\u306e\u30dd\u30c3\u30c9\u3092\u542b\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u57fa\u672c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u306f\u3001\u30be\u30fc\u30f3\u5185\u306e\u30dd\u30c3\u30c9\u306f 1 \u3064\u3067\u3059\u3002 +message.installWizard.copy.whatIsAZone=\u30be\u30fc\u30f3\u306f CloudStack™ \u74b0\u5883\u5185\u306e\u6700\u5927\u306e\u7d44\u7e54\u5358\u4f4d\u3067\u3059\u30021 \u3064\u306e\u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u5185\u306b\u8907\u6570\u306e\u30be\u30fc\u30f3\u3092\u8a2d\u5b9a\u3067\u304d\u307e\u3059\u304c\u3001\u901a\u5e38\u3001\u30be\u30fc\u30f3\u306f\u5358\u4e00\u306e\u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u306b\u76f8\u5f53\u3057\u307e\u3059\u3002\u30a4\u30f3\u30d5\u30e9\u30b9\u30c8\u30e9\u30af\u30c1\u30e3\u3092\u30be\u30fc\u30f3\u306b\u7d44\u7e54\u5316\u3059\u308b\u3068\u3001\u30be\u30fc\u30f3\u3092\u7269\u7406\u7684\u306b\u5206\u96e2\u3057\u3066\u5197\u9577\u5316\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305f\u3068\u3048\u3070\u3001\u5404\u30be\u30fc\u30f3\u306b\u96fb\u6e90\u3068\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30a2\u30c3\u30d7\u30ea\u30f3\u30af\u3092\u914d\u5099\u3057\u307e\u3059\u3002\u5fc5\u9808\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u304c\u3001\u30be\u30fc\u30f3\u306f\u9060\u9694\u5730\u306b\u5206\u6563\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002 message.installWizard.copy.whatIsSecondaryStorage=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u306f\u30be\u30fc\u30f3\u3068\u95a2\u9023\u4ed8\u3051\u3089\u308c\u3001\u6b21\u306e\u9805\u76ee\u3092\u683c\u7d0d\u3057\u307e\u3059\u3002
    • \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 - VM \u306e\u8d77\u52d5\u306b\u4f7f\u7528\u3067\u304d\u308b OS \u30a4\u30e1\u30fc\u30b8\u3067\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306a\u3069\u8ffd\u52a0\u306e\u69cb\u6210\u3092\u542b\u3081\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002
    • ISO \u30a4\u30e1\u30fc\u30b8 - \u8d77\u52d5\u53ef\u80fd\u307e\u305f\u306f\u8d77\u52d5\u4e0d\u53ef\u306e OS \u30a4\u30e1\u30fc\u30b8\u3067\u3059\u3002
    • \u30c7\u30a3\u30b9\u30af \u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u30b9\u30ca\u30c3\u30d7\u30b7\u30e7\u30c3\u30c8 - VM \u30c7\u30fc\u30bf\u306e\u4fdd\u5b58\u30b3\u30d4\u30fc\u3067\u3059\u3002\u30c7\u30fc\u30bf\u306e\u5fa9\u5143\u307e\u305f\u306f\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u4f5c\u6210\u306b\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002
    message.installWizard.tooltip.addCluster.name=\u30af\u30e9\u30b9\u30bf\u30fc\u306e\u540d\u524d\u3067\u3059\u3002CloudStack \u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u306a\u3044\u3001\u4efb\u610f\u306e\u30c6\u30ad\u30b9\u30c8\u3092\u6307\u5b9a\u3067\u304d\u307e\u3059\u3002 message.installWizard.tooltip.addHost.hostname=\u30db\u30b9\u30c8\u306e DNS \u540d\u307e\u305f\u306f IP \u30a2\u30c9\u30ec\u30b9\u3067\u3059\u3002 @@ -1344,7 +1344,7 @@ message.migrate.instance.to.ps=\u5225\u306e\u30d7\u30e9\u30a4\u30de\u30ea \u30b9 message.migrate.router.confirm=\u30eb\u30fc\u30bf\u30fc\u306e\u79fb\u884c\u5148\u306f\u6b21\u306e\u30db\u30b9\u30c8\u3067\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.migrate.systemvm.confirm=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u79fb\u884c\u5148\u306f\u6b21\u306e\u30db\u30b9\u30c8\u3067\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.migrate.volume=\u5225\u306e\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u306b\u30dc\u30ea\u30e5\u30fc\u30e0\u3092\u79fb\u884c\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.new.user=\u00e3\u0082\u00a2\u00e3\u0082\u00ab\u00e3\u0082\u00a6\u00e3\u0083\u00b3\u00e3\u0083\u0088\u00e3\u0081\u00ab\u00e6\u0096\u00b0\u00e3\u0081\u0097\u00e3\u0081\u0084\u00e3\u0083\u00a6\u00e3\u0083\u00bc\u00e3\u0082\u00b6\u00e3\u0083\u00bc\u00e3\u0082\u0092\u00e8\u00bf\u00bd\u00e5\u008a\u00a0\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u009f\u00e3\u0082\u0081\u00e3\u0081\u00ab\u00e3\u0080\u0081\u00e6\u00ac\u00a1\u00e3\u0081\u00ae\u00e6\u0083 +message.new.user=\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u65b0\u3057\u3044\u30e6\u30fc\u30b6\u30fc\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u60c5\u5831\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.no.network.support.configuration.not.true=\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3 \u30b0\u30eb\u30fc\u30d7\u304c\u6709\u52b9\u306a\u30be\u30fc\u30f3\u304c\u7121\u3044\u305f\u3081\u3001\u8ffd\u52a0\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u6a5f\u80fd\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u624b\u9806 5. \u306b\u9032\u3093\u3067\u304f\u3060\u3055\u3044\u3002 message.no.network.support=\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u3068\u3057\u3066 vSphere \u3092\u9078\u629e\u3057\u307e\u3057\u305f\u304c\u3001\u3053\u306e\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc\u306b\u8ffd\u52a0\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u6a5f\u80fd\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u624b\u9806 5. \u306b\u9032\u3093\u3067\u304f\u3060\u3055\u3044\u3002 message.no.projects.adminOnly=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
    \u7ba1\u7406\u8005\u306b\u65b0\u3057\u3044\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\u3092\u4f9d\u983c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 @@ -1407,7 +1407,7 @@ message.tooltip.reserved.system.netmask=\u30dd\u30c3\u30c9\u306e\u30b5\u30d6\u30 message.tooltip.zone.name=\u30be\u30fc\u30f3\u306e\u540d\u524d\u3067\u3059\u3002 message.update.os.preference=\u3053\u306e\u30db\u30b9\u30c8\u306e OS \u57fa\u672c\u8a2d\u5b9a\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u540c\u69d8\u306e\u57fa\u672c\u8a2d\u5b9a\u3092\u6301\u3064\u3059\u3079\u3066\u306e\u4eee\u60f3\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306f\u3001\u5225\u306e\u30db\u30b9\u30c8\u3092\u9078\u629e\u3059\u308b\u524d\u306b\u307e\u305a\u3053\u306e\u30db\u30b9\u30c8\u306b\u5272\u308a\u5f53\u3066\u3089\u308c\u307e\u3059\u3002 message.update.resource.count=\u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u30ea\u30bd\u30fc\u30b9\u6570\u3092\u66f4\u65b0\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.update.ssl=\u5404\u30b3\u30f3\u30bd\u30fc\u30eb \u30d7\u30ed\u30ad\u30b7\u306e\u4eee\u60f3\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3067\u66f4\u65b0\u3059\u308b\u3001X.509 \u6e96\u62e0\u306e\u65b0\u3057\u3044 SSL \u8a3c\u660e\u66f8\u3092\u9001\u4fe1\u3057\u3066\u304f\u3060\u3055\u3044\: +message.update.ssl=\u5404\u30b3\u30f3\u30bd\u30fc\u30eb \u30d7\u30ed\u30ad\u30b7\u306e\u4eee\u60f3\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3067\u66f4\u65b0\u3059\u308b\u3001X.509 \u6e96\u62e0\u306e\u65b0\u3057\u3044 SSL \u8a3c\u660e\u66f8\u3092\u9001\u4fe1\u3057\u3066\u304f\u3060\u3055\u3044: message.validate.instance.name=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u540d\u306f 63 \u6587\u5b57\u4ee5\u5185\u3067\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002ASCII \u6587\u5b57\u306e a\uff5ez\u3001A\uff5eZ\u3001\u6570\u5b57\u306e 0\uff5e9\u3001\u304a\u3088\u3073\u30cf\u30a4\u30d5\u30f3\u306e\u307f\u3092\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002\u6587\u5b57\u3067\u59cb\u307e\u308a\u3001\u6587\u5b57\u307e\u305f\u306f\u6570\u5b57\u3067\u7d42\u308f\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 message.virtual.network.desc=\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u5c02\u7528\u4eee\u60f3\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3067\u3059\u3002\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8 \u30c9\u30e1\u30a4\u30f3\u306f VLAN \u5185\u306b\u914d\u7f6e\u3055\u308c\u3001\u30d1\u30d6\u30ea\u30c3\u30af \u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3078\u306e\u30a2\u30af\u30bb\u30b9\u306f\u3059\u3079\u3066\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc\u306b\u3088\u3063\u3066\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002 message.vm.create.template.confirm=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f5c\u6210\u3059\u308b\u3068 VM \u304c\u81ea\u52d5\u7684\u306b\u518d\u8d77\u52d5\u3055\u308c\u307e\u3059\u3002 @@ -1418,9 +1418,9 @@ message.Zone.creation.complete=\u30be\u30fc\u30f3\u304c\u4f5c\u6210\u3055\u308c\ message.zone.creation.complete.would.you.like.to.enable.this.zone=\u30be\u30fc\u30f3\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\u3002\u3053\u306e\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.zone.no.network.selection=\u9078\u629e\u3057\u305f\u30be\u30fc\u30f3\u3067\u306f\u3001\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u3092\u9078\u629e\u3067\u304d\u307e\u305b\u3093\u3002 message.zone.step.1.desc=\u30be\u30fc\u30f3\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30e2\u30c7\u30eb\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -message.zone.step.2.desc=\u00e6\u0096\u00b0\u00e3\u0081\u0097\u00e3\u0081\u0084Zone\u00e3\u0082\u0092\u00e8\u00bf\u00bd\u00e5\u008a\u00a0\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u009f\u00e3\u0082\u0081\u00e3\u0081\u00ab\u00e3\u0080\u0081\u00e6\u00ac\u00a1\u00e3\u0081\u00ae\u00e6\u0083 -message.zone.step.3.desc=\u00e6\u0096\u00b0\u00e3\u0081\u0097\u00e3\u0081\u0084Pod\u00e3\u0082\u0092\u00e8\u00bf\u00bd\u00e5\u008a\u00a0\u00e3\u0081\u0099\u00e3\u0082\u008b\u00e3\u0081\u009f\u00e3\u0082\u0081\u00e3\u0081\u00ab\u00e3\u0080\u0081\u00e6\u00ac\u00a1\u00e3\u0081\u00ae\u00e6\u0083 -message.zoneWizard.enable.local.storage=\u8b66\u544a\: \u3053\u306e\u30be\u30fc\u30f3\u306e\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u6709\u52b9\u306b\u3059\u308b\u5834\u5408\u306f\u3001\u30b7\u30b9\u30c6\u30e0 VM \u306e\u8d77\u52d5\u5834\u6240\u306b\u5fdc\u3058\u3066\u6b21\u306e\u64cd\u4f5c\u304c\u5fc5\u8981\u3067\u3059\u3002

    1. \u30b7\u30b9\u30c6\u30e0 VM \u3092\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3067\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u4f5c\u6210\u3057\u305f\u5f8c\u3067\u30be\u30fc\u30f3\u306b\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u7121\u52b9\u72b6\u614b\u306e\u30be\u30fc\u30f3\u3092\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u3082\u3042\u308a\u307e\u3059\u3002

    2. \u30b7\u30b9\u30c6\u30e0 VM \u3092\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8\u3067\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001system.vm.use.local.storage \u3092 true \u306b\u8a2d\u5b9a\u3057\u3066\u304b\u3089\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002


    \u7d9a\u884c\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.zone.step.2.desc=\u65b0\u3057\u3044\u30be\u30fc\u30f3\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.zone.step.3.desc=\u65b0\u3057\u3044\u30dd\u30c3\u30c9\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u6b21\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.zoneWizard.enable.local.storage=\u8b66\u544a: \u3053\u306e\u30be\u30fc\u30f3\u306e\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u6709\u52b9\u306b\u3059\u308b\u5834\u5408\u306f\u3001\u30b7\u30b9\u30c6\u30e0 VM \u306e\u8d77\u52d5\u5834\u6240\u306b\u5fdc\u3058\u3066\u6b21\u306e\u64cd\u4f5c\u304c\u5fc5\u8981\u3067\u3059\u3002

    1. \u30b7\u30b9\u30c6\u30e0 VM \u3092\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3067\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u4f5c\u6210\u3057\u305f\u5f8c\u3067\u30be\u30fc\u30f3\u306b\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u307e\u305f\u3001\u7121\u52b9\u72b6\u614b\u306e\u30be\u30fc\u30f3\u3092\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u3082\u3042\u308a\u307e\u3059\u3002

    2. \u30b7\u30b9\u30c6\u30e0 VM \u3092\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8\u3067\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001system.vm.use.local.storage \u3092 true \u306b\u8a2d\u5b9a\u3057\u3066\u304b\u3089\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002


    \u7d9a\u884c\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? mode=\u30e2\u30fc\u30c9 network.rate=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u901f\u5ea6 notification.reboot.instance=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u518d\u8d77\u52d5 diff --git a/ui/index.jsp b/ui/index.jsp index 8f31740a039..2992afb5c31 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -15,7 +15,7 @@ software distributed under the License is distributed on an KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---%> +--%><%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> From 6758d727a4b61e2e7a185c404cddf203876b560a Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Mon, 27 May 2013 09:05:04 +0200 Subject: [PATCH 135/412] debian: Package server.xml and tomcat6.conf This are symlinks to server-nonssl.xml and tomcat6-nonssl.conf, but they are required for starting the management server. Commit 2db7a4559e64e34b5c707157db240a1be322cb69 broke this. --- debian/cloudstack-management.install | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index 2cc3e925970..a1325cdb2b5 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -15,20 +15,22 @@ # specific language governing permissions and limitations # under the License. -/etc/cloudstack/management/server-ssl.xml /etc/cloudstack/management/catalina.policy /etc/cloudstack/management/catalina.properties /etc/cloudstack/management/cloudmanagementserver.keystore /etc/cloudstack/management/logging.properties /etc/cloudstack/management/commands.properties /etc/cloudstack/management/ehcache.xml -/etc/cloudstack/management/tomcat6-nonssl.conf /etc/cloudstack/management/componentContext.xml /etc/cloudstack/management/applicationContext.xml +/etc/cloudstack/management/server-ssl.xml /etc/cloudstack/management/server-nonssl.xml +/etc/cloudstack/management/server.xml /etc/cloudstack/management/classpath.conf /etc/cloudstack/management/db.properties /etc/cloudstack/management/tomcat6-ssl.conf +/etc/cloudstack/management/tomcat6-nonssl.conf +/etc/cloudstack/management/tomcat6.conf /etc/cloudstack/management/web.xml /etc/cloudstack/management/environment.properties /etc/cloudstack/management/nonossComponentContext.xml From a16b707250fde01f24c9ab6f11189b630dd70962 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Thu, 23 May 2013 20:15:35 +0900 Subject: [PATCH 136/412] CLOUDSTACK-2327: make cloud-setup-agent ovs aware Ovs brcompat will be obsolete, so if network.bridge.type was set to openvswitch, we'll use ovs command explicitly. Signed-off-by: Hiroaki KAWAI --- agent/bindir/cloud-setup-agent.in | 5 +- python/lib/cloudutils/globalEnv.py | 2 + python/lib/cloudutils/networkConfig.py | 14 ++++-- python/lib/cloudutils/serviceConfig.py | 66 +++++++++++++++++++++----- 4 files changed, 70 insertions(+), 17 deletions(-) diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in index 5e2ba09406d..d6f481db2e0 100755 --- a/agent/bindir/cloud-setup-agent.in +++ b/agent/bindir/cloud-setup-agent.in @@ -95,6 +95,9 @@ if __name__ == '__main__': parser.add_option("--prvNic", dest="prvNic", help="Private traffic interface") parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface") + old_config = configFileOps("@AGENTSYSCONFDIR@/agent.properties") + glbEnv.bridgeType = old_config.getEntry("network.bridge.type").lower() + (options, args) = parser.parse_args() if options.auto is None: userInputs = getUserInputs() @@ -104,7 +107,7 @@ if __name__ == '__main__': glbEnv.pod = userInputs[3] glbEnv.cluster = userInputs[4] #generate UUID - glbEnv.uuid = configFileOps("@AGENTSYSCONFDIR@/agent.properties").getEntry("guid") + glbEnv.uuid = old_config.getEntry("guid") if glbEnv.uuid == "": glbEnv.uuid = bash("uuidgen").getStdout() else: diff --git a/python/lib/cloudutils/globalEnv.py b/python/lib/cloudutils/globalEnv.py index 94d981f4dab..867aa17670d 100644 --- a/python/lib/cloudutils/globalEnv.py +++ b/python/lib/cloudutils/globalEnv.py @@ -40,3 +40,5 @@ class globalEnv: self.privateNet = "cloudbr0" #distribution self.distribution = None + # bridgeType + self.bridgeType = "native" diff --git a/python/lib/cloudutils/networkConfig.py b/python/lib/cloudutils/networkConfig.py index b6b729a4fec..9e456e970be 100644 --- a/python/lib/cloudutils/networkConfig.py +++ b/python/lib/cloudutils/networkConfig.py @@ -19,6 +19,7 @@ from cloudException import CloudRuntimeException, CloudInternalException import logging import os import re +import subprocess class networkConfig: class devInfo: @@ -85,15 +86,22 @@ class networkConfig: @staticmethod def isNetworkDev(devName): - return os.path.exists("/sys/class/net/%s"%devName) + return os.path.exists("/sys/class/net/%s" % devName) @staticmethod def isBridgePort(devName): - return os.path.exists("/sys/class/net/%s/brport"%devName) + return os.path.exists("/sys/class/net/%s/brport" % devName) @staticmethod def isBridge(devName): - return os.path.exists("/sys/class/net/%s/bridge"%devName) + return os.path.exists("/sys/class/net/%s/bridge" % devName) + + @staticmethod + def isOvsBridge(devName): + try: + return 0==subprocess.check_call(("ovs-vsctl", "br-exists", devName)) + except subprocess.CalledProcessError: + return False @staticmethod def getBridge(devName): diff --git a/python/lib/cloudutils/serviceConfig.py b/python/lib/cloudutils/serviceConfig.py index 1e32d0f3b0f..d129e00c45b 100755 --- a/python/lib/cloudutils/serviceConfig.py +++ b/python/lib/cloudutils/serviceConfig.py @@ -94,8 +94,10 @@ class networkConfigBase: if not self.netcfg.isNetworkDev(br): logging.debug("%s is not a network device, is it down?"%br) return False - if not self.netcfg.isBridge(br): - raise CloudInternalException("%s is not a bridge"%br) + if self.syscfg.env.bridgeType == "openvswitch" and not self.netcfg.isOvsBridge(br): + raise CloudInternalException("%s is not an openvswitch bridge" % br) + if self.syscfg.env.bridgeType == "native" and not self.netcfg.isBridge(br): + raise CloudInternalException("%s is not a bridge" % br) preCfged = True return preCfged @@ -153,11 +155,28 @@ class networkConfigUbuntu(serviceCfgBase, networkConfigBase): match = re.match("^ *iface %s.*"%dev.name, line) if match is not None: dev.method = self.getNetworkMethod(match.group(0)) - bridgeCfg = "\niface %s inet manual\n \ - auto %s\n \ - iface %s inet %s\n \ - bridge_ports %s\n"%(dev.name, br, br, dev.method, dev.name) cfo = configFileOps(self.netCfgFile, self) + if self.syscfg.env.bridgeType == "openvswitch": + bridgeCfg = "\n".join(("", + "iface {device} inet manual", + " ovs_type OVSPort", + " ovs_bridge {bridge}", + "", + "auto {bridge}", + "allow-ovs {bridge}", + "iface {bridge} inet {device_method}", + " ovs_type OVSBridge", + " ovs_ports {device}", + "")).format(bridge=br, device=dev.name, device_method=dev.method) + cfo.replace_line("^ *auto %s.*" % dev.name, + "allow-{bridge} {device}".format(bridge=br, device=dev.name)) + elif self.syscfg.env.bridgeType == "native": + bridgeCfg = "\niface %s inet manual\n \ + auto %s\n \ + iface %s inet %s\n \ + bridge_ports %s\n"%(dev.name, br, br, dev.method, dev.name) + else: + raise CloudInternalException("Unknown network.bridge.type %s" % self.syscfg.env.bridgeType) cfo.replace_line("^ *iface %s.*"%dev.name, bridgeCfg) def addDev(self, br, dev): @@ -193,8 +212,9 @@ class networkConfigUbuntu(serviceCfgBase, networkConfigBase): self.syscfg.svo.stopService("network-manager") self.syscfg.svo.disableService("network-manager") - if not bash("ifup %s"%self.brName).isSuccess(): - raise CloudInternalException("Can't start network:%s"%self.brName, bash.getErrMsg(self)) + ifup_op = bash("ifup %s"%self.brName) + if not ifup_op.isSuccess(): + raise CloudInternalException("Can't start network:%s %s" % (self.brName, ifup_op.getErrMsg())) self.syscfg.env.nics.append(self.brName) self.syscfg.env.nics.append(self.brName) @@ -222,8 +242,8 @@ class networkConfigRedhat(serviceCfgBase, networkConfigBase): networkConfigBase.__init__(self, syscfg) def writeToCfgFile(self, brName, dev): - self.devCfgFile = "/etc/sysconfig/network-scripts/ifcfg-%s"%dev.name - self.brCfgFile = "/etc/sysconfig/network-scripts/ifcfg-%s"%brName + self.devCfgFile = "/etc/sysconfig/network-scripts/ifcfg-%s" % dev.name + self.brCfgFile = "/etc/sysconfig/network-scripts/ifcfg-%s" % brName isDevExist = os.path.exists(self.devCfgFile) isBrExist = os.path.exists(self.brCfgFile) @@ -241,7 +261,7 @@ class networkConfigRedhat(serviceCfgBase, networkConfigBase): def addBridge(self, brName, dev): - bash("ifdown %s"%dev.name) + bash("ifdown %s" % dev.name) if not os.path.exists(self.brCfgFile): shutil.copy(self.devCfgFile, self.brCfgFile) @@ -250,14 +270,34 @@ class networkConfigRedhat(serviceCfgBase, networkConfigBase): cfo = configFileOps(self.devCfgFile, self) cfo.addEntry("NM_CONTROLLED", "no") cfo.addEntry("ONBOOT", "yes") - cfo.addEntry("BRIDGE", brName) + if self.syscfg.env.bridgeType == "openvswitch": + if cfo.getEntry("IPADDR"): + cfo.rmEntry("IPADDR", cfo.getEntry("IPADDR")) + cfo.addEntry("DEVICETYPE", "ovs") + cfo.addEntry("TYPE", "OVSPort") + cfo.addEntry("OVS_BRIDGE", brName) + elif self.syscfg.env.bridgeType == "native": + cfo.addEntry("BRIDGE", brName) + else: + raise CloudInternalException("Unknown network.bridge.type %s" % self.syscfg.env.bridgeType) cfo.save() cfo = configFileOps(self.brCfgFile, self) cfo.addEntry("NM_CONTROLLED", "no") cfo.addEntry("ONBOOT", "yes") cfo.addEntry("DEVICE", brName) - cfo.addEntry("TYPE", "Bridge") + if self.syscfg.env.bridgeType == "openvswitch": + if cfo.getEntry("HWADDR"): + cfo.rmEntry("HWADDR", cfo.getEntry("HWADDR")) + if cfo.getEntry("UUID"): + cfo.rmEntry("UUID", cfo.getEntry("UUID")) + cfo.addEntry("STP", "yes") + cfo.addEntry("DEVICETYPE", "ovs") + cfo.addEntry("TYPE", "OVSBridge") + elif self.syscfg.env.bridgeType == "native": + cfo.addEntry("TYPE", "Bridge") + else: + raise CloudInternalException("Unknown network.bridge.type %s" % self.syscfg.env.bridgeType) cfo.save() def config(self): From cc2866c1ea2c778d434e416f8a7183bd3c10c518 Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Mon, 27 May 2013 15:47:56 +0800 Subject: [PATCH 137/412] CLOUDSTACK-2686 Upload volume to project is not shown in project storage view --- .../api/command/user/volume/UploadVolumeCmd.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java index 3b00ba0d4bb..fea3e04d249 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java @@ -22,7 +22,9 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.BaseCmd.CommandType; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.log4j.Logger; @@ -72,6 +74,10 @@ public class UploadVolumeCmd extends BaseAsyncCmd { description="Image store uuid") private String imageStoreUuid; + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, + description="Upload volume for the project") + private Long projectId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -135,7 +141,7 @@ public class UploadVolumeCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return UserContext.current().getCaller().getId(); } From 08ac8fb4687fb14cf9524a022527a64e033be9ab Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Mon, 27 May 2013 14:02:17 +0530 Subject: [PATCH 138/412] CLOUDSTACK-2688 Add DB index to Alert.last_sent column to speed up listAlertsCmd. --- .../cloud/upgrade/dao/Upgrade410to420.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index e7f47331eab..16ab05cea06 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -28,6 +28,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import com.cloud.network.vpc.NetworkACL; @@ -77,6 +79,34 @@ public class Upgrade410to420 implements DbUpgrade { updateNetworksForPrivateGateways(conn); removeFirewallServiceFromSharedNetworkOfferingWithSGService(conn); fix22xKVMSnapshots(conn); + addIndexForAlert(conn); + } + + private void addIndexForAlert(Connection conn) { + + //First drop if it exists. (Due to patches shipped to customers some will have the index and some wont.) + List indexList = new ArrayList(); + s_logger.debug("Dropping index i_alert__last_sent if it exists"); + indexList.add("i_alert__last_sent"); + DbUpgradeUtils.dropKeysIfExist(conn, "alert", indexList, false); + + //Now add index. + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("ALTER TABLE `cloud`.`alert` ADD INDEX `i_alert__last_sent`(`last_sent`)"); + pstmt.executeUpdate(); + s_logger.debug("Added index i_alert__last_sent for table alert"); + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to add index i_alert__last_sent to alert table for the column last_sent", e); + } finally { + try { + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } private void updateSystemVmTemplates(Connection conn) { From a688d631047ae7dc70a4c4d8c5330dec1dc1cd95 Mon Sep 17 00:00:00 2001 From: Nitin Mehta Date: Mon, 27 May 2013 14:16:16 +0530 Subject: [PATCH 139/412] CLOUDSTACK-2689 Create volume after upgrade from snapshot taken in 2.2.14 deletes the snapshot physically from sec. storage. Added DB upgrade sql to make swift_id NULL if they are set to 0 --- setup/db/db/schema-410to420.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 435bb83d3c9..1c9a8c1bddc 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1709,6 +1709,8 @@ update `cloud`.`vpc_gateways` set network_acl_id = 2; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'VpcManager', 'blacklisted.routes', NULL, 'Routes that are blacklisted, can not be used for Static Routes creation for the VPC Private Gateway'); +UPDATE `cloud`.`snapshots` set swift_id=null where swift_id=0; + -- Re-enable foreign key checking, at the end of the upgrade path SET foreign_key_checks = 1; From fac59a742913e347fb090d46fe75d80c3fda122f Mon Sep 17 00:00:00 2001 From: Milamber Date: Mon, 27 May 2013 09:56:52 +0100 Subject: [PATCH 140/412] Update L10N strings from Transifex to repo --- .../classes/resources/messages_ar.properties | 2 +- .../classes/resources/messages_ca.properties | 1 - .../resources/messages_de_DE.properties | 1 - .../classes/resources/messages_es.properties | 1 - .../resources/messages_fr_FR.properties | 5 +- .../resources/messages_it_IT.properties | 191 +++++++++++++++++- .../resources/messages_ko_KR.properties | 4 +- .../resources/messages_nb_NO.properties | 1 - .../resources/messages_pt_BR.properties | 2 +- .../resources/messages_ru_RU.properties | 4 +- .../resources/messages_zh_CN.properties | 10 +- 11 files changed, 211 insertions(+), 11 deletions(-) diff --git a/client/WEB-INF/classes/resources/messages_ar.properties b/client/WEB-INF/classes/resources/messages_ar.properties index 4d3011b5a6c..5b3afea929d 100644 --- a/client/WEB-INF/classes/resources/messages_ar.properties +++ b/client/WEB-INF/classes/resources/messages_ar.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=\u062a\u063a\u064a\u0631 \u062e\u0635\u0627\u0626\u0635 \u0627\u0644\u0639\u0646\u0635\u0631 confirm.enable.s3=\u0641\u0636\u0644\u0627 \u0642\u0645 \u0628\u062a\u0639\u0628\u0626\u0629 \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0627\u0644\u0642\u0627\u062f\u0645\u0629 \u0644\u062a\u0645\u0643\u064a\u0646 \u0627\u0644\u062a\u062e\u0632\u064a\u0646 S3 \u0644\u0644\u0630\u0627\u0643\u0631\u0629 \u0627\u0644\u062b\u0627\u0646\u0648\u064a\u0629. instances.actions.reboot.label=\u0625\u0639\u0627\u062f\u0629 \u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u0646\u0645\u0648\u0630\u062c @@ -217,6 +216,7 @@ label.zone.step.3.title=\u0627\u0644\u062e\u0637\u0648\u0629 3 \\\: \u0639\u0644 label.zone.step.4.title=\u0627\u0644\u062e\u0637\u0648\u0629 4 \\\: <\u0642\u0648\u064a> \u0625\u0636\u0627\u0641\u0629 \u0645\u062c\u0645\u0648\u0639\u0629 IP <\\\u0642\u0648\u064a> label.zone.wide=\u0645\u0646\u0637\u0642\u0629 \u0648\u0627\u0633\u0639\u0629 label.zoneWizard.trafficType.guest=\u0627\u0644\u0636\u064a\u0641 \\\: \u0627\u0644\u062d\u0631\u0643\u0629 \u0628\u064a\u0646 \u0627\u0644\u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u0625\u0641\u062a\u0631\u0627\u0636\u064a\u0629 \u0644\u0644\u0645\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0646\u0647\u0627\u0626\u064a. +label.zoneWizard.trafficType.management=\u0625\u062f\u0627\u0631\u0629\\\: \u0627\u0644\u062d\u0631\u0643\u0629 \u0628\u064a\u0646 \u0627\u0644\u0645\u0648\u0627\u0631\u062f \u0627\u0644\u062f\u0627\u062e\u0644\u064a\u0629 \u0644 \u0643\u0644\u0627\u0648\u062f \u0633\u062a\u0627\u0643 \u060c \u0645\u062a\u0636\u0645\u0646\u0629 \u0623\u064a \u062c\u0632\u0621 \u064a\u062a\u0635\u0644 \u0628\u062e\u0627\u062f\u0645\\\u0633\u064a\u0631\u0641\u0631 \u0627\u0644\u0625\u062f\u0627\u0631\u0629 \u060c \u0645\u062b\u0644 \u0627\u0644\u0645\u0636\u064a\u0641\u0627\u062a \u0648 \u0623\u0646\u0638\u0645\u0629 \u0643\u0644\u0627\u0648\u062f \u0633\u062a\u0627\u0643 \u0627\u0644\u0625\u0641\u062a\u0631\u0627\u0636\u064a\u0629. label.zoneWizard.trafficType.public=\u0627\u0644\u0639\u0627\u0645\u0629 \\\: \u0627\u0644\u0645\u0631\u0648\u0631 \u0628\u064a\u0646 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0627\u0644\u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u0638\u0627\u0647\u0631\u064a\u0629 \u0641\u064a \u0627\u0644\u0633\u062d\u0627\u0628\u0629. label.zoneWizard.trafficType.storage=\u0627\u0644\u062a\u062e\u0632\u064a\u0646 \\\: \u0627\u0644\u0645\u0631\u0648\u0631 \u0628\u064a\u0646 \u0645\u0644\u0642\u0645\u0627\u062a \u0627\u0644\u062a\u062e\u0632\u064a\u0646 \u0627\u0644\u0627\u0628\u062a\u062f\u0627\u0626\u064a\u0629 \u0648\u0627\u0644\u062b\u0627\u0646\u0648\u064a\u0629\u060c \u0645\u062b\u0644 \u0642\u0648\u0627\u0644\u0628 VM \u0648\u0627\u0644\u0644\u0642\u0637\u0627\u062a message.acquire.new.ip.vpc=\u064a\u0631\u062c\u0649 \u0627\u0644\u062a\u0623\u0643\u064a\u062f \u0628\u0623\u0646\u0643 \u062a\u0631\u063a\u0628 \u0641\u064a \u0627\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u0628\u0648\u0631\u062a\u0648\u0643\u0648\u0644 \u0625\u0646\u062a\u0631\u0646\u062a \u062c\u062f\u064a\u062f \u0644\u0647\u0630\u0627 \u0627\u0644\u062d\u0627\u0633\u0648\u0628 \u0627\u0644\u0625\u0641\u062a\u0631\u0627\u0636\u064a. diff --git a/client/WEB-INF/classes/resources/messages_ca.properties b/client/WEB-INF/classes/resources/messages_ca.properties index 4e66083dbd5..2d8e953419f 100644 --- a/client/WEB-INF/classes/resources/messages_ca.properties +++ b/client/WEB-INF/classes/resources/messages_ca.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - confirm.enable.swift=Si us plau ompliu la seg\u00fcent informaci\u00f3 per habilitar el suport per a Swift error.installWizard.message=Quelcom ha fallat, vost\u00e8 pot tornar enrere i corregir els errors detalls suggerime error.password.not.match=Els camps de contrasenya no coincideixen diff --git a/client/WEB-INF/classes/resources/messages_de_DE.properties b/client/WEB-INF/classes/resources/messages_de_DE.properties index ca87323cc77..3c0c8deaabd 100644 --- a/client/WEB-INF/classes/resources/messages_de_DE.properties +++ b/client/WEB-INF/classes/resources/messages_de_DE.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - error.installWizard.message=Ein Fehler ist aufgetreten; Sie k\u00f6nnen zur\u00fcckgehen und den Fehler korregieren error.login=Ihr Benutzername / Passwort stimmt nicht mit uneren unseren Aufzeichnungen \u00fcberein. error.session.expired=Ihre Sitzung ist abgelaufen. diff --git a/client/WEB-INF/classes/resources/messages_es.properties b/client/WEB-INF/classes/resources/messages_es.properties index 16cfc1cda49..86eb596689c 100644 --- a/client/WEB-INF/classes/resources/messages_es.properties +++ b/client/WEB-INF/classes/resources/messages_es.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - error.installWizard.message=Algo salio mal, debes ir para atr\u00e1s y corregir los error. error.login=Su nombre de usuario / contrase\u00c3\u00b1a no coincide con nuestros registros. error.mgmt.server.inaccessible=El Servidor de Gesti\u00c3\u00b3n es inaccesible. Por favor, int\u00c3\u00a9ntelo de nuevo m\u00c3\u00a1s tarde. diff --git a/client/WEB-INF/classes/resources/messages_fr_FR.properties b/client/WEB-INF/classes/resources/messages_fr_FR.properties index 8be438a11c1..33ffcfc4714 100644 --- a/client/WEB-INF/classes/resources/messages_fr_FR.properties +++ b/client/WEB-INF/classes/resources/messages_fr_FR.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=Propri\u00e9t\u00e9s de l\\'\u00e9l\u00e9ment modifi\u00e9es confirm.enable.s3=Remplir les informations suivantes pour activer le support de stockage secondaire S3 confirm.enable.swift=Remplir les informations suivantes pour activer Swift @@ -93,6 +92,7 @@ label.action.delete.load.balancer=Supprimer la r\u00e8gle de r\u00e9partition de label.action.delete.network.processing=Suppression du r\u00e9seau... label.action.delete.network=Supprimer le r\u00e9seau label.action.delete.nexusVswitch=Supprimer le Nexus 1000v +label.action.delete.nic=Supprimer carte NIC label.action.delete.physical.network=Supprimer le r\u00e9seau physique label.action.delete.pod.processing=Suppression du pod... label.action.delete.pod=Supprimer le Pod @@ -1200,6 +1200,7 @@ message.action.delete.ISO.for.all.zones=L\\'ISO est utilis\u00e9 par toutes les message.action.delete.ISO=\u00cates-vous s\u00fbr que vous souhaitez supprimer cette ISO. message.action.delete.network=\u00cates-vous s\u00fbr que vous voulez supprimer ce r\u00e9seau. message.action.delete.nexusVswitch=Confirmer la suppession de ce Nexus 1000v +message.action.delete.nic=Veuillez confirmer que vous souhaitez supprimer cette carte NIC, ce qui supprimera \u00e9galement le r\u00e9seau associ\u00e9 sur la machine virtuelle. message.action.delete.physical.network=Confirmer la suppression du r\u00e9seau physique message.action.delete.pod=\u00cates-vous s\u00fbr que vous souhaitez supprimer ce pod. message.action.delete.primary.storage=\u00cates-vous s\u00fbr que vous voulez supprimer ce stockage principal. @@ -1412,6 +1413,7 @@ message.migrate.router.confirm=Confirmer la migration du routeur vers \: message.migrate.systemvm.confirm=Confirmer la migration de la VM syst\u00e8me vers \: message.migrate.volume=Confirmer la migration du volume vers un autre stockage principal. message.new.user=Renseigner les informations suivantes pour ajouter un nouveau compte utilisateur +message.no.affinity.groups=Vous n\\'avez pas de groupes d\\'affinit\u00e9. Continuer vers la prochaine \u00e9tape. message.no.network.support.configuration.not.true=Il n\\'y a pas de zone avec la fonction groupe de s\u00e9curit\u00e9 active. D\u00e8s lors, pas de fonction r\u00e9seau suppl\u00e9mentaires disponibles. Continuer \u00e0 l\\'\u00e9tape 5. message.no.network.support=S\u00e9lectionnez l\\'hyperviseur. vSphere, n\\'a pas de fonctionnalit\u00e9s suppl\u00e9mentaires pour le r\u00e9seau. Continuez \u00e0 l\\'\u00e9tape 5. message.no.projects.adminOnly=Vous n\\'avez pas de projet.
    Contacter votre administrateur pour ajouter un projet. @@ -1444,6 +1446,7 @@ message.restart.mgmt.usage.server=Red\u00e9marrer le ou les serveur(s) de gestio message.restart.network=Tous les services fournit par ce routeur virtuel vont \u00eatre interrompus. Confirmer le red\u00e9marrage de ce routeur. message.restart.vpc=Confirmer le red\u00e9marrage du VPC message.security.group.usage=(Utilisez Ctrl-clic pour s\u00e9lectionner les groupes de s\u00e9curit\u00e9 vis\u00e9s) +message.select.affinity.groups=S\u00e9lectionner les groupes d\\'affinit\u00e9 qui appartiendront \u00e0 cette machine virtuelle \: message.select.a.zone=Une zone correspond typiquement \u00e0 un seul centre de donn\u00e9es. Des zones multiples peuvent permettre de rendre votre cloud plus fiable en apportant une isolation physique et de la redondance. message.select.instance=S\u00e9lectionner une instance. message.select.iso=S\u00e9lectionner un ISO pour votre nouvelle instance virtuelle. diff --git a/client/WEB-INF/classes/resources/messages_it_IT.properties b/client/WEB-INF/classes/resources/messages_it_IT.properties index 78323b02578..6ae5bd85376 100644 --- a/client/WEB-INF/classes/resources/messages_it_IT.properties +++ b/client/WEB-INF/classes/resources/messages_it_IT.properties @@ -15,32 +15,207 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=Elementi delle propriet\u00e0 modificati confirm.enable.s3=Si prega di inserire i valori richiesti per abilitare il supporto per il Secondary Storage di tipo S3 confirm.enable.swift=Si prega di inserire i valori richiesti per abilitare il supporto per Swift error.could.not.enable.zone=Impossibile abilitare la zona error.installWizard.message=E\\' stato rilevato un errore\: tornare agli step precedenti e correggere gli errori error.invalid.username.password=Username o Password non valida +error.login=Le credenziali fornite per username/password non corrispondono a quelle nei nostri sistemi. +error.menu.select=Impossibile effettuare operazioni senza aver selezionato alcun elemento. +error.mgmt.server.inaccessible=Impossibile accedere al Management Server. Si prega di riprovare pi\u00f9 tardi. error.password.not.match=I campi password non corrispondono error.please.specify.physical.network.tags=Le offerte di rete non sono disponibili se non si specificano tag per questa rete fisica. +error.session.expired=La sessione \u00e8 scaduta. error.something.went.wrong.please.correct.the.following=E\\' stato rilevato un errore; si prega di correggere quanto indicato di seguito error.unable.to.reach.management.server=Impossibile raggiungere il Management Server +error.unresolved.internet.name=Il tuo nome internet non pu\u00f2 essere risolto. +extractable=Estraibile +force.delete.domain.warning=Attenzione\: La scelta di questa opzione provocher\u00e0 la rimozione di tutti i sotto domini e agli account associati e alle loro risorse. +force.remove.host.warning=Attenzione\: La scelta di questa opzione provocher\u00e0 l\\'arresto forzato di tutte le virtual machine da parte di CloudStack prima di rimuovere questo host dal cluster. +force.stop.instance.warning=Attenzione\: Forzare un arresto su questa instanza dovrebbe essere l\\'ultima opzione. C\\'\u00e8 il rischio di perdita di dati e di un comportamento inconsistente dello stato della virtual machine. +ICMP.code=Codice ICMP +ICMP.type=Tipo ICMP +image.directory=Directory Immagine instances.actions.reboot.label=Riavviare una instanza label.accept.project.invitation=Accettare un invito ad un progetto +label.account=Account label.account.and.security.group=Account, Security group +label.account.id=ID dell\\'Account +label.account.name=Nome Account +label.account.specific=Specifico dell\\'Account +label.accounts=Utenti +label.acquire.new.ip=Acquisizione nuovo indirizzo IP +label.action.attach.disk=Collegamento di un Disco +label.action.attach.disk.processing=Collegamento Disco in corso... +label.action.attach.iso=Collegamento di una immagine ISO +label.action.attach.iso.processing=Collegamento immagine ISO in corso... +label.action.cancel.maintenance.mode=Annullamento dello stato di Maintenance Mode +label.action.cancel.maintenance.mode.processing=Cancellazione dello stato Maintenance Mode in corso... +label.action.change.password=Modifica della Password +label.action.change.service=Modificare Servizio +label.action.change.service.processing=Modifica del Servizio in corso... +label.action.copy.ISO=Copia della immagine ISO +label.action.copy.ISO.processing=Copia immagine ISO in corso... +label.action.copy.template=Copia di un Template +label.action.copy.template.processing=Copia Template in corso... +label.action.create.template=Creazione Template +label.action.create.template.from.vm=Creazione Template da una VM +label.action.create.template.from.volume=Creazione Template da un Volume +label.action.create.template.processing=Creazione Template in corso... +label.action.create.vm=Creazione VM +label.action.create.vm.processing=Creazione VM in corso... +label.action.create.volume=Creazione Volume +label.action.create.volume.processing=Creazione Volume in corso... +label.action.delete.account=Cancellazione account +label.action.delete.account.processing=Cancellazione account in corso.... +label.action.delete.cluster=Cancellazione Cluster +label.action.delete.cluster.processing=Cancellazione Cluster in corso.... +label.action.delete.disk.offering=Cancellazione Offerta Disco +label.action.delete.disk.offering.processing=Cancellazione Offerta Disco in corso.... +label.action.delete.domain=Cancellazione Dominio +label.action.delete.domain.processing=Cancellazione Dominio in corso.... +label.action.delete.firewall=Cancellazione regola firewall +label.action.delete.firewall.processing=Cancellazione Firewall in corso.... +label.action.delete.IP.range=Cancellazione intervallo indirizzi IP +label.action.delete.IP.range.processing=Cancellazione intervallo indirizzi IP in corso.... +label.action.delete.ISO=Cancellazione immagine ISO +label.action.delete.ISO.processing=Cancellazione immagine ISO in corso.... +label.action.delete.load.balancer=Cancellazione regola load balancer +label.action.delete.load.balancer.processing=Cancellazione Load Balancer in corso.... +label.action.delete.network=Cancellazione Rete +label.action.delete.network.processing=Cancellazione Rete in corso.... label.action.delete.nexusVswitch=Cancellare Nexus 1000v label.action.delete.physical.network=Cancellazione di una rete fisica +label.action.delete.pod=Cancellazione Pod +label.action.delete.pod.processing=Cancellazione Pod in corso.... +label.action.delete.primary.storage=Cancellazione Storage Primario +label.action.delete.primary.storage.processing=Cancellazione Storage Primario in corso.... +label.action.delete.secondary.storage=Cancellazione Storage Secondario +label.action.delete.secondary.storage.processing=Cancellazione Storage Secondario in corso.... +label.action.delete.security.group=Cancellazione Security Group +label.action.delete.security.group.processing=Cancellazione Security Group in corso.... +label.action.delete.service.offering=Cancellazione Offerta di Servizio +label.action.delete.service.offering.processing=Cancellazione Offerta di Servizio in corso.... +label.action.delete.snapshot=Cancellazione Snapshot +label.action.delete.snapshot.processing=Cancellazione Snapshot in corso.... label.action.delete.system.service.offering=Cancellare Offerta di Servizio di Sistema +label.action.delete.template=Cancellazione Template +label.action.delete.template.processing=Cancellazione Template in corso.... +label.action.delete.user=Cancellazione Utente +label.action.delete.user.processing=Cancellazione Utente in corso.... +label.action.delete.volume=Cancellazione Volume +label.action.delete.volume.processing=Cancellazione Volume in corso.... +label.action.delete.zone=Cancellazione Zona +label.action.delete.zone.processing=Cancellazione Zona in corso.... +label.action.destroy.instance.processing=Rimozione Instanza in corso.... +label.action.destroy.instance=Rimozione instanza +label.action.destroy.systemvm.processing=Rimozione VM di Sistema in corso.... +label.action.destroy.systemvm=Rimozione VM di sistema +label.action.detach.disk.processing=Scollegamento Disco in corso.... +label.action.detach.disk=Scollegamento di un Disco +label.action.detach.iso.processing=Scollegamento immagine ISO in corso.... +label.action.detach.iso=Scollegamento immagine ISO +label.action.disable.account=Disabilitazione account +label.action.disable.account.processing=Disabilitazione account in corso.... +label.action.disable.cluster=Disabilitazione Cluster +label.action.disable.cluster.processing=Disabilitazione Cluster in corso.... label.action.disable.nexusVswitch=Disabilitare Nexus 1000v label.action.disable.physical.network=Disabilitare la rete fisica +label.action.disable.pod=Disabilitazione Pod +label.action.disable.pod.processing=Disabilitazione Pod in corso.... +label.action.disable.static.NAT=Disabilitazione NAT Statico +label.action.disable.static.NAT.processing=Disabilitazione NAT Statico in corso.... +label.action.disable.user=Disabilitazione Utente +label.action.disable.user.processing=Disabilitazione Utente in corso.... +label.action.disable.zone=Disabilitazione Zona +label.action.disable.zone.processing=Disabilitazione Zona in corso.... +label.action.download.ISO=Download immagine ISO +label.action.download.template=Download Template +label.action.download.volume=Download Volume +label.action.download.volume.processing=Download Volume in corso.... +label.action.edit.account=Modifica account +label.action.edit.disk.offering=Modifica Offerta Disco +label.action.edit.domain=Modifica Dominio +label.action.edit.global.setting=Modifica Impostazioni Globali +label.action.edit.host=Modifica Host +label.action.edit.instance=Modifica Instanza +label.action.edit.ISO=Modifica immagine ISO +label.action.edit.network=Modifica Rete +label.action.edit.network.offering=Modifica Offerta di Rete +label.action.edit.network.processing=Modifica Rete in corso.... +label.action.edit.pod=Modifica Pod +label.action.edit.primary.storage=Modifica Storage Primario +label.action.edit.resource.limits=Modifica Limiti delle Risorse +label.action.edit.service.offering=Modifica Offerta di Servizio +label.action.edit.template=Modifica Template +label.action.edit.user=Modifica Utente +label.action.edit.zone=Modifica Zona +label.action.enable.account=Abilitazione account +label.action.enable.account.processing=Abilitazione account in corso.... +label.action.enable.cluster=Abilitazione Cluster +label.action.enable.cluster.processing=Abilitazione Cluster in corso.... +label.action.enable.maintenance.mode=Abilitazione dello stato Maintenance Mode +label.action.enable.maintenance.mode.processing=Abilitazione dello stato Maintenance Mode in corso.... label.action.enable.nexusVswitch=Abilitare Nexus 1000v label.action.enable.physical.network=Abilitare la rete fisica +label.action.enable.pod=Abilitazione Pod +label.action.enable.pod.processing=Abilitazione Pod in corso.... +label.action.enable.static.NAT=Abilitazione NAT Statico +label.action.enable.static.NAT.processing=Abilitazione NAT Statico in corso.... +label.action.enable.user=Abilitazione Utente +label.action.enable.user.processing=Abilitazione Utente in corso.... +label.action.enable.zone=Abilitazione Zona +label.action.enable.zone.processing=Abilitazione Zona in corso.... +label.action.force.reconnect.processing=Riconnessione in corso.... +label.action.generate.keys=Generazione Chiavi +label.action.generate.keys.processing=Generazione Chiavi in corso.... label.action.list.nexusVswitch=Elencare Nexus 1000v +label.action.lock.account=Blocco di un account +label.action.lock.account.processing=Blocco account in corso.... +label.action.manage.cluster=Gestione Cluster +label.action.manage.cluster.processing=Gestione Cluster in corso.... +label.action.migrate.instance=Migrazione Instanza +label.action.migrate.instance.processing=Migrazione Instanza in corso.... +label.action.migrate.router=Migrazione Router label.action.migrate.router.processing=Migrazione Router... +label.action.migrate.systemvm=Migrazione VM di Systema +label.action.migrate.systemvm.processing=Migrazione VM di Sistema in corso.... +label.action.reboot.instance.processing=Riavvio Instanza in corso.... +label.action.reboot.instance=Riavvio Instanza +label.action.reboot.router.processing=Riavvio Router in corso.... +label.action.reboot.router=Riavvio Router +label.action.reboot.systemvm.processing=Riavvio VM di Sistema in corso.... +label.action.reboot.systemvm=Riavvio VM di Sistema label.action.register.iso=Registrare una ISO label.action.register.template=Registrare un template +label.action.release.ip.processing=Rilascio indirizzo IP in corso.... +label.action.release.ip=Rilascio indirizzo IP +label.action.remove.host.processing=Rimozione Host in corso.... +label.action.remove.host=Rimozione Host +label.action.reset.password.processing=Reset della Password in corso.... +label.action.reset.password=Reset Password +label.action.resize.volume.processing=Ridimensionamento Volume in corso.... +label.action.resize.volume=Ridimensionamento Volume +label.action.restore.instance.processing=Restore dell\\'Instanza in corso.... +label.action.restore.instance=Restore Instanza +label.actions=Azioni +label.action.start.instance=Avvio Instanza +label.action.start.instance.processing=Avvio Instanza in corso.... +label.action.start.router=Avvio Router +label.action.start.router.processing=Avvio Router in corso.... +label.action.start.systemvm=Avvio VM di Sistema +label.action.start.systemvm.processing=Avvio VM di Sistema in corso.... +label.action.stop.instance=Arresto Instanza +label.action.stop.instance.processing=Arresto Instanza in corso.... +label.action.stop.router=Arresto Router +label.action.stop.router.processing=Arresto Router in corso.... +label.action.stop.systemvm=Arresto VM di Sistema +label.action.stop.systemvm.processing=Arresto VM di Sistema in corso.... +label.action.update.OS.preference=Aggiornamento Preferenze OS +label.action.update.OS.preference.processing=Aggiornamento preferenze OS in corso.... label.activate.project=Attivare il Progetto +label.active.sessions=Sessioni Attive label.add.accounts=Aggiungere utenti label.add.accounts.to=Aggiungere utenti a label.add.account.to.project=Aggiungere account al progetto @@ -49,6 +224,7 @@ label.add.compute.offering=Aggiungere una offerta computazionale label.add.egress.rule=Aggiungere una regola d\\'uscita label.add.F5.device=Aggiungere device F5 label.add.guest.network=Aggiungere una rete guest +label.additional.networks=Network Aggiuntivi label.add.netScaler.device=Aggiungere device Netscaler label.add.network.ACL=Aggiungere le ACL di rete label.add.network.offering=Aggiungere offerta di rete @@ -76,22 +252,32 @@ label.add.vpn.customer.gateway=Aggiungere Gateway VPN del Cliente label.add.VPN.gateway=Aggiungere un Gateway VPN label.add.vpn.user=Aggiungere utente VPN label.advanced=Avanzato +label.advanced.search=Ricerca Avanzata label.agent.password=Password per l\\'Agent label.agent.username=Username per l\\'Agent label.allocated=Allocato label.apply=Applicare label.associated.network=Rete Associata +label.available.public.ips=Indirizzi IP Pubblici Disponibili label.bandwidth=Capacit\u00e0 della banda (Bandwidth) label.basic=Basic label.broadcast.uri=URI di Broadcast +label.cancel=Annulla +label.certificate=Certificato label.change.service.offering=Modificare offerta di servizio +label.character=Carattere label.checksum=Checksum MD5 +label.cidr=CIDR label.CIDR.list=Lista CIDR label.CIDR.of.destination.network=Valore CIDR della rete di destinazione label.clear.list=Pulizia dell\\'elenco +label.cloud.console=Console di Gestione Cloud label.cluster=Cluster label.cluster.name=Nome del Cluster label.clusters=Cluster +label.cluster.type=Tipo di Cluster +label.clvm=CLVM +label.code=Codice label.community=Community label.compute.and.storage=Computazione e Storage label.compute=Compute @@ -107,6 +293,7 @@ label.console.proxy=Proxy di Console label.continue.basic.install=Proseguire con l\\'installazione di base label.continue=Continuare label.corrections.saved=Salvataggio correzioni effettuato +label.cpu=CPU label.cpu.mhz=CPU (in MHz) label.created.by.system=Creato dal sistema label.create.project=Creare un progetto @@ -234,6 +421,7 @@ label.max.volumes=Numero max di volumi label.max.vpcs=Numero max di VPC label.may.continue=E\\' ora possibile continuare. label.memory.mb=Memoria (in MB) +label.menu.accounts=Utenti label.menu.configuration=Configurazione label.menu.infrastructure=Infrastruttura label.menu.system.service.offerings=Offerte di Sistema @@ -433,6 +621,7 @@ label.zone.name=Nome Zona label.zones=Zone label.zone.type=Tipo di Zona label.zoneWizard.trafficType.guest=Guest\: Traffico di rete tra le virtual machine dell\\'utente finale +label.zoneWizard.trafficType.management=Management\: Traffico di rete tra le risorse interne di CloudStack, incluso qualsiasi componente che comunichi con il Management Server, come ad esempio gli host e le VM di Sistema di CloudStack label.zoneWizard.trafficType.public=Public\: Traffico di rete tra la rete internet e le virtual machine nell\\'infrastruttura cloud. label.zoneWizard.trafficType.storage=Storage\: Traffico di rete tra i server di primary e secondary storage, come ad esempio i template delle VM e le operazioni di snapshot message.acquire.new.ip=Si prega di confermare di voler acquisire un nuovo indirizzo IP per questa rete. diff --git a/client/WEB-INF/classes/resources/messages_ko_KR.properties b/client/WEB-INF/classes/resources/messages_ko_KR.properties index 766fc607648..757871acde0 100644 --- a/client/WEB-INF/classes/resources/messages_ko_KR.properties +++ b/client/WEB-INF/classes/resources/messages_ko_KR.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=\ud56d\ubaa9 \uc18d\uc131 \ubcc0\uacbd confirm.enable.swift=Swift \uae30\uc220 \uc9c0\uc6d0\ub97c \uc0ac\uc6a9 \ud558\ub824\uba74 \ub2e4\uc74c \uc815\ubcf4\ub97c \uc785\ub825\ud574 \uc8fc\uc2ed\uc2dc\uc624. error.could.not.enable.zone=Zone\uc744 \uc0ac\uc6a9 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. @@ -1194,6 +1193,7 @@ message.add.load.balancer.under.ip=\ub2e4\uc74c IP \uc8fc\uc18c\uc5d0 \ub300\ud5 message.add.load.balancer=Zone\uc5d0 \ub124\ud2b8\uc6cc\ud06c \ub85c\ub4dc \uacf5\uc720 \uc7a5\uce58\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. message.add.network=Zone \uc5d0 \uc0c8\ub85c\uc6b4 \ub124\ud2b8\uc6cc\ud06c\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. message.add.new.gateway.to.vpc=\ud604\uc7ac VPC\uc5d0 \uc0c8\ub85c\uc6b4 \uac8c\uc774\ud2b8\uc6e8\uc774\ub97c \ucd94\uac00\ud558\uae30 \uc704\ud55c \uc815\ubcf4\ub97c \uc9c0\uc815\ud574 \uc8fc\uc2ed\uc2dc\uc624. +message.add.pod.during.zone.creation=\uac01 Zone\uc5d0\ub294 \ud55c \uac1c \uc774\uc0c1 Pod\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc9c0\uae08 \uc5ec\uae30\uc11c \uccab\ubc88\uc9f8 Pod\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. Pod\ub294 \ud638\uc2a4\ud2b8\uc640 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0 \uc11c\ubc84\uc5d0\uc11c \uad6c\uc131\ud569\ub2c8\ub2e4\ub9cc \uc774\ub294 \ub2e4\uc74c \uc21c\uc11c\ub85c \ucd94\uac00\ud569\ub2c8\ub2e4. \ub9e8 \ucc98\uc74c CloudStack \ub0b4\ubd80 \uad00\ub9ac \ud2b8\ub798\ud53d\uc744 \uc704\ud574\uc11c IP \uc8fc\uc18c \ubc94\uc704\ub97c \uc608\uc57d\ud569\ub2c8\ub2e4. IP \uc8fc\uc18c \ubc94\uc704\ub294 \ud074\ub77c\uc6b0\ub4dc \ub0b4\ubd80 \uac01 Zone\uc5d0\uc11c \uc911\ubcf5 \ud558\uc9c0 \uc54a\uac8c \uc608\uc57d\ud560 \ud544\uc694\uac00 \uc788\uc2b5\ub2c8\ub2e4. message.add.pod=Zone \uc5d0 \uc0c8\ub85c\uc6b4 Pod\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. message.add.primary.storage=Zone Pod \uc5d0 \uc0c8\ub85c\uc6b4 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. message.add.primary=\uc0c8\ub85c\uc6b4 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0\ub97c \ucd94\uac00\ud558\uae30 \uc704\ud574 \uc544\ub798 \ud30c\ub77c\ubbf8\ud130\ub97c \uc9c0\uc815\ud574 \uc8fc\uc2ed\uc2dc\uc624. @@ -1253,6 +1253,7 @@ message.delete.VPN.gateway=\ud604\uc7ac VPN \uac8c\uc774\ud2b8\uc6e8\uc774\ub97c message.desc.advanced.zone=\ubcf4\ub2e4 \uc138\ub828\ub41c \ub124\ud2b8\uc6cc\ud06c \uae30\uc220\uc744 \uc9c0\uc6d0\ud569\ub2c8\ub2e4. \uc774 \ub124\ud2b8\uc6cc\ud06c \ubaa8\ub378\uc744 \uc120\ud0dd\ud558\uba74, \ubcf4\ub2e4 \uc720\uc5f0\ud558\uac8c \uac8c\uc2a4\ud2b8 \ub124\ud2b8\uc6cc\ud06c\ub97c \uc815\ud558\uace0 \ubc29\ud654\ubcbd(fire wall), VPN, \ub124\ud2b8\uc6cc\ud06c \ub85c\ub4dc \uacf5\uc720 \uc7a5\uce58 \uae30\uc220 \uc9c0\uc6d0\uc640 \uac19\uc740 \uc0ac\uc6a9\uc790 \uc9c0\uc815 \ud55c \ub124\ud2b8\uc6cc\ud06c \uc81c\uacf5\uc744 \uc81c\uacf5\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. message.desc.basic.zone=\uac01 VM \uc778\uc2a4\ud134\uc2a4\uc5d0 IP \uc8fc\uc18c\uac00 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uc9c1\uc811 \ud560\ub2f9\ud560 \uc218 \uc788\ub294 \ub2e8\uc77c \ub124\ud2b8\uc6cc\ud06c\ub97c \uc81c\uacf5\ud569\ub2c8\ub2e4. \ubcf4\uc548 \uadf8\ub8f9 (\uc804\uc1a1\uc6d0 IP \uc8fc\uc18c \ud544\ud130)\uacfc \uac19\uc740 \uce35 \uc138 \uac00\uc9c0 \ub808\ubca8 \ubc29\ubc95\uc73c\ub85c \uac8c\uc2a4\ud2b8\ub97c \ubd84\ub9ac\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. message.desc.cluster=\uac01 Pod\uc5d0\ub294 \ud55c \uac1c \uc774\uc0c1 \ud074\ub7ec\uc2a4\ud130\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc9c0\uae08 \uc5ec\uae30\uc11c \ucd5c\ucd08 \ud074\ub7ec\uc2a4\ud130\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. \ud074\ub7ec\uc2a4\ud130\ub294 \ud638\uc2a4\ud2b8\ub97c \uadf8\ub8f9\ud654 \ud558\ub294 \ubc29\ubc95\uc785\ub2c8\ub2e4. \ud55c \ud074\ub7ec\uc2a4\ud130 \ub0b4\ubd80 \ud638\uc2a4\ud2b8\ub294 \ubaa8\ub450 \ub3d9\uc77c\ud55c \ud558\ub4dc\uc6e8\uc5b4\uc5d0\uc11c \uad6c\uc131\ub418\uc5b4 \uac19\uc740 \ud558\uc774\ud37c \ubc14\uc774\uc800\ub97c \uc2e4\ud589\ud558\uace0 \uac19\uc740 \uc11c\ube0c \ub124\ud2b8\uc6cc\ud06c\uc0c1\uc5d0 \uc788\uc5b4 \uac19\uc740 \uacf5\uc720 \uc2a4\ud1a0\ub9ac\uc9c0\uc5d0 \uc811\uadfc \ud569\ub2c8\ub2e4. \uac01 \ud074\ub7ec\uc2a4\ud130\ub294 \ud55c \uac1c \uc774\uc0c1 \ud638\uc2a4\ud2b8\uc640 \ud55c \uac1c \uc774\uc0c1 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0 \uc11c\ubc84\uc5d0\uc11c \uad6c\uc131\ub429\ub2c8\ub2e4. +message.desc.host=\uac01 \ud074\ub7ec\uc2a4\ud130\uc5d0\ub294 \uc801\uc5b4\ub3c4 \ud55c \uac1c \uc774\uc0c1 \uac8c\uc2a4\ud2b8 VM\ub97c \uc2e4\ud589\ud558\uae30 \uc704\ud55c \ud638\uc2a4\ud2b8 (\ucef4\ud4e8\ud130)\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc9c0\uae08 \uc5ec\uae30\uc11c \uccab\ubc88\uc9f8 \ud638\uc2a4\ud2b8\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. CloudStack\uc73c\ub85c \ud638\uc2a4\ud2b8\ub97c \ub3d9\uc791\ud558\ub824\uba74 \ud638\uc2a4\ud2b8\uc5d0\uac8c \ud558\uc774\ud37c \ubc14\uc774\uc800\ub97c \uc124\uce58\ud558\uace0 IP \uc8fc\uc18c\ub97c \ud560\ub2f9\ud574 \ud638\uc2a4\ud2b8\uac00 CloudStack \uad00\ub9ac \uc11c\ubc84\uc5d0 \uc811\uc18d\ud558\ub3c4\ub85d \ud569\ub2c8\ub2e4.

    \ud638\uc2a4\ud2b8 DNS \uba85 \ub610\ub294 IP \uc8fc\uc18c, \uc0ac\uc6a9\uc790\uba85(\uc6d0\ub798 root)\uacfc \uc554\ud638 \ubc0f \ud638\uc2a4\ud2b8 \ubd84\ub958\uc5d0 \uc0ac\uc6a9\ud558\ub294 \ub77c\ubca8\uc744 \uc785\ub825\ud574 \uc8fc\uc2ed\uc2dc\uc624. message.desc.primary.storage=\uac01 \ud074\ub7ec\uc2a4\ud130\uc5d0\ub294 \uc801\uc5b4\ub3c4 \ud55c \uac1c \uc774\uc0c1\uc758 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0 \uc11c\ubc84\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc9c0\uae08 \uc5ec\uae30\uc11c \uccab\ubc88\uc9f8 \uc11c\ubc84\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0\ub294 \ud074\ub7ec\uc2a4\ud130 \ub0b4 \ubd80 \ud638\uc2a4\ud2b8\uc0c1\uc5d0\uc11c \ub3d9\uc791\ud558\ub294 \ubaa8\ub4e0 VM \ub514\uc2a4\ud06c \ubcfc\ub968\uc744 \ud3ec\ud568\ud569\ub2c8\ub2e4. \uae30\ubcf8\uc801\uc73c\ub85c \ud558\uc774\ud37c \ubc14\uc774\uc800\uc5d0\uc11c \uae30\uc220 \uc9c0\uc6d0\ub418\ub294 \ud45c\uc900\uc5d0 \uc900\uac70\ud55c \ud504\ub85c\ud1a0\ucf5c\uc744 \uc0ac\uc6a9\ud574 \uc8fc\uc2ed\uc2dc\uc624. message.desc.secondary.storage=\uac01 Zone\uc5d0\ub294 \uc801\uc5b4\ub3c4 \ud55c \uac1c \uc774\uc0c1\uc758 NFS \uc989 2\ucc28 \uc2a4\ud1a0\ub9ac\uc9c0 \uc11c\ubc84\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc9c0\uae08 \uc5ec\uae30\uc11c \uccab\ubc88\uc9f8 \uc11c\ubc84\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4. 2\ucc28 \uc2a4\ud1a0\ub9ac\uc9c0\ub294 VM \ud15c\ud50c\ub9bf, ISO \uc774\ubbf8\uc9c0 \ubc0f VM \ub514\uc2a4\ud06c \ubcfc\ub968 \uc2a4\ub0c5\uc0f7\uc744 \ud3ec\ud568\ud569\ub2c8\ub2e4. \uc774 \uc11c\ubc84\ub294 Zone\ub0b4 \ubaa8\ub4e0 \ud638\uc2a4\ud2b8\uc5d0\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4.

    IP \uc8fc\uc18c\uc640 \ub0b4\ubcf4\ub0b4\ub0bc \uacbd\ub85c\ub97c \uc785\ub825\ud574 \uc8fc\uc2ed\uc2dc\uc624. message.desc.zone=Zone\uc740 CloudStack \ud658\uacbd\ub0b4 \ucd5c\ub300 \uc870\uc9c1 \ub2e8\uc704\ub85c \uc6d0\ub798 \ub2e8\uc77c \ub370\uc774\ud130 \uc13c\ud130\uc5d0 \ud574\ub2f9\ud569\ub2c8\ub2e4. Zone\uc5d0 \ud574\uc11c \ubb3c\ub9ac\uc801\uc778 \ubd84\ub9ac\uc640 \uc911\ubcf5\uc131\uc774 \uc81c\uacf5\ub429\ub2c8\ub2e4. Zone\uc740 \ud55c \uac1c \uc774\uc0c1 Pod( \uac01 Pod\ub294 \ud638\uc2a4\ud2b8\uc640 \uae30\ubcf8 \uc2a4\ud1a0\ub9ac\uc9c0 \uc11c\ubc84\uc5d0\uc11c \uad6c\uc131)\uc640 Zone\ub0b4 \ubaa8\ub4e0 Pod\ub85c \uacf5\uc720\ub418\ub294 2\ucc28 \uc2a4\ud1a0\ub9ac\uc9c0 \uc11c\ubc84\ub85c \uad6c\uc131\ub429\ub2c8\ub2e4. @@ -1380,6 +1381,7 @@ message.step.3.continue=\uc2e4\ud589\ud558\ub824\uba74 \ub514\uc2a4\ud06c\uc81c\ message.step.3.desc= message.step.4.continue=\uc2e4\ud589\ud558\ub824\uba74 \ub124\ud2b8\uc6cc\ud06c\ub97c \uc801\uc5b4\ub3c4 \ud55c \uac1c \uc774\uc0c1 \uc120\ud0dd\ud574 \uc8fc\uc2ed\uc2dc\uc624. message.step.4.desc=\uac00\uc0c1 \uc778\uc2a4\ud134\uc2a4\uac00 \uc811\uc18d\ud558\ub294 \uae30\ubcf8 \ub124\ud2b8\uc6cc\ud06c\ub97c \uc120\ud0dd\ud574 \uc8fc\uc2ed\uc2dc\uc624. +message.storage.traffic=\ud638\uc2a4\ud2b8\ub098 CloudStack \uc2dc\uc2a4\ud15c VM \ub4f1 \uad00\ub9ac \uc11c\ubc84\uc640 \ud1b5\uc2e0\ud558\ub294 CloudStack \ub0b4\ubd80 \uc790\uc6d0\uac04 \ud2b8\ub798\ud53d\uc785\ub2c8\ub2e4. \uc5ec\uae30\uc11c \uc2a4\ud1a0\ub9ac\uc9c0 \ud2b8\ub798\ud53d\uc744 \uad6c\uc131\ud574 \uc8fc\uc2ed\uc2dc\uc624. message.suspend.project=\ud604\uc7ac \ud504\ub85c\uc81d\ud2b8\ub97c \uc77c\uc2dc\uc815\uc9c0\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? message.template.desc=VM\uc758 \uc2dc\uc791\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 OS \uc774\ubbf8\uc9c0 message.tooltip.dns.1=Zone\ub0b4 VM \ub85c \uc0ac\uc6a9\ud558\ub294 DNS \uc11c\ubc84 \uc774\ub984\uc785\ub2c8\ub2e4. Zone \uacf5\uac1c IP \uc8fc\uc18c\uc5d0\uc11c \uc774 \uc11c\ubc84\uc5d0 \ud1b5\uc2e0\ud560 \uc218 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4. diff --git a/client/WEB-INF/classes/resources/messages_nb_NO.properties b/client/WEB-INF/classes/resources/messages_nb_NO.properties index 8fba48ca9c4..be412449398 100644 --- a/client/WEB-INF/classes/resources/messages_nb_NO.properties +++ b/client/WEB-INF/classes/resources/messages_nb_NO.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=Endrede egenskaper error.could.not.enable.zone=Kunne ikke aktivere sonen error.installWizard.message=Noe gikk galt. G\u00e5 tilbake og korriger feilene. diff --git a/client/WEB-INF/classes/resources/messages_pt_BR.properties b/client/WEB-INF/classes/resources/messages_pt_BR.properties index 23123c16764..fd24f542e8d 100644 --- a/client/WEB-INF/classes/resources/messages_pt_BR.properties +++ b/client/WEB-INF/classes/resources/messages_pt_BR.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=Alteradas propriedades do item confirm.enable.s3=Por favor preencha as informa\u00e7\u00f5es abaixo para habilitar suporte a storage secund\u00e1ria fornecida por S3 confirm.enable.swift=Por favor preencha as informa\u00e7\u00f5es abaixo para habilitar suporte ao Swift @@ -1113,6 +1112,7 @@ label.zones=Zonas label.zone.type=Tipo de Zona label.zone.wide=Zone-Wide label.zoneWizard.trafficType.guest=H\u00f3spede\: tr\u00e1fego entre m\u00e1quinas virtuais de usu\u00e1rios finais +label.zoneWizard.trafficType.management=Ger\u00eancia\: tr\u00e1fego entre recursos internos do CloudStack, incluindo quaisquer componentes que se comunicam com o servidor de gerenciamento, tais como hosts e m\u00e1quinas virtuais de sistema do CloudStack label.zoneWizard.trafficType.public=P\u00fablico\: tr\u00e1fego entre a internet e m\u00e1quinas virtuais na nuvem. label.zoneWizard.trafficType.storage=Storage\: tr\u00e1fego entre servidores de storage prim\u00e1ria e secund\u00e1ria, tais como templates de m\u00e1quinas virtuais e snapshots label.zone=Zona diff --git a/client/WEB-INF/classes/resources/messages_ru_RU.properties b/client/WEB-INF/classes/resources/messages_ru_RU.properties index 5818abc9199..b28f6b69e6f 100644 --- a/client/WEB-INF/classes/resources/messages_ru_RU.properties +++ b/client/WEB-INF/classes/resources/messages_ru_RU.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u044b confirm.enable.swift=\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0438\u0436\u0435\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438 Swift error.could.not.enable.zone=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u043e\u043d\u0443 @@ -1131,6 +1130,7 @@ message.additional.networks.desc=\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u044 message.add.load.balancer=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0443 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432 \u0437\u043e\u043d\u0443 message.add.load.balancer.under.ip=\u041f\u0440\u0430\u0432\u0438\u043b\u043e \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0431\u044b\u043b \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0432 IP\: message.add.network=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0441\u0435\u0442\u044c \u0434\u043b\u044f \u0437\u043e\u043d\u044b\: +message.add.pod.during.zone.creation=\u041a\u0430\u0436\u0434\u0430\u044f \u0437\u043e\u043d\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0435\u043d\u0434\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0441\u0435\u0439\u0447\u0430\u0441 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u043f\u0435\u0440\u0432\u044b\u043c. \u0421\u0442\u0435\u043d\u0434 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u0437\u043b\u044b \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u044b \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c \u0448\u0430\u0433\u0435. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u0430\u0434\u0440\u0435\u0441\u043e\u0432 IP \u0434\u043b\u044f \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u0441\u0435\u0442\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 IP \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u043e\u043d\u044b \u043e\u0431\u043b\u0430\u043a\u0430. message.add.pod=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u0442\u0435\u043d\u0434 \u0434\u043b\u044f \u0437\u043e\u043d\u044b message.add.primary.storage=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u043b\u044f \u0437\u043e\u043d\u044b , \u0441\u0442\u0435\u043d\u0434\u0430 message.add.primary=\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 @@ -1185,6 +1185,7 @@ message.delete.user=\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442 message.desc.advanced.zone=\u0414\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u0442\u043e\u043f\u043e\u043b\u043e\u0433\u0438\u0439. \u042d\u0442\u0430 \u0441\u0435\u0442\u0435\u0432\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0438 \u0433\u043e\u0441\u0442\u0435\u0432\u043e\u0439 \u0441\u0435\u0442\u0438 \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u043b\u0443\u0433, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a \u043c\u0435\u0436\u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u044d\u043a\u0440\u0430\u043d, VPN, \u0438\u043b\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438. message.desc.basic.zone=\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0434\u0438\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0441\u0435\u0442\u044c, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u0430\u044f \u0412\u041c \u0438\u043c\u0435\u0435\u0442 \u00ab\u0431\u0435\u043b\u044b\u0439\u00bb IP-\u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0442\u0438. \u0418\u0437\u043e\u043b\u044f\u0446\u0438\u0438 \u0433\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0435\u0442\u0438 3-\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0433\u0440\u0443\u043f\u043f\u044b \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 (\u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f IP-\u0432\u0434\u0440\u0435\u0441\u043e\u0432) message.desc.cluster=\u041a\u0430\u0436\u0434\u044b\u0439 \u0441\u0442\u0435\u043d\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u043c\u0435\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u043e\u0432, \u043f\u0435\u0440\u0432\u044b\u0439 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u044b \u0441\u0435\u0439\u0447\u0430\u0441 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435. \u041a\u043b\u0430\u0441\u0442\u0435\u0440 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0433\u0440\u0443\u043f\u043f\u0443 \u0443\u0437\u043b\u043e\u0432. \u0423\u0437\u043b\u044b \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0435 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u043e\u0434\u0438\u043d \u0433\u0438\u043f\u0435\u0440\u0432\u0438\u0437\u043e\u0440, \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u0439 \u0441\u0435\u0442\u0438 \u0438 \u0438\u043c\u0435\u044e\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043e\u0434\u043d\u043e\u043c\u0443 \u0438 \u0442\u043e\u043c\u0443 \u0436\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u043c\u0443 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0443. \u041a\u0430\u0436\u0434\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u0443\u0437\u043b\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0435\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449. +message.desc.host=\u041a\u0430\u0436\u0434\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043e\u0434\u0438\u043d \u0443\u0437\u0435\u043b (\u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440) \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0412\u041c, \u043f\u0435\u0440\u0432\u044b\u0439 \u0438\u0437 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 \u0432\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u0435 \u0441\u0435\u0439\u0447\u0430\u0441. \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0443\u0437\u043b\u0430 \u0432 CloudStack \u0432\u0430\u0436\u043d\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0433\u0438\u043f\u0435\u0440\u0432\u0438\u0437\u043e\u0440\u0430 \u043d\u0430 \u0443\u0437\u0435\u043b, \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 IP \u043a \u0443\u0437\u043b\u0443 \u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0443\u0437\u043b\u0430 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f CloudStack.

    \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0438\u043c\u044f DNS \u0438\u043b\u0438 \u0430\u0434\u0440\u0435\u0441 IP, \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u043a \u041e\u0421 (\u043e\u0431\u044b\u0447\u043d\u043e root), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043c\u0435\u0442\u043a\u0438 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0443\u0437\u043b\u043e\u0432. message.desc.primary.storage=\u041a\u0430\u0436\u0434\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445, \u0438 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u0435\u0439\u0447\u0430\u0441. \u041f\u0435\u0440\u0432\u0438\u0447\u043d\u0430\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044b \u0436\u0435\u0441\u0442\u043a\u043e\u0433\u043e \u0434\u0438\u0441\u043a\u0430 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u0430\u0448\u0438\u043d, \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0445 \u043d\u0430 \u0443\u0437\u043b\u0430\u0445 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043b\u044e\u0431\u043e\u0439 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0433\u0438\u043f\u0435\u0440\u0432\u0438\u0437\u043e\u0440\u0430. message.desc.secondary.storage=\u041a\u0430\u0436\u0434\u0430\u044f \u0437\u043e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043e\u0431\u043b\u0430\u0434\u0430\u0442\u044c \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c NFS \u0438\u043b\u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435\u043c \u0438 \u0438\u0445 \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043e \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0412\u041c, \u043e\u0431\u0440\u0430\u0437\u043e\u0432 ISO \u0438 \u0441\u043d\u0438\u043c\u043a\u043e\u0432 \u0412\u041c. \u042d\u0442\u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0443\u0437\u043b\u043e\u0432 \u0437\u043e\u043d\u044b.

    \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c IP-\u0430\u0434\u0440\u0435\u0441 \u0438 \u043f\u0443\u0442\u044c. message.desc.zone=layer 3 @@ -1309,6 +1310,7 @@ message.step.3.continue=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\ message.step.3.desc= message.step.4.continue=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u043d\u0443 \u0441\u0435\u0442\u044c \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f. message.step.4.desc=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0441\u0435\u0442\u044c, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043c\u0430\u0448\u0438\u043d\u0430. +message.storage.traffic=\u0422\u0440\u0430\u0444\u0438\u043a \u043c\u0435\u0436\u0434\u0443 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u043c\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c\u0438 CloudStack, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0432\u0441\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0445\u043e\u0441\u0442\u044b \u0438 CloudStack \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0442\u0440\u0430\u0444\u0438\u043a \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u0434\u0435\u0441\u044c. message.suspend.project=\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442? message.template.desc=\u041e\u0431\u0440\u0430\u0437 \u041e\u0421, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u043e\u0439 \u0432 \u0412\u041c message.tooltip.dns.1=\u0418\u043c\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 DNS \u0434\u043b\u044f \u0412\u041c \u044d\u0442\u043e\u0439 \u0437\u043e\u043d\u044b. \u041f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0435 IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u044d\u0442\u043e\u0439 \u0437\u043e\u043d\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u043c\u0435\u0442\u044c \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430. diff --git a/client/WEB-INF/classes/resources/messages_zh_CN.properties b/client/WEB-INF/classes/resources/messages_zh_CN.properties index 687ef60b3c1..30daacc3627 100644 --- a/client/WEB-INF/classes/resources/messages_zh_CN.properties +++ b/client/WEB-INF/classes/resources/messages_zh_CN.properties @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - changed.item.properties=\u66f4\u6539\u9879\u76ee\u5c5e\u6027 confirm.enable.s3=\u8bf7\u586b\u5199\u4e0b\u5217\u4fe1\u606f\u4ee5\u542f\u7528\u652f\u6301S3\u7684\u4e8c\u7ea7\u5b58\u50a8 confirm.enable.swift=\u8bf7\u586b\u5199\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u542f\u7528\u5bf9 SWIFT \u7684\u652f\u6301 @@ -622,12 +621,17 @@ label.keyboard.type=\u952e\u76d8\u7c7b\u578b label.key=\u5bc6\u94a5 label.kvm.traffic.label=KVM \u6d41\u91cf\u6807\u7b7e label.label=\u6807\u7b7e +label.lang.arabic=\u963f\u62c9\u4f2f\u8bed label.lang.brportugese=\u5df4\u897f\u8461\u8404\u7259\u8bed +label.lang.catalan=\u52a0\u6cf0\u7f57\u5c3c\u4e9a\u8bed label.lang.chinese=\u7b80\u4f53\u4e2d\u6587 label.lang.english=\u82f1\u8bed label.lang.french=\u6cd5\u8bed +label.lang.german=\u5fb7\u8bed +label.lang.italian=\u610f\u5927\u5229\u8bed label.lang.japanese=\u65e5\u8bed label.lang.korean=\u97e9\u56fd\u8bed +label.lang.norwegian=\u632a\u5a01\u8bed label.lang.russian=\u4fc4\u8bed label.lang.spanish=\u897f\u73ed\u7259\u8bed label.last.disconnected=\u4e0a\u6b21\u65ad\u5f00\u8fde\u63a5\u65f6\u95f4 @@ -1165,6 +1169,7 @@ label.zone.type=\u533a\u57df\u7c7b\u578b label.zone=\u533a\u57df label.zone.wide=\u6574\u4e2a\u533a\u57df label.zoneWizard.trafficType.guest=\u6765\u5bbe\u7f51\u7edc\: \u5ba2\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u7f51\u7edc\u6d41\u91cf +label.zoneWizard.trafficType.management=\u7ba1\u7406\u7f51\: CloudStack\u5185\u90e8\u8d44\u6e90\u4e4b\u95f4\u7684\u7f51\u7edc\u6d41\u91cf, \u5305\u62ec\u4e0e\u7ba1\u7406\u670d\u52a1\u5668\u4ea4\u4e92\u7684\u4efb\u4f55\u7ec4\u4ef6, \u6bd4\u5982\u4e3b\u673a\u548cCloudStack\u7cfb\u7edf\u865a\u62df\u673a label.zoneWizard.trafficType.public=\u516c\u5171\u7f51\u7edc\: \u4e91\u73af\u5883\u4e2d\u865a\u62df\u673a\u4e0e\u56e0\u7279\u7f51\u4e4b\u95f4\u7684\u7f51\u7edc\u6d41\u91cf. label.zoneWizard.trafficType.storage=\u5b58\u50a8\u7f51\: \u4e3b\u5b58\u50a8\u4e0e\u4e8c\u7ea7\u5b58\u50a8\u670d\u52a1\u5668\u4e4b\u95f4\u7684\u6d41\u91cf, \u6bd4\u5982\u865a\u673a\u6a21\u677f\u548c\u5feb\u7167 managed.state=\u6258\u7ba1\u72b6\u6001 @@ -1255,6 +1260,7 @@ message.add.load.balancer=\u5411\u533a\u57df\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u8d1f message.add.load.balancer.under.ip=\u5df2\u5728\u4ee5\u4e0b IP \u4e0b\u6dfb\u52a0\u8d1f\u8f7d\u5e73\u8861\u5668\u89c4\u5219\: message.add.network=\u4e3a\u533a\u57df\u6dfb\u52a0\u4e00\u4e2a\u65b0\u7f51\u7edc\: message.add.new.gateway.to.vpc=\u8bf7\u6307\u5b9a\u5c06\u65b0\u7f51\u5173\u6dfb\u52a0\u5230\u6b64 VPC \u6240\u9700\u7684\u4fe1\u606f\u3002 +message.add.pod.during.zone.creation=\u6bcf\u4e2a\u533a\u57df\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u63d0\u4f9b\u70b9\u3002\u63d0\u4f9b\u70b9\u4e2d\u5305\u542b\u4e3b\u673a\u548c\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u60a8\u5c06\u5728\u968f\u540e\u7684\u67d0\u4e2a\u6b65\u9aa4\u4e2d\u6dfb\u52a0\u8fd9\u4e9b\u4e3b\u673a\u548c\u670d\u52a1\u5668\u3002\u9996\u5148\uff0c\u8bf7\u4e3a CloudStack \u7684\u5185\u90e8\u7ba1\u7406\u6d41\u91cf\u914d\u7f6e\u4e00\u4e2a\u9884\u7559 IP \u5730\u5740\u8303\u56f4\u3002\u9884\u7559\u7684 IP \u8303\u56f4\u5bf9\u4e91\u4e2d\u7684\u6bcf\u4e2a\u533a\u57df\u6765\u8bf4\u5fc5\u987b\u552f\u4e00\u3002 message.add.pod=\u4e3a\u533a\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u63d0\u4f9b\u70b9 message.add.primary.storage=\u4e3a\u533a\u57df \u3001\u63d0\u4f9b\u70b9 \u6dfb\u52a0\u4e00\u4e2a\u65b0\u7684\u4e3b\u5b58\u50a8 message.add.primary=\u8bf7\u6307\u5b9a\u4ee5\u4e0b\u53c2\u6570\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u4e3b\u5b58\u50a8 @@ -1316,6 +1322,7 @@ message.delete.VPN.gateway=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u966 message.desc.advanced.zone=\u9002\u7528\u4e8e\u66f4\u52a0\u590d\u6742\u7684\u7f51\u7edc\u62d3\u6251\u3002\u6b64\u7f51\u7edc\u6a21\u5f0f\u5728\u5b9a\u4e49\u6765\u5bbe\u7f51\u7edc\u5e76\u63d0\u4f9b\u9632\u706b\u5899\u3001VPN \u6216\u8d1f\u8f7d\u5e73\u8861\u5668\u652f\u6301\u7b49\u81ea\u5b9a\u4e49\u7f51\u7edc\u65b9\u6848\u65b9\u9762\u63d0\u4f9b\u4e86\u6700\u5927\u7684\u7075\u6d3b\u6027\u3002 message.desc.basic.zone=\u63d0\u4f9b\u4e00\u4e2a\u7f51\u7edc\uff0c\u5c06\u76f4\u63a5\u4ece\u6b64\u7f51\u7edc\u4e2d\u4e3a\u6bcf\u4e2a VM \u5b9e\u4f8b\u5206\u914d\u4e00\u4e2a IP\u3002\u53ef\u4ee5\u901a\u8fc7\u5b89\u5168\u7ec4\u7b49\u7b2c 3 \u5c42\u65b9\u5f0f\u63d0\u4f9b\u6765\u5bbe\u9694\u79bb(IP \u5730\u5740\u6e90\u8fc7\u6ee4)\u3002 message.desc.cluster=\u6bcf\u4e2a\u63d0\u4f9b\u70b9\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u7fa4\u96c6\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u7fa4\u96c6\u3002\u7fa4\u96c6\u63d0\u4f9b\u4e86\u4e00\u79cd\u7f16\u7ec4\u4e3b\u673a\u7684\u65b9\u6cd5\u3002\u7fa4\u96c6\u4e2d\u7684\u6240\u6709\u4e3b\u673a\u90fd\u5177\u6709\u76f8\u540c\u7684\u786c\u4ef6\uff0c\u8fd0\u884c\u76f8\u540c\u7684\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\uff0c\u4f4d\u4e8e\u76f8\u540c\u7684\u5b50\u7f51\u4e2d\uff0c\u5e76\u8bbf\u95ee\u76f8\u540c\u7684\u5171\u4eab\u5b58\u50a8\u3002\u6bcf\u4e2a\u7fa4\u96c6\u7531\u4e00\u4e2a\u6216\u591a\u4e2a\u4e3b\u673a\u4ee5\u53ca\u4e00\u4e2a\u6216\u591a\u4e2a\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u7ec4\u6210\u3002 +message.desc.host=\u6bcf\u4e2a\u7fa4\u96c6\u4e2d\u5fc5\u987b\u81f3\u5c11\u5305\u542b\u4e00\u4e2a\u4e3b\u673a\u4ee5\u4f9b\u6765\u5bbe VM \u5728\u4e0a\u9762\u8fd0\u884c\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u4e3b\u673a\u3002\u8981\u4f7f\u4e3b\u673a\u5728 CloudStack \u4e2d\u8fd0\u884c\uff0c\u5fc5\u987b\u5728\u6b64\u4e3b\u673a\u4e0a\u5b89\u88c5\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u8f6f\u4ef6\uff0c\u4e3a\u5176\u5206\u914d\u4e00\u4e2a IP \u5730\u5740\uff0c\u5e76\u786e\u4fdd\u5c06\u5176\u8fde\u63a5\u5230 CloudStack \u7ba1\u7406\u670d\u52a1\u5668\u3002

    \u8bf7\u63d0\u4f9b\u4e3b\u673a\u7684 DNS \u6216 IP \u5730\u5740\u3001\u7528\u6237\u540d(\u901a\u5e38\u4e3a root)\u548c\u5bc6\u7801\uff0c\u4ee5\u53ca\u7528\u4e8e\u5bf9\u4e3b\u673a\u8fdb\u884c\u5206\u7c7b\u7684\u4efb\u4f55\u6807\u7b7e\u3002 message.desc.primary.storage=\u6bcf\u4e2a\u7fa4\u96c6\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u3002\u4e3b\u5b58\u50a8\u4e2d\u5305\u542b\u5728\u7fa4\u96c6\u4e2d\u7684\u4e3b\u673a\u4e0a\u8fd0\u884c\u7684\u6240\u6709 VM \u7684\u78c1\u76d8\u5377\u3002\u8bf7\u4f7f\u7528\u5e95\u5c42\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u652f\u6301\u7684\u7b26\u5408\u6807\u51c6\u7684\u534f\u8bae\u3002 message.desc.secondary.storage=\u6bcf\u4e2a\u533a\u57df\u4e2d\u5fc5\u987b\u81f3\u5c11\u5305\u542b\u4e00\u4e2a NFS \u6216\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a NFS \u6216\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668\u3002\u8f85\u52a9\u5b58\u50a8\u7528\u4e8e\u5b58\u50a8 VM \u6a21\u677f\u3001ISO \u6620\u50cf\u548c VM \u78c1\u76d8\u5377\u5feb\u7167\u3002\u6b64\u670d\u52a1\u5668\u5fc5\u987b\u5bf9\u533a\u57df\u4e2d\u7684\u6240\u6709\u670d\u52a1\u5668\u53ef\u7528\u3002

    \u8bf7\u63d0\u4f9b IP \u5730\u5740\u548c\u5bfc\u51fa\u8def\u5f84\u3002 message.desc.zone=\u533a\u57df\u662f CloudStack \u4e2d\u6700\u5927\u7684\u7ec4\u7ec7\u5355\u4f4d\uff0c\u4e00\u4e2a\u533a\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u533a\u57df\u53ef\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\u3002\u4e00\u4e2a\u533a\u57df\u7531\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\u4ee5\u53ca\u7531\u533a\u57df\u4e2d\u7684\u6240\u6709\u63d0\u4f9b\u70b9\u5171\u4eab\u7684\u4e00\u4e2a\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668\u7ec4\u6210\uff0c\u5176\u4e2d\u6bcf\u4e2a\u63d0\u4f9b\u70b9\u4e2d\u5305\u542b\u591a\u4e2a\u4e3b\u673a\u548c\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u3002 @@ -1445,6 +1452,7 @@ message.step.3.continue=\u8bf7\u9009\u62e9\u4e00\u79cd\u78c1\u76d8\u65b9\u6848\u message.step.3.desc= message.step.4.continue=\u8bf7\u81f3\u5c11\u9009\u62e9\u4e00\u4e2a\u7f51\u7edc\u4ee5\u7ee7\u7eed message.step.4.desc=\u8bf7\u9009\u62e9\u865a\u62df\u5b9e\u4f8b\u8981\u8fde\u63a5\u5230\u7684\u4e3b\u7f51\u7edc\u3002 +message.storage.traffic=CloudStack \u5185\u90e8\u8d44\u6e90(\u5305\u62ec\u4e0e\u7ba1\u7406\u670d\u52a1\u5668\u901a\u4fe1\u7684\u4efb\u4f55\u7ec4\u4ef6\uff0c\u4f8b\u5982\u4e3b\u673a\u548c CloudStack \u7cfb\u7edf VM)\u4e4b\u95f4\u7684\u6d41\u91cf\u3002\u8bf7\u5728\u6b64\u5904\u914d\u7f6e\u5b58\u50a8\u6d41\u91cf\u3002 message.suspend.project=\u662f\u5426\u786e\u5b9e\u8981\u6682\u505c\u6b64\u9879\u76ee? message.template.desc=\u53ef\u7528\u4e8e\u542f\u52a8 VM \u7684\u64cd\u4f5c\u7cfb\u7edf\u6620\u50cf message.tooltip.dns.1=\u4f9b\u533a\u57df\u4e2d\u7684 VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u533a\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 From 82974458a675c52e888c91f569f0f1ca278197d7 Mon Sep 17 00:00:00 2001 From: Harikrishna Patnala Date: Mon, 27 May 2013 18:24:35 +0530 Subject: [PATCH 141/412] CLOUDSTACK-2672: Adding isVolatile parameter in service offering response object Signed off by :- Nitin Mehta --- .../api/response/ServiceOfferingResponse.java | 11 +++++++++++ .../api/query/dao/ServiceOfferingJoinDaoImpl.java | 1 + .../com/cloud/api/query/vo/ServiceOfferingJoinVO.java | 11 +++++++++++ setup/db/db/schema-410to420.sql | 1 + 4 files changed, 24 insertions(+) diff --git a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index 08ebbb05887..31533f87728 100644 --- a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -60,6 +60,9 @@ public class ServiceOfferingResponse extends BaseResponse { @SerializedName("limitcpuuse") @Param(description="restrict the CPU usage to committed service offering") private Boolean limitCpuUse; + @SerializedName("isvolatile") @Param(description="true if the vm needs to be volatile, i.e., on every reboot of vm from API root disk is discarded and creates a new root disk") + private Boolean isVolatile; + @SerializedName("tags") @Param(description="the tags for the service offering") private String tags; @@ -237,4 +240,12 @@ public class ServiceOfferingResponse extends BaseResponse { public void setDeploymentPlanner(String deploymentPlanner) { this.deploymentPlanner = deploymentPlanner; } + + public boolean getVolatileVm() { + return isVolatile; + } + + public void setVolatileVm(boolean isVolatile) { + this.isVolatile = isVolatile; + } } diff --git a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index ce20562d5f7..56e4d0a369e 100644 --- a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -68,6 +68,7 @@ public class ServiceOfferingJoinDaoImpl extends GenericDaoBase Date: Sat, 25 May 2013 17:46:13 +0530 Subject: [PATCH 142/412] generate an action event when portable IP association is transferred from a network to different network --- api/src/com/cloud/event/EventTypes.java | 1 + server/src/com/cloud/network/NetworkManagerImpl.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index da96294c14c..6dfb1ab57b6 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -436,6 +436,7 @@ public class EventTypes { public static final String EVENT_PORTABLE_IP_RANGE_CREATE = "PORTABLE.IP.RANGE.CREATE"; public static final String EVENT_PORTABLE_IP_RANGE_DELETE = "PORTABLE.IP.RANGE.DELETE"; + public static final String EVENT_PORTABLE_IP_TRANSFER = "PORTABLE.IP.TRANSFER"; static { diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 254510b15a9..d699b2e02e2 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -39,6 +39,7 @@ import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEventUtils; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; import com.cloud.event.dao.UsageEventDao; @@ -1063,6 +1064,9 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L _ipAddressDao.update(ipAddrId, ip); txn.commit(); + ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, Domain.ROOT_DOMAIN, + EventTypes.EVENT_PORTABLE_IP_TRANSFER, "Portable IP associated is transferred from network " + + currentNetworkId + " to " + newNetworkId); } @Override From 883333c2143e9f3b14a3bc91f8e486e3574dfa37 Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Mon, 27 May 2013 18:42:33 +0530 Subject: [PATCH 143/412] CLOUDSTACK-2700:on network/vpc delete, portable IP should be still associated with account Unlike public ip which gets dis-associated (released) with the account on network/VPC delete, portable IP should continue to be associated with the account even when the network/VPC with which it is currently associated in deleted. This fix ensures portable IP are associated to account even after network/vpc is deleted. --- api/src/com/cloud/network/NetworkService.java | 2 +- .../src/com/cloud/network/NetworkManager.java | 9 +++++---- .../com/cloud/network/NetworkManagerImpl.java | 20 ++++++++++++++----- .../com/cloud/network/NetworkServiceImpl.java | 10 +++++++--- .../cloud/network/rules/RulesManagerImpl.java | 12 +++++------ .../com/cloud/network/vpc/VpcManagerImpl.java | 15 +++++++++++--- .../com/cloud/user/AccountManagerImpl.java | 8 ++++++++ .../cloud/network/MockNetworkManagerImpl.java | 2 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 2 +- 9 files changed, 56 insertions(+), 24 deletions(-) diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 59702a2864e..405cecd8847 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -55,7 +55,7 @@ public interface NetworkService { IpAddress allocatePortableIP(Account ipOwner, int regionId, Long zoneId, Long networkId, Long vpcId) throws ResourceAllocationException, InsufficientAddressCapacityException, ConcurrentOperationException; - boolean releasePortableIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; + boolean releasePortableIpAddress(long ipAddressId); Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 05bc26ee5c2..19f396abe6e 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -268,6 +268,11 @@ public interface NetworkManager { IPAddressVO associateIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, InsufficientAddressCapacityException, ConcurrentOperationException; + IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID) + throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; + + boolean releasePortableIpAddress(long addrId); + IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, InsufficientAddressCapacityException, ConcurrentOperationException; @@ -362,10 +367,6 @@ public interface NetworkManager { IpAddress allocateIp(Account ipOwner, boolean isSystem, Account caller, long callerId, DataCenter zone) throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; - - IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID) - throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; - Map finalizeServicesAndProvidersForNetwork(NetworkOffering offering, Long physicalNetworkId); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index d699b2e02e2..b443dca6abc 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1118,7 +1118,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } @DB - private void releasePortableIpAddress(long addrId) { + @Override + public boolean releasePortableIpAddress(long addrId) { Transaction txn = Transaction.currentTxn(); GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange"); @@ -1133,12 +1134,13 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L // removed the provisioned vlan VlanVO vlan = _vlanDao.findById(ip.getVlanId()); - _vlanDao.expunge(vlan.getId()); + _vlanDao.remove(vlan.getId()); // remove the provisioned public ip address - _ipAddressDao.expunge(ip.getId()); + _ipAddressDao.remove(ip.getId()); txn.commit(); + return true; } finally { portableIpLock.releaseRef(); } @@ -3537,8 +3539,16 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L List ipsToRelease = _ipAddressDao.listByAssociatedNetwork(networkId, null); for (IPAddressVO ipToRelease : ipsToRelease) { if (ipToRelease.getVpcId() == null) { - IPAddressVO ip = markIpAsUnavailable(ipToRelease.getId()); - assert (ip != null) : "Unable to mark the ip address id=" + ipToRelease.getId() + " as unavailable."; + if (!ipToRelease.isPortable()) { + IPAddressVO ip = markIpAsUnavailable(ipToRelease.getId()); + assert (ip != null) : "Unable to mark the ip address id=" + ipToRelease.getId() + " as unavailable."; + } else { + // portable IP address are associated with owner, until explicitly requested to be disassociated + // so as part of network clean up just break IP association with guest network + ipToRelease.setAssociatedWithNetworkId(null); + _ipAddressDao.update(ipToRelease.getId(), ipToRelease); + s_logger.debug("Portable IP address " + ipToRelease + " is no longer associated with any network"); + } } else { _vpcMgr.unassignIPFromVpcNetwork(ipToRelease.getId(), network.getId()); } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 1533ca9bc4f..f880bccb046 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -591,8 +591,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_RELEASE, eventDescription = "disassociating portable Ip", async = true) - public boolean releasePortableIpAddress(long ipAddressId) throws InsufficientAddressCapacityException { - return releaseIpAddressInternal(ipAddressId); + public boolean releasePortableIpAddress(long ipAddressId) { + try { + return releaseIpAddressInternal(ipAddressId); + } catch (Exception e) { + return false; + } } @Override @@ -880,7 +884,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { boolean success = _networkMgr.disassociatePublicIpAddress(ipAddressId, userId, caller); if (success) { - if (!ipVO.isPortable()) { + if (ipVO.isPortable()) { return success; } Long networkId = ipVO.getAssociatedWithNetworkId(); diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 883455377f4..3c95cef440f 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -490,10 +490,9 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules "a part of enable static nat"); return false; } - performedIpAssoc = true; } else if (ipAddress.isPortable()) { - s_logger.info("Portable IP " + ipAddress.getUuid() + " is not associated with the network, so" + - "associate IP with the network " + networkId); + s_logger.info("Portable IP " + ipAddress.getUuid() + " is not associated with the network yet " + + " so associate IP with the network " + networkId); try { // check if StaticNat service is enabled in the network _networkModel.checkIpForService(ipAddress, Service.StaticNat, networkId); @@ -504,13 +503,12 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules } // associate portable IP with guest network - _networkMgr.associatePortableIPToGuestNetwork(ipId, networkId, false); + ipAddress = _networkMgr.associatePortableIPToGuestNetwork(ipId, networkId, false); } catch (Exception e) { s_logger.warn("Failed to associate portable id=" + ipId + " to network id=" + networkId + " as " + "a part of enable static nat"); return false; } - performedIpAssoc = true; } } else if (ipAddress.getAssociatedWithNetworkId() != networkId) { if (ipAddress.isPortable()) { @@ -520,14 +518,16 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules // check if portable IP can be transferred across the networks if (_networkMgr.isPortableIpTransferableFromNetwork(ipId, ipAddress.getAssociatedWithNetworkId() )) { try { + // transfer the portable IP and refresh IP details _networkMgr.transferPortableIP(ipId, ipAddress.getAssociatedWithNetworkId(), networkId); + ipAddress = _ipAddressDao.findById(ipId); } catch (Exception e) { s_logger.warn("Failed to associate portable id=" + ipId + " to network id=" + networkId + " as " + "a part of enable static nat"); return false; } } else { - throw new InvalidParameterValueException("Portable IP: " + ipId + " has associated services" + + throw new InvalidParameterValueException("Portable IP: " + ipId + " has associated services " + "in network " + ipAddress.getAssociatedWithNetworkId() + " so can not be transferred to " + " network " + networkId); } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 1aab7320fb4..15e1539ae7d 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -1217,9 +1217,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis List ipsToRelease = _ipAddressDao.listByAssociatedVpc(vpcId, null); s_logger.debug("Releasing ips for vpc id=" + vpcId + " as a part of vpc cleanup"); for (IPAddressVO ipToRelease : ipsToRelease) { - success = success && _ntwkMgr.disassociatePublicIpAddress(ipToRelease.getId(), callerUserId, caller); - if (!success) { - s_logger.warn("Failed to cleanup ip " + ipToRelease + " as a part of vpc id=" + vpcId + " cleanup"); + if (ipToRelease.isPortable()) { + // portable IP address are associated with owner, until explicitly requested to be disassociated. + // so as part of VPC clean up just break IP association with VPC + ipToRelease.setVpcId(null); + ipToRelease.setAssociatedWithNetworkId(null); + _ipAddressDao.update(ipToRelease.getId(), ipToRelease); + s_logger.debug("Portable IP address " + ipToRelease + " is no longer associated with any VPC"); + } else { + success = success && _ntwkMgr.disassociatePublicIpAddress(ipToRelease.getId(), callerUserId, caller); + if (!success) { + s_logger.warn("Failed to cleanup ip " + ipToRelease + " as a part of vpc id=" + vpcId + " cleanup"); + } } } diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 7421422d294..e72005e8214 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -731,6 +731,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M _resourceCountDao.removeEntriesByOwner(accountId, ResourceOwnerType.Account); _resourceLimitDao.removeEntriesByOwner(accountId, ResourceOwnerType.Account); + // release account specific acquired portable IP's. Since all the portable IP's must have been already + // disassociated with VPC/guest network (due to deletion), so just mark portable IP as free. + List portableIpsToRelease = _ipAddressDao.listByAccount(accountId); + for (IpAddress ip : portableIpsToRelease) { + s_logger.debug("Releasing portable ip " + ip + " as a part of account id=" + accountId + " cleanup"); + _networkMgr.releasePortableIpAddress(ip.getId()); + } + return true; } catch (Exception ex) { s_logger.warn("Failed to cleanup account " + account + " due to ", ex); diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index e5d34fbacc7..53c0fa82672 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -887,7 +887,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage } @Override - public boolean releasePortableIpAddress(long ipAddressId) throws InsufficientAddressCapacityException { + public boolean releasePortableIpAddress(long ipAddressId) { return false;// TODO Auto-generated method stub } diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 7129273a50c..a1ece441863 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -210,7 +210,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage } @Override - public boolean releasePortableIpAddress(long ipAddressId) throws InsufficientAddressCapacityException { + public boolean releasePortableIpAddress(long ipAddressId) { return false;// TODO Auto-generated method stub } From e9a6d47316d8de90eb662e9938ea7fcbe0ee0596 Mon Sep 17 00:00:00 2001 From: Hiroaki KAWAI Date: Tue, 28 May 2013 11:55:21 +0900 Subject: [PATCH 144/412] agent: fix network.bridge.type to be optional New network.bridge.type was introduced, but for buckward compatibility, the key should be optional. Signed-off-by: Hiroaki KAWAI --- agent/bindir/cloud-setup-agent.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in index d6f481db2e0..6932672b962 100755 --- a/agent/bindir/cloud-setup-agent.in +++ b/agent/bindir/cloud-setup-agent.in @@ -96,7 +96,9 @@ if __name__ == '__main__': parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface") old_config = configFileOps("@AGENTSYSCONFDIR@/agent.properties") - glbEnv.bridgeType = old_config.getEntry("network.bridge.type").lower() + bridgeType = old_config.getEntry("network.bridge.type").lower() + if bridgeType: + glbEnv.bridgeType = bridgeType (options, args) = parser.parse_args() if options.auto is None: From 204bdac77f2c48e304b8bd7ccf2c85a69f4885ef Mon Sep 17 00:00:00 2001 From: ashutoshkelkar Date: Thu, 23 May 2013 11:23:34 +0530 Subject: [PATCH 145/412] Fix for repeated log lines in test case logs. Signed-off-by: Prasanna Santhanam --- tools/marvin/marvin/TestCaseExecuteEngine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/marvin/marvin/TestCaseExecuteEngine.py b/tools/marvin/marvin/TestCaseExecuteEngine.py index 57438688486..4b64aaee835 100644 --- a/tools/marvin/marvin/TestCaseExecuteEngine.py +++ b/tools/marvin/marvin/TestCaseExecuteEngine.py @@ -72,7 +72,7 @@ class TestCaseExecuteEngine(object): self.injectTestCase(test) else: #logger bears the name of the test class - testcaselogger = logging.getLogger("testclient.testcase.%s"%test.__class__.__name__) + testcaselogger = logging.getLogger("%s" % (test)) fh = logging.FileHandler(self.logfile) fh.setFormatter(self.logformat) testcaselogger.addHandler(fh) From 8fd476e2fddd75949362630823d1a574d44a1705 Mon Sep 17 00:00:00 2001 From: SrikanteswaraRao Talluri Date: Fri, 17 May 2013 14:42:26 +0530 Subject: [PATCH 146/412] CLOUDSTACK-2557: Fix add primary storage tests Signed-off-by: Prasanna Santhanam --- .../integration/smoke/test_primary_storage.py | 134 ++++++++++-------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/test/integration/smoke/test_primary_storage.py b/test/integration/smoke/test_primary_storage.py index eb747fa6588..598654da112 100644 --- a/test/integration/smoke/test_primary_storage.py +++ b/test/integration/smoke/test_primary_storage.py @@ -35,32 +35,17 @@ class Services: def __init__(self): self.services = { - "nfs": { - 0: { - "url": "nfs://192.168.100.131/testprimary", + "nfs": + { + "url": "nfs://10.147.28.7/export/home/talluri/testprimary", # Format: File_System_Type/Location/Path - "name": "Primary XEN", - "hypervisor": 'XEN', + "name": "Primary XEN" }, - 1: { - "url": "nfs://192.168.100.131/Primary", - "name": "Primary KVM", - "hypervisor": 'KVM', - }, - 2: { - "url": "nfs://192.168.100.131/Primary", - "name": "Primary VMWare", - "hypervisor": 'VMWare', - }, - }, "iscsi": { - 0: { "url": "iscsi://192.168.100.21/iqn.2012-01.localdomain.clo-cstack-cos6:iser/1", # Format : iscsi://IP Address/IQN number/LUN# - "name": "Primary iSCSI", - "hypervisor": 'XEN', - }, - }, + "name": "Primary iSCSI" + } } class TestPrimaryStorageServices(cloudstackTestCase): @@ -85,31 +70,27 @@ class TestPrimaryStorageServices(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @unittest.skip("skipped - will not be adding storage in our environments") - def test_01_primary_storage(self): + @attr(tags = ["advanced", "advancedns", "smoke", "basic", "sg"]) + def test_01_primary_storage_nfs(self): """Test primary storage pools - XEN, KVM, VMWare """ # Validate the following: - # 1. verify hypervisortype returned by api is Xen/KVM/VMWare + # 1. List Clusters # 2. verify that the cluster is in 'Enabled' allocation state # 3. verify that the host is added successfully and # in Up state with listHosts api response #Create NFS storage pools with on XEN/KVM/VMWare clusters - for k, v in self.services["nfs"].items(): - clusters = list_clusters( - self.apiclient, - zoneid=self.zone.id, - hypervisortype=v["hypervisor"] - ) - self.assertEqual( - isinstance(clusters, list), - True, - "Check list response returns a valid list" - ) - cluster = clusters[0] + + clusters = list_clusters( + self.apiclient, + zoneid=self.zone.id + ) + assert isinstance(clusters,list) and len(clusters)>0 + for cluster in clusters: + #Host should be present before adding primary storage list_hosts_response = list_hosts( self.apiclient, @@ -124,11 +105,11 @@ class TestPrimaryStorageServices(cloudstackTestCase): self.assertNotEqual( len(list_hosts_response), 0, - "Check list Hosts for hypervisor: " + v["hypervisor"] + "Check list Hosts in the cluster: " + cluster.name ) storage = StoragePool.create(self.apiclient, - v, + self.services["nfs"], clusterid=cluster.id, zoneid=self.zone.id, podid=self.pod.id @@ -140,13 +121,13 @@ class TestPrimaryStorageServices(cloudstackTestCase): self.assertEqual( storage.state, 'Up', - "Check primary storage state for hypervisor: " + v["hypervisor"] + "Check primary storage state " ) self.assertEqual( storage.type, 'NetworkFilesystem', - "Check storage pool type for hypervisor : " + v["hypervisor"] + "Check storage pool type " ) #Verify List Storage pool Response has newly added storage pool @@ -169,45 +150,76 @@ class TestPrimaryStorageServices(cloudstackTestCase): self.assertEqual( storage_response.id, storage.id, - "Check storage pool ID for hypervisor: " + v["hypervisor"] + "Check storage pool ID" ) self.assertEqual( storage.type, storage_response.type, - "Check storage pool type for hypervisor: " + v["hypervisor"] + "Check storage pool type " ) # Call cleanup for reusing primary storage cleanup_resources(self.apiclient, self.cleanup) self.cleanup = [] + return + + + @attr(tags = ["advanced", "advancedns", "smoke", "basic", "sg"]) + def test_01_primary_storage_iscsi(self): + """Test primary storage pools - XEN, KVM, VMWare + """ + + # Validate the following: + # 1. List Clusters + # 2. verify that the cluster is in 'Enabled' allocation state + # 3. verify that the host is added successfully and + # in Up state with listHosts api response # Create iSCSI storage pools with on XEN/KVM clusters - for k, v in self.services["iscsi"].items(): - clusters = list_clusters( - self.apiclient, - zoneid=self.zone.id, - hypervisortype=v["hypervisor"] - ) + clusters = list_clusters( + self.apiclient, + zoneid=self.zone.id + ) + assert isinstance(clusters,list) and len(clusters)>0 + for cluster in clusters: + + #Host should be present before adding primary storage + list_hosts_response = list_hosts( + self.apiclient, + clusterid=cluster.id + ) self.assertEqual( - isinstance(clusters, list), + isinstance(list_hosts_response, list), True, "Check list response returns a valid list" ) - cluster = clusters[0] + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check list Hosts in the cluster: " + cluster + ) + storage = StoragePool.create(self.apiclient, - v, + self.services["iscsi"], clusterid=cluster.id, zoneid=self.zone.id, podid=self.pod.id ) self.cleanup.append(storage) - self.debug("Created iSCSI storage pool in cluster: %s" % cluster.id) - + self.debug("Created storage pool in cluster: %s" % cluster.id) + self.assertEqual( storage.state, 'Up', - "Check primary storage state for hypervisor: " + v["hypervisor"] + "Check primary storage state " + ) + + self.assertEqual( + storage.type, + 'NetworkFilesystem', + "Check storage pool type " ) #Verify List Storage pool Response has newly added storage pool @@ -221,24 +233,24 @@ class TestPrimaryStorageServices(cloudstackTestCase): "Check list response returns a valid list" ) self.assertNotEqual( - len(storage_pools_response), - 0, - "Check Hosts response for hypervisor: " + v["hypervisor"] + len(storage_pools_response), + 0, + "Check list Hosts response" ) storage_response = storage_pools_response[0] self.assertEqual( storage_response.id, storage.id, - "Check storage pool ID for hypervisor: " + v["hypervisor"] - ) + "Check storage pool ID" + ) self.assertEqual( storage.type, storage_response.type, - "Check storage pool type hypervisor: " + v["hypervisor"] + "Check storage pool type " ) - # Call cleanup for reusing primary storage cleanup_resources(self.apiclient, self.cleanup) self.cleanup = [] + return From 2e8d1264a247772b5dfda5bcab26fa5ed384a6d9 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 28 May 2013 09:43:23 +0200 Subject: [PATCH 147/412] CLOUDSTACK-2707: use executeBatch instead of persist when Usage Server createNetworkHelperEntry --- .../com/cloud/usage/dao/UsageNetworkDao.java | 2 ++ .../cloud/usage/dao/UsageNetworkDaoImpl.java | 34 +++++++++++++++++++ .../src/com/cloud/usage/UsageManagerImpl.java | 12 ++++--- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/engine/schema/src/com/cloud/usage/dao/UsageNetworkDao.java b/engine/schema/src/com/cloud/usage/dao/UsageNetworkDao.java index 0f7c771b2dc..aa43eab10e9 100644 --- a/engine/schema/src/com/cloud/usage/dao/UsageNetworkDao.java +++ b/engine/schema/src/com/cloud/usage/dao/UsageNetworkDao.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.usage.dao; +import java.util.List; import java.util.Map; import com.cloud.usage.UsageNetworkVO; @@ -24,4 +25,5 @@ import com.cloud.utils.db.GenericDao; public interface UsageNetworkDao extends GenericDao { Map getRecentNetworkStats(); void deleteOldStats(long maxEventTime); + void saveUsageNetworks(List usageNetworks); } diff --git a/engine/schema/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java b/engine/schema/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java index d64fd807890..af8083aa65a 100644 --- a/engine/schema/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java +++ b/engine/schema/src/com/cloud/usage/dao/UsageNetworkDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.usage.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.ejb.Local; @@ -29,6 +30,7 @@ import org.springframework.stereotype.Component; import com.cloud.usage.UsageNetworkVO; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; @Component @Local(value={UsageNetworkDao.class}) @@ -41,6 +43,8 @@ public class UsageNetworkDaoImpl extends GenericDaoBase im ") joinnet on u.account_id = joinnet.acct_id and u.zone_id = joinnet.z_id and u.event_time_millis = joinnet.max_date"; private static final String DELETE_OLD_STATS = "DELETE FROM cloud_usage.usage_network WHERE event_time_millis < ?"; + private static final String INSERT_USAGE_NETWORK = "INSERT INTO cloud_usage.usage_network (account_id, zone_id, host_id, host_type, network_id, bytes_sent, bytes_received, agg_bytes_received, agg_bytes_sent, event_time_millis) VALUES (?,?,?,?,?,?,?,?,?,?)"; + public UsageNetworkDaoImpl() { } @@ -95,4 +99,34 @@ public class UsageNetworkDaoImpl extends GenericDaoBase im s_logger.error("error deleting old usage network stats", ex); } } + + @Override + public void saveUsageNetworks (List usageNetworks) { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + String sql = INSERT_USAGE_NETWORK; + PreparedStatement pstmt = null; + pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection + for (UsageNetworkVO usageNetwork : usageNetworks) { + pstmt.setLong(1, usageNetwork.getAccountId()); + pstmt.setLong(2, usageNetwork.getZoneId()); + pstmt.setLong(3, usageNetwork.getHostId()); + pstmt.setString(4, usageNetwork.getHostType()); + pstmt.setLong(5, usageNetwork.getNetworkId()); + pstmt.setLong(6, usageNetwork.getBytesSent()); + pstmt.setLong(7, usageNetwork.getBytesReceived()); + pstmt.setLong(8, usageNetwork.getAggBytesReceived()); + pstmt.setLong(9, usageNetwork.getAggBytesSent()); + pstmt.setLong(10, usageNetwork.getEventTimeMillis()); + pstmt.addBatch(); + } + pstmt.executeBatch(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error saving usage_network to cloud_usage db", ex); + throw new CloudRuntimeException(ex.getMessage()); + } + } } diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java index 16fe67bcbec..0c2ad6ef339 100644 --- a/usage/src/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/com/cloud/usage/UsageManagerImpl.java @@ -18,6 +18,7 @@ package com.cloud.usage; import java.net.InetAddress; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -119,6 +120,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna int m_pid = 0; TimeZone m_usageTimezone = TimeZone.getTimeZone("GMT");; private final GlobalLock m_heartbeatLock = GlobalLock.getInternLock("usage.job.heartbeat.check"); + private List usageNetworks = new ArrayList(); private final ScheduledExecutorService m_executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Usage-Job")); private final ScheduledExecutorService m_heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Usage-HB")); @@ -547,16 +549,18 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna // loop over the user stats, create delta entries in the usage_network helper table int numAcctsProcessed = 0; + usageNetworks.clear(); for (String key : aggregatedStats.keySet()) { UsageNetworkVO currentNetworkStats = null; if (networkStats != null) { currentNetworkStats = networkStats.get(key); } - + createNetworkHelperEntry(aggregatedStats.get(key), currentNetworkStats, endDateMillis); numAcctsProcessed++; - } - + } + m_usageNetworkDao.saveUsageNetworks(usageNetworks); + if (s_logger.isDebugEnabled()) { s_logger.debug("created network stats helper entries for " + numAcctsProcessed + " accts"); } @@ -999,7 +1003,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna s_logger.debug("creating networkHelperEntry... accountId: " + userStat.getAccountId() + " in zone: " + userStat.getDataCenterId() + "; abr: " + userStat.getAggBytesReceived() + "; abs: " + userStat.getAggBytesSent() + "; curABS: " + currentAccountedBytesSent + "; curABR: " + currentAccountedBytesReceived + "; ubs: " + bytesSent + "; ubr: " + bytesReceived); } - m_usageNetworkDao.persist(usageNetworkVO); + usageNetworks.add(usageNetworkVO); } private void createIPHelperEvent(UsageEventVO event) { From a0d5b78e84df705611f6e59df39ed7c7059c8b4d Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Tue, 28 May 2013 14:13:26 +0530 Subject: [PATCH 148/412] CLOUDSTACK-2708: AutoScale listConditions response wrongly mapped AutoScale - listConditions was mapped to CounterResponse instead of ConditionResponse Signed-off-by: Prasanna Santhanam --- .../api/command/user/autoscale/ListConditionsCmd.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java index 1c949232403..2b15d2b7e7d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java @@ -17,9 +17,7 @@ package org.apache.cloudstack.api.command.user.autoscale; -import java.util.ArrayList; -import java.util.List; - +import com.cloud.network.as.Condition; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListAccountResourcesCmd; @@ -30,9 +28,10 @@ import org.apache.cloudstack.api.response.CounterResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.log4j.Logger; -import com.cloud.network.as.Condition; +import java.util.ArrayList; +import java.util.List; -@APICommand(name = "listConditions", description = "List Conditions for the specific user", responseObject = CounterResponse.class) +@APICommand(name = "listConditions", description = "List Conditions for the specific user", responseObject = ConditionResponse.class) public class ListConditionsCmd extends BaseListAccountResourcesCmd { public static final Logger s_logger = Logger.getLogger(ListConditionsCmd.class.getName()); private static final String s_name = "listconditionsresponse"; From 8ebf2ca3d74707b0006e6c6a667968a67520d346 Mon Sep 17 00:00:00 2001 From: radhikap Date: Tue, 28 May 2013 14:40:14 +0530 Subject: [PATCH 149/412] CLOUDSTACK-2709 --- docs/en-US/Release_Notes.xml | 6 ++++++ docs/en-US/egress-firewall-rule.xml | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/en-US/Release_Notes.xml b/docs/en-US/Release_Notes.xml index 25e1175b148..ab734cdf5d1 100644 --- a/docs/en-US/Release_Notes.xml +++ b/docs/en-US/Release_Notes.xml @@ -4396,6 +4396,12 @@ under the License.