From 4e967e3568a7a5ec2cb8fc8b54a0358c07e98d3a Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 10:08:33 -0800 Subject: [PATCH 01/41] bug 7134: VM Wizard - step 4 - basic zone - allow the user to select the default security group. --- ui/scripts/cloud.core.instance.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/scripts/cloud.core.instance.js b/ui/scripts/cloud.core.instance.js index 03b55274fe2..bb72b39b4d1 100644 --- a/ui/scripts/cloud.core.instance.js +++ b/ui/scripts/cloud.core.instance.js @@ -394,8 +394,7 @@ function initVMWizard() { var $securityGroupSelect = $vmPopup.find("#wizard_security_groups").empty(); if (items != null && items.length > 0) { for (var i = 0; i < items.length; i++) { - if(items[i].name != "default") - $securityGroupSelect.append(""); + $securityGroupSelect.append(""); } } } From a8da46845dc72bd3a616dc968eb2a45e089f5881 Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 11 Jan 2011 10:45:40 -0800 Subject: [PATCH 02/41] adding some more descriptions, to be consumed by the xsl transformer --- .../RevokeSecurityGroupIngressCmd.java | 30 +++++++------------ .../api/response/IngressRuleResponse.java | 6 ++-- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/api/src/com/cloud/api/commands/RevokeSecurityGroupIngressCmd.java b/api/src/com/cloud/api/commands/RevokeSecurityGroupIngressCmd.java index 88e4b69b6a4..916a72153f1 100644 --- a/api/src/com/cloud/api/commands/RevokeSecurityGroupIngressCmd.java +++ b/api/src/com/cloud/api/commands/RevokeSecurityGroupIngressCmd.java @@ -29,44 +29,34 @@ public class RevokeSecurityGroupIngressCmd extends BaseAsyncCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - //FIXME - add description - @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING) + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="an optional account for the security group. Must be used with domainId.") private String accountName; - //FIXME - add description - @Parameter(name=ApiConstants.CIDR_LIST, type=CommandType.STRING) + @Parameter(name=ApiConstants.CIDR_LIST, type=CommandType.STRING, description="the cidr list associated") private String cidrList; - //FIXME - add description - @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG) + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the security group. If the account parameter is used, domainId must also be used.") private Long domainId; - //FIXME - add description - @Parameter(name=ApiConstants.END_PORT, type=CommandType.INTEGER) + @Parameter(name=ApiConstants.END_PORT, type=CommandType.INTEGER, description="end port for this ingress rule") private Integer endPort; - //FIXME - add description - @Parameter(name=ApiConstants.ICMP_CODE, type=CommandType.INTEGER) + @Parameter(name=ApiConstants.ICMP_CODE, type=CommandType.INTEGER, description="error code for this icmp message") private Integer icmpCode; - //FIXME - add description - @Parameter(name=ApiConstants.ICMP_TYPE, type=CommandType.INTEGER) + @Parameter(name=ApiConstants.ICMP_TYPE, type=CommandType.INTEGER, description="type for this icmp message") private Integer icmpType; - //FIXME - add description - @Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, required=true) + @Parameter(name=ApiConstants.SECURITY_GROUP_NAME, type=CommandType.STRING, required=true, description="name of the security group") private String securityGroupName; - //FIXME - add description - @Parameter(name=ApiConstants.PROTOCOL, type=CommandType.STRING) + @Parameter(name=ApiConstants.PROTOCOL, type=CommandType.STRING, description="protocol used") private String protocol; - //FIXME - add description - @Parameter(name=ApiConstants.START_PORT, type=CommandType.INTEGER) + @Parameter(name=ApiConstants.START_PORT, type=CommandType.INTEGER,description="start port for this ingress rule") private Integer startPort; - //FIXME - add description - @Parameter(name=ApiConstants.USER_SECURITY_GROUP_LIST, type=CommandType.MAP) + @Parameter(name=ApiConstants.USER_SECURITY_GROUP_LIST, type=CommandType.MAP, description="user to security group mapping") private Map userSecurityGroupList; ///////////////////////////////////////////////////// diff --git a/api/src/com/cloud/api/response/IngressRuleResponse.java b/api/src/com/cloud/api/response/IngressRuleResponse.java index ed4bbc248d6..9fa12f3ee09 100644 --- a/api/src/com/cloud/api/response/IngressRuleResponse.java +++ b/api/src/com/cloud/api/response/IngressRuleResponse.java @@ -28,12 +28,10 @@ public class IngressRuleResponse extends BaseResponse { @SerializedName("protocol") @Param(description="the protocol of the ingress rule") private String protocol; - //FIXME - add description - @SerializedName(ApiConstants.ICMP_TYPE) + @SerializedName(ApiConstants.ICMP_TYPE) @Param(description="the type of the ICMP message response") private Integer icmpType; - //FIXME - add description - @SerializedName(ApiConstants.ICMP_CODE) + @SerializedName(ApiConstants.ICMP_CODE) @Param(description="the code for the ICMP message response") private Integer icmpCode; @SerializedName(ApiConstants.START_PORT) @Param(description="the starting IP of the ingress rule") From 00e34cd146620eb59d03d84c515082c919dae4e5 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 10:50:53 -0800 Subject: [PATCH 03/41] bug 7134: VM Wizard - step 4 - for basic zone - show "security group is currently not available" if getDirectAttachSecurityGroupsEnabled() is not true or hypervisor of selected template is VmWare. --- ui/jsp/instance.jsp | 142 +++++++++++++++--------------- ui/scripts/cloud.core.instance.js | 32 ++++--- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/ui/jsp/instance.jsp b/ui/jsp/instance.jsp index e0a69e2f652..18060798963 100644 --- a/ui/jsp/instance.jsp +++ b/ui/jsp/instance.jsp @@ -638,77 +638,79 @@
- -
- - - +
+
+
+ <%=t.t("hypervisor")%>:
+
+
+
+
+
+
+
+
+
+ Type:
+
+
+
+
+
+
@@ -182,18 +202,8 @@
-
+
-
-
- <%=t.t("hypervisor")%>:
-
-
-
-
-
-
-
<%=t.t("Account")%>:
@@ -203,7 +213,7 @@
-
+
<%=t.t("Domain")%>:
@@ -213,7 +223,7 @@
-
+
<%=t.t("Created")%>:
diff --git a/ui/scripts/cloud.core.template.js b/ui/scripts/cloud.core.template.js index ad4b9af1c21..043f119a5f8 100644 --- a/ui/scripts/cloud.core.template.js +++ b/ui/scripts/cloud.core.template.js @@ -279,7 +279,10 @@ function templateJsonToDetailsTab() { $thisTab.find("#name_edit").val(fromdb(jsonObj.name)); $thisTab.find("#displaytext").text(fromdb(jsonObj.displaytext)); - $thisTab.find("#displaytext_edit").val(fromdb(jsonObj.displaytext)); + $thisTab.find("#displaytext_edit").val(fromdb(jsonObj.displaytext)); + + $thisTab.find("#hypervisor").text(fromdb(jsonObj.hypervisor)); + $thisTab.find("#templatetype").text(fromdb(jsonObj.templatetype)); var status = "Ready"; if (jsonObj.isready == false) @@ -305,8 +308,6 @@ function templateJsonToDetailsTab() { $thisTab.find("#ostypename").text(fromdb(jsonObj.ostypename)); $thisTab.find("#ostypename_edit").val(jsonObj.ostypeid); - $thisTab.find("#hypervisor").text(fromdb(jsonObj.hypervisor)); - $thisTab.find("#account").text(fromdb(jsonObj.account)); $thisTab.find("#domain").text(fromdb(jsonObj.domain)); setDateField(jsonObj.created, $thisTab.find("#created")); From 5c275827adb3239bdf1d7bbbd7435b0d3c507a95 Mon Sep 17 00:00:00 2001 From: will Date: Tue, 11 Jan 2011 12:08:37 -0800 Subject: [PATCH 06/41] Initial support for allowing users to change their UI skin. --- ui/css/main.css | 8 ++-- ui/index.jsp | 42 ++++++++----------- ui/scripts/cloud.core.init.js | 79 ++++++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 29 deletions(-) diff --git a/ui/css/main.css b/ui/css/main.css index 4cbd1c044f7..05a7509b616 100644 --- a/ui/css/main.css +++ b/ui/css/main.css @@ -268,7 +268,7 @@ a:hover { .loginoptions_dropdown { width:91px; - height:100px; + height:auto; float:left; position:absolute; background:#FFF url(../images/actiondd_bg.gif) repeat-x top left; @@ -282,13 +282,13 @@ a:hover { width:73px; height:auto; float:left; - margin:3px 0 0 9px; + margin:3px 0 0 5px; padding:0 0 10px 0; list-style:none; } .loginoptions_dropdown li{ - width:68px; + width:80px; height:auto; float:left; color:#333; @@ -297,7 +297,7 @@ a:hover { background:none; border-bottom:1px dotted #999; margin:0; - padding:5px 0 3px 5px; + padding:5px 0 3px 0px; list-style:none; overflow:hidden; } diff --git a/ui/index.jsp b/ui/index.jsp index 49534277783..7dd72e2b76a 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -108,33 +108,26 @@
-
-

English

+
+

English

- -
-
-

Default Theme

+
+

Default Theme

- - +
@@ -163,6 +156,7 @@

,

+
diff --git a/ui/scripts/cloud.core.init.js b/ui/scripts/cloud.core.init.js index 94df9847aff..1ebf664cf3c 100644 --- a/ui/scripts/cloud.core.init.js +++ b/ui/scripts/cloud.core.init.js @@ -18,7 +18,7 @@ // Default password is MD5 hashed. Set the following variable to false to disable this. var md5Hashed = true; - $(document).ready(function() { +$(document).ready(function() { function initUI() { var context = $.urlParam('lp'); if (context != null) { @@ -39,6 +39,83 @@ var md5Hashed = true; } } + // Setup custom theme + var $currentTheme = null; + if ($.cookie("theme") != null) { + $currentTheme = $("").appendTo("head").attr({ + rel: "stylesheet", + type: "text/css", + href: $.cookie("theme") + }); + $("#theme_button p").text($.cookie("theme_name")); + } + $("#theme_button").click(function(event) { + var $menu = $(this).find("#theme_menu"); + if ($menu.css("display") == "none") { + $menu.slideDown(500); + } else { + $menu.slideUp(500); + } + }); + + $("#theme_button #theme_menu").click(function(event) { + var target = $(event.target); + var id = target.attr("id"); + if ($currentTheme != null) { + $currentTheme.remove(); + $currentTheme = null; + } + var name = "Default Theme"; + if (id != "theme_default") { + $currentTheme = $("").appendTo("head").attr({ + rel: "stylesheet", + type: "text/css", + href: id + }); + name = target.text(); + $.cookie("theme_name", name) + $.cookie("theme", id); + } else { + if ($currentTheme != null) { + $currentTheme.remove(); + } + $.cookie("theme", null); + $.cookie("theme_name", null); + name = "Default Theme"; + } + $("#theme_button p").text(name); + $(this).hide(); + return false; + }); + + // Setup Language option + $("#lang_button").click(function(event) { + var $menu = $(this).find("#lang_menu"); + if ($menu.css("display") == "none") { + $menu.slideDown(500); + } else { + $menu.slideUp(500); + } + }); + + $("#lang_button #lang_menu").click(function(event) { + var target = $(event.target); + var id = target.attr("id"); + var name = "English"; + switch (id) { + case "lang_chinese" : + // Change Language here + name = "Chinese"; + break; + default: + name = "English"; + break; + } + $("#lang_button p").text(name); + $(this).hide(); + return false; + }); + // Setup drag and slide for the main UI $("#west_panel").resizable({ minWidth: 221, From 95756802e31a1b46d07994dd06567e7bde085489 Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 11 Jan 2011 12:09:56 -0800 Subject: [PATCH 07/41] bug 7899: this seems to be a corner case, and from the logs, there was a vol with no pool id associated with it. Adding a check against it, as this should never happen status 7899: resolved fixed --- server/src/com/cloud/storage/StorageManagerImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 1e3826a3f4f..8d3d97ce120 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -2652,6 +2652,11 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag for (VolumeVO vol : vols) { Volume.State state = vol.getState(); if (state == Volume.State.Ready) { + + if(vol.getPoolId() == null) { + throw new StorageUnavailableException("Volume " + vol + " has no storage pool associated with it, and the pool id associated with it is:", vol.getPoolId()); + } + StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId()); if (pool.getRemoved() != null || pool.isInMaintenance()) { if (vol.isRecreatable()) { From 8995a0afb6d4174720869392a48c534b53a07a16 Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 11 Jan 2011 12:14:13 -0800 Subject: [PATCH 08/41] adding more logging for the corner case --- server/src/com/cloud/storage/StorageManagerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 8d3d97ce120..84651834cb3 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -2654,6 +2654,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag if (state == Volume.State.Ready) { if(vol.getPoolId() == null) { + s_logger.warn("Found volume:"+vol.getId()+" with no storage pool associated with it"); throw new StorageUnavailableException("Volume " + vol + " has no storage pool associated with it, and the pool id associated with it is:", vol.getPoolId()); } From 04e27c9d12ee633a8770bebaf191a59084fac6e4 Mon Sep 17 00:00:00 2001 From: NIKITA Date: Tue, 11 Jan 2011 13:32:13 -0800 Subject: [PATCH 09/41] Custom Login background has been modified --- ui/custom/custom1/images/custom_login_bg.gif | Bin 27497 -> 37542 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ui/custom/custom1/images/custom_login_bg.gif b/ui/custom/custom1/images/custom_login_bg.gif index ff52df28c52767614de8211ddc4be96fa33a04d2..174f2bf83481097283f7aaa88de4fc14000373e8 100644 GIT binary patch delta 36410 zcmV)4K+3=A)&Zua0)IzIMmRZPzylxy_W%GhIzco$LOn%JH$FyKV{u1NTTxqRT4HR0 zlBhdFOguzQIYCJ}LP|bHPc}V8Mo(H$T4!u}jaFfAa)Fg=c#1evhSQa)f$mH9SK?N>nsFLq$$lR9OiJwtiX=ZeVOH^P^SY(8anO0zKe1(vChMHAh zZB$=uQe0_zil075P(w>rK}u6QLQ6eHPe4ghJVZ=BMt@L$kEV>3poo#3Q(bCPUTkA- ze^Xs+UTAlBgqTuXYD!dJc7>UOlc`x{b5B}kU1f1tV{%7PTufD9N>pHej-!2yqGfD$ zOI2WLaCuEuV^v;gVQF=Jj-z*nn_*~jYk7-hZFp^TepOv&UTJw>YkP5jlWB5&P*`Ga zc7RAwTYpScUVo6LN>W@Qp zVpm~rQ(R_NUu%AenOI+FS72;USY=mWZgzl=Pk&itPgrDBTV!KwcvoO*RbOmlYj%Bz zoNsu8Pgh}ohmlQJVrOo5P+4Y6Q(ahMZ%tNVRbFgsa(j1!mSbvjhmM(IYoJq5$&6_xL>fFh*XGa7;g9;r=w5ZXeNRujE%CxD|r%fOt?uiw9b0}CEZxUk{Fh!ZPb z%($`R$B-jSo=my2<;$2eYu?NwxwGfbphJruO}ez{)2LIcUd_6->({Vj!xcljw(Z-v zbL-xd^aDkI`19-E&%eL_{{RLk;D7`cXyAbeCaB z8Do(`+Bl<;B=&e@j56BT!dPRJQ!1%{C6pzuC?t_cg4m>$R+{Ohnrvbz zWtS(Wc_f!izBwk7QO-!Gky(Oy=aORH*`|_Q?up`_E6N#Uo`d?CB#nLA8RwO7hKQ!4 zFNzuHqntLnB&BY07-^%F_NXJNR~pJEsxInS=$WRj|LW$cb*h@Fq_k?8=#OoFs_UD( zcKT(1tCkjfs+>f=Dyf-datf%hbuLR~rMI%#p`y+jx@oI`ZkwdC--_BSk6YF%DztVw znJlrh;wtEu;F_qXw4JIe@0@8~YOj>dK8Y@wJB})CteWDPDWm2N`s~5LHtgxV)#CeT zu+h#LE|&dXJE^hLVjQf-Rr+hGuc0pLD9L4i(o1ca%Yr=dyV*JmFO>-Ii|(2gvx_pQ z7+1S#zhGiJWv+^1i?Y!E3S8`%-mZ(HyxMxptjN(W%rA}#zo& zSv9Xi=IZduGGBf1x1VNAGSXe=ORCdsv;4Bu7W4nTtKWI|S?`AADeV z^wT3Q{T|X^f4KJ6cV9U7*jG<|_t!@c{`ZS#pS}0&D?UE@@NYl;`HpM_W3HmRA4y>F90eHg4RS<*xb7A9R*h26LFn#*--UVIwLdM;&gBmQL5C3Nz7qZr3X#xk1mjA%@w8rR5w z#x}a~jc|;k9Op>KI@glGLx6U?4~(&Sxs`XlbOhjr#RJFPHf(ip70!}Go?BI&20YCo$&nU zFpud?esXh`>ii}^iD}Pe((<9M)j#HUFKDjYSpD~m8(+C>Q|d;PNhmUs#i^GQ>jYUs_K%gO#P}= zl?hk4W;Lm1#p+Y5>Q$V!^{!Zdm1|f7J5{bi)vR01D_Ga5Sdlh%sB&)VCt0++N@t?Y40>srb__OGE`?O~D2+?Aqrwxf0GU%T3W)!&v@wuCJ$ zb{AV*(+=0MmM!lzA8TIS_IA0TJ#Tuct5^1(H?DbY>vpZ%S@Q;$zm3%|W8FGk_3~D{ z0TwKHCwpDMp4Pa`&98wgJ6iXSm9^_dEPu({UHE$VtzM1pf!9mo)#7)&DE_T?c`IVN z+V{D#ZE<+}Yg_x?x41TcW^jP_+SmDVn8h7ttB{9G!;c zr%dH4TlvaZ&a#%b%;he7`O9DqvzW(B<}#c4%xG?M7}m_@HoN)%&2WyhoaapEI@|fq zc+Rt)_sr)$_qiQ@4z!>LP3S@!`p}3@w4&Dx=tVpF(U6X`q$f>(=}KGr(wNS)rZ>&$ zN;lfjpYF7%M@?!!hnmf&UUaHU&FWSQdewx6wX0`M>ss6T){Mrrt_v+|Q1ANJzz(*s zG2QE78{5>!zB4424T&*~f!WJ;HnX2iZD=?9+0lkJwx^wKZCm@=-Tt<=x4rFbQ@h;U z7I(MN&F*lcyV>x6R(H6~t?p`DTiWR^H@@RtZ*jky-vHOQxBtCwc^jPG(Qfy=?_KbG zf1BLiW;nS2z3_xb9N_MzxV*^?af@&K;15qXyq&FZkKg;^A^)%VxJ4fCj-R~WAg{K} zNsjQ8Yuw=Pp83jYp7WQ3+}%HSIKL@=Zfv{!;QO|>z$fm1?}nc|=_6;k&{b~kb4R`B z0e865ukG@VZ{6ujk2%UQZts={o#kcsxydU|@R;{p+G$t1*pL47k|TZUA0IooH!gFS z5B%)(2D{(U?(dXW-S9UjyV3K$_r&9S@l>~Y%fn80!v{U_mlrz3+phO{mp$xP$2#UW zAN8LT{qItL@4U)6ZhDF1TRey2N1K#-h z-g@irE^)|@-RLBbzVxS0{pwr)`q9B=hjdtnc6f()n1_0}hkV$Fe)xxg7>I&6h=f>(hIojGn23tFh>X~Xj{o?G zkQj-QIEj>4iGP-OiI|v)nz)Ia*omI_iJ%yYqBx4ASc;~2il~^1sZ&Bi@2DJy10wH*o(gSi@+F+!Z?h?Sd7MajL4Xb%D9Zo*o@BjjL;a3(m0LO zSdG?rjo6rt+PIC}*p1%!jo=uL;y8}vSdQj+j_8<<>VLS7?AVU(_>S-hjb<>9^jMGf zc#rs)kNUWe{Me8F_>TY?kODc71X+*(Qk|H^hBw3Osd6FoZk}A29EZLGS`H)sHk1;8eR#21o7?U{vX^%BI zlV*^UGJn~VGs%-csgpf9kTzMAIw_P&8I=F1ls{RNLfMa2un+(65BRu~H#v{iun#nu zk5O5VU&)h4Nsm~WlwfI;S4oy?`ITOImSicGZK;-LS(f_9mU79I`{eyNvxnUqJVmu{JrQOT5Hxqp~M*_D(TnS+UxUP+gad6R*OnRl6% zOWB!_DVGXKnVLD5s9Bksxtcb~mWAn-aQT>FIh14BnxfS(AOh2Yv7l)?f|Fxt++l57)^D|Nl^(XlTBAC;qklZwqdxkhKsujgFr-9Uq(*wANSdTdx};3n zq)z&zP#UE<%B1`7q6Dg?9Af;qlre=DkXqu*Kx~6Q}rf&MC za2ls_I;V76r*?X$c$%krx~F{Fr+)gUWm*qbfTZBL4`r|qx)7rcik(^iI-n}*pnneP zr7x-rYXGR0da0P2shYZ}oZ6|L`l+BAs-iloq)MvwFs4On4fS9RYrvu`>Y^(eqg*PY zU8<-sDygvA2m7#{rP`~$`m4Yitin31#9FMz%BPr+tjfBq%-XEZ`mE3zta6vEt=X!qtGW;1S*y0%qTq_Fh2G^6Z5Ep;^+OPiluK*jc0z0q- zTd)Ruun6m~?7FOFpsEbJm3{E4V5+F(TCra$sgNq8-m0owxv2|KbRTe2p5 zvM8IfD!Z~Q+p;eEvM?L7ChM`Mx~kj%nymG(r4}ox6N{^`@Us`Is4F_6|FE&!d9&R5 z4}E~HRJjjGE3r3>vhU`?fpFyF%NbD|)WGnhd*Iwh_yv4m!6>`?s*$yw3Z)&>Owd zOSnl;z1DlZ*qgoDyS?1oz25u1;Qt%G;+ws0kPOM-2KZnN4@n zYq4NDvF3l8v2(iy{JRG5Tdo9(uEc8%<6FQ6e832tzzV#;4BWsD{J;<#!4f>d6kNd; ze8Cu;!5X~59NfX-YX~45!XiAvBwWHKe8MQ4!YaJNEDXZZ14?smVLH$Xl`Iy0lfPwCP$8E!=;^PW;4B9K}*R#Z+9yR(!=+oW)wa z#a!IQUi`&i9L8ci#$;T^W_-qI{KWVW!y}9j?%)n8oDBYJx5vw+Ks>+n+p`&at2>LU zd|a_=Yp#wu$k=HOG@QnWoXCp*yvU5)$d3HTkQ~X9Jjs+?$(G#3Nnpb6V7_rI$7}Gu z%d3CGJWIcRyvID-$D=&I7z?S$i=AuW2A3SmvOLSQT+6n6%eb7&y1dJ}oCItf!enc= zu#CcF8=igezORb5sl3B`{KKit!+Bi4;mXR`xevq4%h;UF+Pux&+|Azn&9uA%;ylje zT+Zfv&gh)Z>b%bE+|KU20)~*u<2=LdTeN?JfX;)E&u(D0%6pwH+QXIWrTUA!&K%7K zEzKA!&;z;#hWySB{m>8{(Goq;6kX94ebE@5(Hgzc9Np0#{m~#D(jq<5Bwf-bebOkM z((Y^sYaGLF{Knw<4{mVJ=Koy2WhP#x7$J=IiQ)mDAg9i0TpI>T=8#%@fNMe7dvOw;1*4p)H8jatOyD$UgV zsFAwNLLJSh47_}B1!RrYc%9dJz1Mu**M9xifF0O^?bllU#)iGd#H+MnJ=5Ym2xfhq z!Q0G!?6#$x)@p6JaedHz?4W#*41<6D*`OWTqCMKAUD~F7+Nhn*@%++Wy#n~)tx5b3 z=X=lQdj;W1(7;R36br@L>;&Ld)KLb+{m5W%Dvpo-Q3Ro*YTVLL;Aui z;MicDoke@T<+}z~V78F|{nJD&*+xv)zg?@rP0GVf+2!=q@`J4<@xx7WozgN)R#|+9@>Z)!X-hV96xjo3b zjoF!<-WiLX$uJ1wAkG0k;v`<;CVt{5p5iLL;w;|cF8<;$9^*1T<1}95Hh$wcp5r>c z<2>HuKF;I$3?r-75O1=Sr({ORIc5(1e`B@q69?y|Y3* zx8$%3SKtmpE)G9l=4O88XrAV3zUFM+=5GGxa31G!KIcB}+8{0tZfw3(iQP$Fw)?OP zVBWvGz|W&QprhN&d;dJ;7@o{pj?m@$*1+2bV7?1PPUdue>6o7Bn!bPOoZji4{^_6| z>Y{GnD=^b|zLl`8wAty(<_qXo0Os%e$yhG8x{c@;PRK)h$C{nq7T%?t&E0E2>EhrH zWNzuBe(cDe?8?6E%--zI{_M?e2!@U7v7N-O?CNXq58S=Hx1GGEjOfAZs6HL3an0*K z46c#R-IPA&(BAIu{_cP99`Eu#@0dOT_I~g9p6~j;@BH5H{{HU(AMpPU1=9Y;)E?VQ zJD}RmzuHa?{JXq{ZtIEe)3~0oL5<$LZt;H{>AOJe_AU+|5Ap&(@+4pKCV%oMpYkfd z@+{x-F8}f{AM-N*Kl3zS^EQ9;IG^)6zwbCvewm!`i zKkgO}*N#5d6<*m6e+6wI4s8Gh69DLBj_*VM^I#wLVn6m|U-o8y_Gq8>YQOeu-}Y|* z@=(y#?vS7ef8~yv;CqP-}sLI_>dp@ zl0W&BU-_2L_``p`$*G>;L|>wQi}z=Z(}3UGavSkg9tpV)}(*@s-OC~9q#8k{7HTVf6n{X zfBo2>{o23%-2dPGl#dU;pX%IB$O-TH$}jcLkKt90=r4cT{Hkx|iXQISxdv_E{q}$V z_@Dp!zyG@r5TFDOBv{bkL4*kvE@aqH;RJWd_D$UP&*Hy68ToAF*pZJe7cOdytPxTq z$&w~@>DrjF&qtFnWzIxd)80y+EqZL}@+HWeF-gv3>5|;rnR)Z3+{-uT&YLlR zdJNi!+`cwr6)$Go*zse?ktI*2T-owv%uvOdrMOS+Kej{DuDQE0?rE8IGm7%+>>Sh$H8^~T}kZFS!UqDg%2lQ-1u?i$(4VL9V9W?bI%qvx;4D!%IMN?VHc*3 zJNEbQvqjRbDR?l@+aHCl*(L6`KxgsagOcCI{c`;I_3!83-~WFAGsEXB=scTly3x{N zjWpL#3u?VJ40DMvpDL_SFYPSUE+p~fiY>hdH@dA4>vs7qK5^`uPrmyCbn!(PW0Y}5 z8f$-S3?k>AlPJQMIwJ8p(lTOjL&4f2=Zal+2~s1rniO#*x+jR3yIOCLaPCDT%%Pg~Y#K;LgUx4gSBH4yW<^20~7#BIbrkZgZr)5PlZ^ixnn6?IfnOEvXWR8v)TRaRSd z^;KA7m33BHYqj-OTyu>R3UPYf)SX=lT*IQY{7ll%)?l&_!nAhbw2fW<b3V?eDl?JUw-@b_g{a2 z_kBUW_(Fg-$z=Q-P18e@T%#>5xjfdoXEy~ByJ|<9@FNYEr83JNAEm3~3Z3Mrx+FIZ zA-**OcKKzPW0rYlnrpWCW}I`@d1sz`_W5U^gBE&dqKh{AXrz<2cVL2@c@|+Lon6@( zXS0d_kUeQj%QVSoDVEP*C5r{pFfM;Je(9|ZNw#!j4M$cxJ&)d6k707c=d~m`GH(Xz)o32mgsRuT+4L{p(dg9TNG&EYtonbnJY`ESOQPOz4mdQy; zZn8UU1$BYs)!`P3?31~?!qCp0Rax`HbJu-$-h21`ci@8;zTYT{H~x6!lUIL!dFGpU z{(0z`Ct=`T6*stP5}aAt;LK@1mTS)?$2UGsLjW6N>X>Y>WFPrlGUQ3kQ~TtM?<%`Q z@NK_czUce+|9=1mP=Es@U;zzyKm;aGfeU0{10DE42u4tX6Qp1TEqFl-P7g)yQ5^0% zxc@lrm2XqFb4C$J5QTdcA#s0`!wzezbuDGFPd%$yP>^8AF}S=fMAQ>|rmNkF-}5CYi5KYEO)SG#({6nLSPa zjxdc}Q<(ZHRGm`NvRZ%Lu@r@NSj!gLZ;e6RWeJzr%VWkNTw*-akb&EpVlU}DKxUFVk3Ug(3=;qG&Nb-^S zBxy-adQz08RFDy1X-i%DQkce6rZc5!O>KJ9nlAHeDkFhRqac6NFCMIXUMyZEf9g_a zR#K^FG#}{FvN8IxjgEne+l16dKU;PwjXWY-<=P;|BA~E@&eUE=ix&aoz4VJmJt|J! z`c}BcRjzZTYhCSnSG?v`uY2WdU;X-5zy?;ZgC%TX4SQI`#y>gyJ?%FlJJh8wjVSVNl0X?n4&VLhWCi`v%2 z=2o}6K2A> zQ?eIgtbO-87Hu(T3wNV zVp0>1m_QT@i@vp3(E)FD&5T2hRTIaQ``gFb@rGtuvM%GMaFTP-TY=a$63yE zrn7F9y5l2zaj9VQ96{|`gkTs9bmVA|c`CfRCu7(ywUi@kw~1mg21AUHyM*SjSq{v!->eZGCH8=epJ`J#r=S z-0L4Zt#*czW-xf6YgeBvNveC|+CYj*lo-esF{* z``3RF&_o1y@o-=V`F8DP_`Dr{WX_2bhi$`BXxMTdj_X6OtDCQq-9EX~f6RY5V`*4)cj*$Pzg@mGPnkzPymAPNkaWD~ zUGID6d*A*3cfbc;@cd@_vVr~McHbM~mE@diH(t8$rt+e=*^+g#A$34&67zR<`r^l~ zUCPDLtI%RFY`Lv+PAN(4UcUR_UH^L6$6ofcr+w{PO#$5JUiZ7_eeZq$|9jvEU-*B+ zCw}pNZ}uPey7R_AKE}`6#w`6rQDjRW4U6^lK$}nMo#zDwxc~G(r%_t8s8i6jrJZIRYZuDLD{yulUkF zUjwwV^EC#nK^we59Lzx-+(91fK_C1;+{=F?m{Y{1+P+d;MrLeAXM9Fzj7H#F0&1*AYrIBm%tme8 zMsDmzZ~R7Z3`cRaM%}|k-Xllc`$q4}JQH-KiCTjp1G`t$B=>t3B>59~B*U>Qf-u}g zOGL5GskB?#rg#LqHCuvpB)g|Oyn`G^hkQtgj7W)`NQ$gTi@ZqxjLd&Xjoe6%>`0IN zNRSLkksL{qEJ>3*NsQ!$aFjq#Tas3i8tel*db~wJGZwa^!jqf9&C8@&Y^^Dyo%PbQ zhoi(+)$Lx#AF9RJBvqiGRG}*z-^CHBw`yo+!oVipk&6LIO`7rStvaj3D>D12coJj4o&hk7^1Wix{T~G$S0SA3h z2#rt)olpv`Pz!&(Pz=pb4c$-=QD&aCuM3H4DSEm9*rQY1}MC0$Y`ZBi$FQYej5DVm*VERnJsR8M0GP;mkg( zgS7H7$nDg_LS##Zl23Wmq}ut<)NE8#O;uH0RaR|PSAA7j6;w2sRa&i8TfJ3W%~f69 zRbK5?U;RN`hS} zyT<&_#MFmhHP&%WOk^cibWK-vliwvFf2G%BHP+j_hRM`CXkAumeZ_9G)ET%2h?P`q zjZ|$VyZ?j5K1IaT?L1asJ=b3K1wgw7j}2IoEm@O2S(Hs#m0ekuZCRInS(uGknbp;S zHCAQ4RC<&;08Pwm$kTDSl9}Ng_TF9?KWn$*BP+8k)2tpf2~@p zy;`izTCLq$uI*Z{by=IehD+sC+-z1EEZT5IR(AUw~cCom)$VSiE)J#5LC7U0$Aj*yIIX-nGk(#gj@W8h>qH z_kCaZjbHhlU-lhZqb=T`&D>*^U7K}Wy1h+(cwM-KUYk8uWewo)-Cx`h8#s zj$jF%U<$5a3%+0s&R`ARU=HqJ+O1gw9^en|-=GEH1HN7bo?f9P;1oVzyChx@j$s*| zVH&Ps8@^#2&S4$i;SQGG+#T5b1%Kh!y3QuEp}c1Sy*E>ZsRou0S<^=F_r~527xz@ zV>m8jS#VqAl?62ZV?YjMK^|m6E@VSKWJFG6MP6h^Ze&M(WJr!=NuFd%u76}pzT`1( zV-9d*PS#^t(1$h#WjD@ba~Or79R*YNV@zIUR&Hfieq~sWWm%qOTCQbVzGYm_=HC_jaw(GVwY|Y+m&hBi_{%p_=ZP6ZW(gtl=SO=kAW17BJ zXI-%O(!9B5<53WXQHX~#rEPOa?NVNc((Y~l-~Mgj4sPKdZsIQP;Z|*$M(&i32c5lC z7`4%`lsbuC2XnA(oQ1&|xa`@Ehul7DOP+o^SfDZ~MM){Lb(Ct_A*%?^>YmT9^TN7}eSlzxC2tpms#L#8_d72Tpi!S*V3I z{%`%Ra0|b149{>4-;=E>8h;=0{%&sy2ZUN!hXB1lw^U)^gmvKY7JqUmk8&xWaw@NKE5C9q&vFx20xs`zFaL5d4|6deb22Y; zGe2|xG*5FiUvoBZ^D>w59j^s32LxFNg-}Rvj9mvnP=Y-Va~W6jS{fLI8LgTVY&6o! zG;XpyJ*90<7zHNhg#UJPlVB?&e|1$~b2<-&2#<6y2Lw(?Maud*PKa0a1zFL(Dc@AVL{GHHQ6Oz|kYD+1bHhje#zf9u|NIp=nP zA9#W?jU#XvZs;~N`_lHoJ?ImybUhs!n;DmUH>oy>E>!76_HMedX zg>oo*uMdTA2!~oI2f0W3IKOjCyf*xNrF-8@GE{bD4|}-pg%V^N&wlOa zhgt}S?dF7E0RMIPg|ntb`$0~0OTi&4f_MlAeqRUOr*VFdfBUuPhq>nmtmlMqX!IEX zh&I`>?OH>QTpvDsbTQPWuumUyYpz_$)+PkTi@|0Hn-L6Suu-*Y@hfTaB+8U3SF&vB z@+HieGH24PY4aw|oH}>%?CJ9-(4azx5-n;}=!v9CmojbY^eNP+Qm0a_YV|7CtXh|r z8ySj|Fkzg2fBlNr(PK7>R}3Ch+m_-)i)}6zIV)@wSx%#7@a5FYU%7Hg{q+i$cSeX^ z6c7I*gsAXw#J0P34V*mCVn&U^{`%YM`7`LyqDPZ1ZTd9o)T&prZteOt?AWqr)2?kh zYS&Qf!i9}Ds$;N^ZPqeo40qxc%NIikyg0+`sIQ|qf5j_;afV-iolg+%RjU`L8EGXR zglPUeIkgqjs#i>rvc`C|Zqu)C|33cw`uFqi@Bcr50Sb6RIo++p7hfMSmknFRA(S3L zlg%|3MiquoL_C~?6N+@y@f8nQ-T~%ME!u?=PcQ`?WYBr$wH2Xyi!GMk6>S6qPZ1&s zHXx5ZfBN_%kUy6ur9K(hcXWCb!euNnHj!d$#1%qt zAO+`9a7F0K=1AHR)@F&dv?(HC;nm6>L2kV&D{_4r#1=)Ear8?q<(PUcw%KaCEw|lz z`z^TPiaRd3nm6oD`83vt!0cFon8WXDgy=ZY2K_ zVYL`Z*d+ayNMd2d3RmH=1F7Yntj;1w8D_za60yfgE4?(+O*{QG)KN=)WMJjIB1{Mf zH!Nthm|eH3g#vjUOgxMFgU%=AY*O~_*3&MuhjgDVP@HVD}t+1*c_OAFLx0<8~sB2=gm9+JoM2^e?L9- z)mwi(_StK{J@?&v|2_EOi$6a3<$oRy>4U~-d}cfgCn#mK)-^lZx(mL^D`f$v|0b=$ z+bwTq@>aCqB#$F;LPzM#r@#d=uz?PIAOs^Q!3k2Zf)>0W1|>HObfB+z>*JeTmeV>u zt!FuZ6AM{hfxk)UE`Mk7Qkud7e;oG}0Ui~)$b@V}zyiiYg$c|c5Q8YhAri5OMm!=C z9p{N9GO>wHd?FO1D8(sKv5Ho_A{Mi##VvAiiH<|ZB(UbQ55@>SB3w|c;)SLgO-qHF zSm7k}lpq`4?Qe6uQOj=Rw~yG6LLl**0pB=_E)uekhCC!96RF5WGP045e|#h)BPq#A zQnHekyd)+wsmV=pGK(n~iYv zAz|v!w4h{#P<}2$6h|_&f4sKs1wpq83oiRtOt8%0mo7WgMmX_`+SP85+59I!11ivg z611QOJt#sG3e8zaV-mOgNC?}Y821qEj9(I4Hr5C{zVs$Tv-^c!j;6mj)r_9lnFKnd z;X?lfFP4+ohBRg&Q-#vBrZ&APPIIc$o$@rN8&nkx!!}W(dCh$Re_Rgh-q{ghWQY?^ zbt+lHv58P_1A#K##5P_k4XbjqBW96dI>Vw*tM;_4W<4uf)2h~is-UfI|9vZ5<0{v= z(zUL3y(?bxs@J{V^@%Eg;$Pc}MZrE%rnA^aD6UaY{$+%f1Ib~UF6zFdZiK0K8A}|; z@-}IBBCvv$sZ~L{e~pnkH6Bfc>Lg;?S-!HiwXS_FY-20i+0wSQw!JNGbF16k^0v3W z{Vi~VE8O7{x45qrglz?3SkS&vH+wmYF2#065uQ_*=49-JWSF6Kym1@bc*X3{AqeEc zRS=dSuX10p4&P|VFua4C3dL*O`O>$(_PsBD^Q+(e^0&YKfBi3j11#6^>XpEDRc=aS zijFtF!nqw;S^NH&CR)+Uo^@O7yb=|b=}2`GTg~b#W^vvDyHyaP6zl);a`(n#Znbvg z6)=orEaMr|xW+cVF^+SKgB|m@$3Ffskb^AbArraCMm{oih^Ld%9^Wgu)oVIWf7J^;)%2loWVz3M#&VzK+~X)$ z;}zS$GgCG6(k(~g&!6rzw4*KUX;Zt}*1k5jv#srIbGzH#9yXOp|E*j>&bid%wL~a; zq2f2TIgA?irG>?Ox69zLXo)%++}_r^dTRJ+ZQewi2o`~)OBXJu%m3=P~$HYeBK0>xA8`G)dkJ0vz0_aNHqDa zS%u#1E}iV+(Khor-~sRWlS92_EKeffTW&a$!>#e9Grj3fe>&8ouJI>Oz3NuKI@YtU z^{sQg>t6pl*uyUNtpC`&voNoqgZ@ToNW&6Se~$8%8N3coaQDiQcttNl(F<0KrJcfg zY=ywt9=d?&yDCR@B(e(9QFxSvE%f!T{mkSi)S1}7271{AG4}t>bH4MQ|2*hJFZ$7w zzVxO)J?c}h`qi_(^{#(C>|-zc+0(x3KvwSAG2diMthnvwmB!JzjCU@Lg1cPK``n*8 ze|KTPFy`-CWwOE<46&RX;LHZLcNOn=`w4F7LkG0xO-=c?A3e}Gp!)f1um1J3zy0oi zKm6k_|M}Cu{`S8={?QNWvzuMmyO)NC`Pr5MUo}X>!L7qJ)QJjh$yZ#1tvS^-SVt+T zMP)o63BgVXoD{G{9<%uxmgUaP-CVPAe;oMv7}kXw<)vW$tso1wpbNer491`g&L9oe zVC>` zp&srbANHXi=AkMGf+`FmAqwIkmH;8Df+Z+os0CZGg~BLAVBpLhcikPUJ>Xtk6y6lo zgt(9F2A6Aikm~M&lp?+MlV^mav*)@r4o2UGW{EUR(n_ z{n8qp2|21szr8{vtRf*Q0y|D4HHsoDwqi5RBR$rmJ>LH#KIWr7?jt|;qd)#5Kn5f& z(t|3*A~Z_lC=%k8^@2HcNvsuNtNn)m1eG|6*~>koo}ptYE+Qemq9Q~ie;m>SJGvq} z#-c!;Bub{FO0FbJwxmnGBuvKSNxotU&?G@dVJ!BComad%Bf*g-V1ZA$E z6Gvr;;N-$8a->L>pm7-_0;R6-TfK;5 z*Z`h|s0_V)NJKhSC~QJge{!TN!s1=V;a7GgU-qS6{v}`rreF>xSHhw+RwDs!Lf%E< zM8=w0-cTok#91PUScSuGa7ZY;f)CmyUaA6NekN#!|E6e;CTRvFBAg~_rlxAHCTq5) zYrZCI#-?n}CT-THYS!Ui!lGd+;s6#P0PbBwUPCsfnT3T-n$!zNfBZ&tywrCw7&=-) zCamK~y5b-z0vsa3b=szOZYOtkr+0oQc!sBVjwgARr+JUnH7kb@9&`QJ6N06=rV3^?BboL}zu5Bz{UIY-VSDPAG*| zsD)lAhGwXSZYYO#2B?Q#C<=s=;W-q4zNm}Z0)7@EL#_GH5GKr!6f1DU&X#A}}e8MyZreDV0{Km0l^9W~r8LDVKJsmwqXjhN+m2 zDVdh3nVu<{rm32)DU~iMSQaEa)+q2@S$~>?Vr8Ss<9ZYrmCYL_BvP2MC4DxypY8~}!b zoeHP_m}Q=_RFJk>oOoeVWst zE-SMxD?vizc!^k`0UAS{YH((Mqi|*`OKIbs!YbX(rNE7YLJnPYMrS)pXLKTJqAEfr zWI~ZPTXi~Wv(78M*8i)$-YdT5tG@0lzm8}L`0FzcqS=w)mW7gHqUwKUtHSQ+S+<%3 zPR9}I9l&K-H8A3vp`5we!gRKyC4A(rYV1c=!{IIHllH60o-E3ytjex`EX%g+yn>}5 zLhE@=qjIs`Zz5kqnnOd5g3hi(s+Ji<#w8N!oz1COs^QNYE#j`SE0WS`yF#ZbEa;VS z-zF%nye6v4X06t4E!TFf*P>}Ugss?)E!mc>*`6)hrmfnpE!(!O+rF*ZA_76itlZ9R zLEdB#3gJlPoz9wrZg2=L1E#8~zG}~IiMj!ta~|A^#aSyXDLwEW%8??U= zmfb2l+5*{fF5HtJJSl%}I@Ily>1IdD=rr0a{@@*|`fc8+8}VLiw#KEzuG`Q7pe3|| z^!{MRsMJux?yytLbc= z1R$41LfZ*lCXBD0vD*iRL$@N~5xx|_b!@w?BCVD$$nL8DGARMaunf;I4cD*@-!Km6 zunzAq5BIPS|1b~-u@Db25f`x$A2AXq|FIGeF_VTWJ=nsVKB=fm>nz~R1Y;)P@@>NM ztj|j9oKo-8{+WOE;?MCqmoM?fbOfoL2u>J}uOc*V3y-8NBPavLPRG45vdA!y%JC@si$cAO@l|>@A1%ZNy@x;5y(DB4IC3E+axG zH7ddfmmDXks>&3oSg2aZit(Y2B&|j-9^3C5k1q?uFd~27GA`$`F7Glg_p&elGB5`- zAunV*t}BV`&PIs)7( zt|B|OD?2D^I7@FvE5b&PLzW@mB&ahS8JzIqJ7hvNfV5oFHCpc~RSz~{ z7q(#^Hex5XVlOshH@0IxHe^ROPa`xmVs#@2?~0LeS9^6>&;KRx0icRSFDz;-JMi8l zK!5Q4a8s*YR!9M=dWC{HGwD6w^*GbR_a2|RrQ^pgsbu%IZ~r!M2e)t!H*puYaUVBi zE3zZsWI--8X7}EAZ1z`+^#*grH>pC) zsP!J*SuZql$G3dXH+|Q)ecv~J2XqtcHV-D&T-;+QNRP_uN*oKg3iO z@2%lha^Lm>I*erj##t*mZrlN`3JGBD0H0#!&hbue)s`;-@HYY2gFVc+Y_Io9m+yba zxPp@qKq!AVIfEkt6u%t~lmL&xevI6gPQdfS74&-$e2 zx~}g!ulKsI|2nV-yRZ*CuroM4JQ)uBxdESBl*yEaM>7_8GKN>|G{XAQZZ1jEDcf!4 zCu5;?&*_uW_=MB?pvyS1pF6szySlGCySKZ$6FCdSlORDBO8dThgTT+Wj6(P`Ixcm7 z@+V7!KX7%l8*V52U@P2YE8K!N*uyPk{4LOU#<#Ztelc{mf+|e!SmIB!MX$vR{Kap) zJ*d3Pdjr6efI%sL0AE0$zYDm$Jy`Xz>!ys#D73m`wQ@DYL;N&{;wy@L#kc&$)A-8Y zf+`H12B$epPwzCwdBu(|*k8OWf_>A!yudTP-QPXl=e^$VJ>U1e-~T<}2fpABKH>Ab z%iFl)TDCDKqS>)s0~>BFWH#kuw=0S~J#75TgZ(Y=|Fz?!*S_uFKJNSdzz=+l8*@6?L*gGKBgz!ROMa{Opi`25 z<&!?xzdhW?UGK%&<#RkiPP}ceWAitE%GbT>yL|3{Klq2g_>VvNm%sU+|MYeB@ zWKzL{2^The7;$37iy1d|j8sBo$&)EpwtN|LX3d*9clP|5voX=5K{D-YFDlfjQ0b`> z^xBdjunVDFU6fYELPZp>e~8?yCJo73sA4t6l$d{ zRa)7LqyLV>Xt@ZPI|rhO=pk;j_%z&*!wxuR=jXC9tywD-hQhXB1D5ac|$||kA63Z-8 zT#>~V`En{Xs;Z(VDjfOZ?H2!T3(zczc0{wn|BO>^IVAOJD9P%y+>_5f{rnTqKm{FC zP%cFyi8PUam`dxZ)xxwzz_iXpb4NVyS~JHl;}p=FbB=s2qdc7?l+;p9Jr&hdRb6#Y zLybX{q`uNo>rtqL+)bn9?89wRhayyNv@rcHMnF7v6Z6WrN;&?Y$S@eD&Rz-+ul5 z7vO*e9@ySy4CYe**o`Dy_#A^Bez=*24UQ0EiXn!Wjf)+oup@~tCX+rzA!2Dhcs_=I zMUuIRJr-KyqK$qy#Vijnrp5__!{i6#U7jNvduml z?X=Zio9(vU2Af_H?0tuBx<#v-jd$+VJKtthmV4ib&tXIEu3M7@2COS z*YA4`Cp>P^U4I?+*kzxc_S$W~9rxUI-<@~MS=agWlErSl_u`E|9{J>zU!M8qoqryB zvQ-bh|Lc0AzaIPSwcnom?!EsWe8dS~z540HKOg<{)nA|e_T6WHYU-^&9q#w-zaRhn z_21w1H~jw}fB_WX00~$?lVC31a6l$G z+1XBaz7wADl;=F@SxQ=AXRjB>}lRiiof0gT8=~`F2-W9KT)$3mQ z+E>5+6|jL7EMEQDOMwm+v58geVj0_5$37Oak(KOZDO*{~UKX>NwJZ_fz*%YXQ?sEJ z?P$kJ2GgDvwW(F@YFXP_*S;3Ev6by?Xd3w4^an6@MW8TOf6li)}gOjoM(A&+GYlfBSwZ+zto%j%*Rzxmbg ze)-#9|Na-i0T%Fp3IB}U<;sIz%UH%c-rHUUKN!LhR}$-!JI5rSN0D_FNXr4@EFo4eht;V*4Mn`6U}RJR*QW)O8%i2~?dj9vdeo&pz(agAmLR1%)g5t@ zg~)P|9}y?c(5{58n_X^mGy4eXR`SqiRQqR! zpB&|rv`Q9#{dRb2i6Bg5aI7L8{|bUE-ik~V{VPeAOVa1EbOjOTy~s$d$#Y(Ht6v@K zS=aj3x!!fJe;w>$&$pjh8%#O(rYe>wy2dR&m$^g&?$v<1K@L(4x^Mr2E(|#bl2_Yj zvHuBtj92aD-wVoLEQs(ZBxB z3+DiTM;3RWryqU$-yi?^*Z=YT00)o&3(x=$Q1_;9(3C3T;_Lv}M;T0m`7N$YTD!~tE(a)%l5pz)&1@R1e zlln|3e-RtAQ5(0B8@tgPzflI&%@4@Hxq`{gMPD31~;lTs;{k{LHr9LdqaJOww*B0(%+6JDVg(2o^R!YG*>Dto!Td^51W(Z{(b6_&(kmx{?xaB#Ob;ZH0UY`a7vq34 zi_3)JAU6B24ergfkCiv`5brP196O*OX1$)J@;i zGHI~-#mcHLw@#aTHP!P)l@BtrSWNl~vDDP-QY6 zXmbXNKubBHQPoa2I>PkGv`qiAQ-_sUi`7_<6 zRj>6`qcjAlv`P=PHnr3h6M_OY&MuY#*ZPbGe^FW2m0jD_UEdX6jnx-7GD4SuBgEu4 z?CvT?wN+rb^;T^YS8>%ug<&1u&l0q4BKGcF3DO{eF<#*m0xlL~Ggf0a zmSa2CV?P#TLsn!*mSjuTe`HSq`wSUI@=o&=^vbVKe?QP*pOjGln|5HQ zG#ILMa9tH!WpZKXa2z5JEg*LKy7p`1R&pnoax2$zFBfw&S93R)b34~_Lv}9}&|WPt zPYVM5*f1syHejQ)4FESvOH@8h6kua@6vl5G!cgPD6eA>69lF+c_40FrS9ph)c#GF~ zj~989S9vd2FJ(|2f5-qCrcDv>bQ4auEK~PZmo^02fO}mxL<{#?rxaFWRsCL}8phD# zD)23DtrCvGcfA&Pmsfu0mwxNle(x85^H+aiwt0#3c@=CV-NF;Aw@<%SPz9EITi12B z)o`u!Z>2O~P1Gy5As0H)62|ZrUX95r!9Vp9e)kuILs*1If0%?z*o04*fBy}@Ypu{( zIRZ81;#5cQTd!4H6Ig-0S9TvbN<)-TOSC4#p;5=74392@Q*H0YR2@_}eo>f-o7jn; z7>c7rkPDNnQY3%*k1-ZZ7E83zj8ly9spSgh>GPY~gp%O^BLaX6az4@EL`IVctRn57F2X_>JSQOJZnCVfubqV>z0qHQJ|t8mNO>sE3-Ui`svvj~c0yTB(+NI9oYA#Z<-c5mZ!Plsq5OV z?;5Z3TCew-umAhnum2jb13RiOwxel*s&C;@+2NE^vK2+~PgfbG0k?Hs*^I+iN(J>j z^HF~e)B1LE0UpON)+&J(-Z`G-x~BshwNqQQSDUq4+qGXCwqv`lJKCeGuWk_}LCqh(vW8yX3^ z+q=ITyu(|($D6#%+q};kz0+I0*PFfD+r59^8@}UPzUQ01>)XEX8^7~gzxSKJ``f?& zo4ip06fG%X?4`)kb)ijw|}^eH-R0dA=Omfz~9_`U);rC9oA)&3oXpkw6@Gy>sdgKlo6V;ogWI=j&E0)b;lSA)-o=|4l3wYR z-r$#h>5(Amkv{4>9_pRm>b;xkH(u+vp6k2b>%SiC!(Qyip6tus?9YE5?bDv%qu$+# z{$hPOq*JXG;E^4&jlsV(*%>_p|0#V2PEmWmmx0rAJ);y$x&0N!m>RsoutuUhP9)^hclcOW*WQAN5mT^;bXj**?i3U=^wv z*E&Ji&$oyb!RQu06C8hi8^WPh`SjV(Smup;J+Blu&zgc8U5GpJYANsl*ddNnEyb<* z*39)7RzbjDJjpFzy*pp&p+3Bspu2gx#^3$EnSu4wU;Wpg{oCLD-yi><2|GiC^57))K5MvLLLMatAlpuk%P z4%}i#^47Ra0u_}!sp!_SN}c~`(K?2c)+$u1dhSF8G^kJ{M2i|diu5SArAr|&WeSv= zP@qMrCcTP;88azdyL$Z!HmumOWXqa8>s5`~lU`UDe>kwC>VLVE`J z87fvjb*{=;=>WS-2c~H@*(z5qkeOb+nuPk-MR*k*PJCT4UOa{sEykPVkzGlXC~qlK zb|7S?kD5c?YGtX_txA=YF`tI4(y>+Zd|F?{Drokps3Lp+&Rr<0@z*K$9FKl``0$a9 zK7$4yf4&-MjqBULk3YZu{rvm;{|{h*0uD&vTJS*@lyx@UbWA6ijdV*&$7Pbq|4O-_ z_R2P#Y*ZUVAxzYeZ5y&SLp;As@)2jdX~qt4ytP7MNF|9xk}}6s;#p@lRp-+@H(6%g zWa~{O8F$!`r{IFuE$NebLxzXMcah0gV3k&0e~D$5T5ic@mtKA;;DJT*)RRs&;nd(X z5uOyGZpV!?+He_8a*;+C)fN$K@gy``Fc$st$~bPWC{T`V(#Ttc2Po4?G|G@PO(&Nr zX;UZANq3A+WLh`gdgzsDQ<4jgN-C(MmZ#p4l%W^YcTqw{C763nL2IqH-im9ky6(zr zlk-_1fBtG9dv{7HKq!Fn-RU&?~tp zN79V!cyh$=eGZ?sgRh`E*Q=%Y=QAahPkB%OndI+i^d9t5O9!11V=l1dB5B99C~ zkVC3R6f^Xx^_3dez6^8BGS5tN%{JeRbIv;Nf6Q~wKG&*Nd`6auQ)VU|8sjaVv15>F zs3mkKL?AwN@14S!7>>Roy`)=lAZ@){!JZv+z)I6FxXQr~A5BjytQ-Wuz!(8}!hfjWV(H;QWSB$#W6(qR|sajnXsIcwvq@ z13l>B1bW;{+fBUH%2pyVY|4<{5m!`_e`Q>XIWd$aniiCkS#IwqVmc5|-oqAP07H+| ztfn=uiOpL{&Txven|utK(1HR;P<Dbh-J(LdSfoKPbQ-AT zi8Us=Bc_RoW``Akdq7n|H!RO-Ds4& zDTpFD6R_b}sF8|P*M=lXk$IU2H64k>?81??Oi~C=Ea}&xD%z-MSagDIlUa|^J=n_90u*F1^-HjlOtX)e_n4fiXlyC00VNO5Ct><31Q3VBpRhWT5>5=yne9>M)AB5 zLS$pL4)M-{2`TZ38tIYQmT*M~Z4_^#0b>}mvoi%_l#ELmQinPSqKaDJKrsxX3dQJ? z#sQ{RgITa;{D!(qqy>aA8=jx43>vhO=79^0+sE7}Cd26c7*sz3~F=$AE6R^$9PByRQlZY+CCrHZXL zZA@Wy>eV?R<*dLmVF^xh|1#5@tVS*}T_H__gs!9xTaJ`$W9mS-Ost-YRNzx(E1wy^ zBdE2lZ;k6*>w4F`0`9ewQePu~?Q2SCW7~X8FchI73RG0*N47lJZtRNOxE_z9$}0)F zT>C++UaiCyv}C*!^1(vCVv+UiDw}q5XM{@h+yaTHj8oT{fV&8}LFQyVbKzOh)N{5D zmpDj*JY+<5G`OY%q_|C&wx3`pDD?TRHpGkVl&gH@EN{8XUk>w_%Y5d4G_SeMZ@zME zb9+AVVdhWMw!KQdQc<0W?r%!^%H6c$6|eo^oHDcx)0`$8|Gt| z?_4mh1^KHL_G&H(wN*IDHnu6Y>Lz3hySHl^PM#HYNbVXg6n30Rq3X$0N`A#{Qv&CN zFTCLokNCtZe({WNyyIzqE?#X=VX~5ujNidcz)78{Qr{9=bTcz)Mx~DEnz<)dag$<+ zb243zZ2bo-xTmbDUN^++I&_O8YNgQxU`JNnW<eL-MJ9ovaT;1BBO3>Si6lE5h#G~qhHVH!ZfF`31cGaqE*YYHBxo+v&{mpdOmri1 z*@1m(a9G=ygN0~_hlq%YsECUQc{`^D&p;W-lqo!cIy4b~KG889s_0hQJ6y z%NGg8*GNvYhQ|j%uq0HBR#G&PDL)YuNI)M{ux*TpjoGM;+sKXGXo%yb4SWO*jDt7P z$P*1BRK%oULec1V;)Y$cuw$*HZmu&9n@~IWhKg`Eb^0=K6(WkZQFT%mI~dkU!&4A& zI9pvva9mR%vv_Y+cM$p3i*A^VTD4WP7?D`li`yV53FlCKHC!X5DcvC?*k_G|NPdl= zlh0x_e>3?F-bQ^skrUOSL#ESUqJRz9&|pudP&md*%5eXGkYR5VPQ6UZa+VZ?RgVOWfk}~XIE-;ZPsn$A5lM9)#F1U2 zaL%DtO*Rjx;9&6~O_cBrO{tli$(fz$nV$)of1xRwqe+^jX_}{rnxFX%2$p$HloLd= zAb^CGS(%krDVw}iN;!f&VEKe=m~X;|fgXVxhgMIHR6%2DLBF6#7HE;9bsAHLNf7sA zq)`y?lwz*+T7dbB8A+IMm|_mMoV4S5SvQP`RB5dALPv*E)Zt(Ywq}@_nnGCx^GToe zlL2Hgf2yAK>alrRaj5I+Nq!6tak*+6<2pL-wS5Kz(NZE;RlduV& zl}ouKL_M@HJ|~Ffqz$#!2o_4FWoo8pil%9*f2M27rfuq`ZwjYzDyL|Qp*lBNJvABF zK?~{uO(W{3eyWuynprr~PC0^r76_fdxqnYOaWWc_^>#IcHj!4R5nC8HIZsahrl;=Qwo4p_jZKoD^iK=6Xq3*K~}Dd~=9};)!n?nMtifX(h2R zl(-WLMyuszYs0Fr3(K$#>#z?Cu@Nh=f5GaKBcOB6%5B_su!Hvv9qX~*5RTGxm8j4y zXJs_MXHVO3rBire6mnsn^?&Ztmcr03f`&o6Gh#@ZHD(!G$#{IlnGo6udxR#hO1f_! z$ejF^E)VH+9$1Scaa+`b3_uZXZFaEVuxk@rpOV0}UF-j~UkkQjE4E`xwqk)H~H8Q6Bw2~Va6k?`t(B|(J0aSXJ1mB8?^iHmE) z&9&yw3~0(JQ^ve@ng5yON5Fy~InrjgY-6nZ1T9vYWD5({*mj z(HkOhXSYa0nlTPpcqgmHil=o!tsxP$p%GKXf%~S0cgdYfni@RHhL6OZJqo1+>7r@b z|CgsRkam_^II#-Y@Tau;4cRNE+*`eKn+6%I!5hrM9qhp$48kES!Xr$=C2YbclTc?M ze;VwP_W;EA5DS5tqJO141OrTr(qb~E#LF{v8{)a-$|q2qLH80HR|AZ8NRiN~n7Bbf zvYWqY*@i(nopc#-aiVq-^bsM^I;u7gq7VylEXQ%2nJU@CcZ|n*tjBxI$9?R_e+Ss$~&> zvZ%=Ci@ylFSl5cj=ro;*A+v>H!-M~Eo%19l}u6{&qGRQbz^Y$W?e~-uYjcV2FjhxC$jNptECW)EXjaV z_o?T4uT^Rq%XxelNsAEoHNrzsoi!Q{$HreZ573;IaU8@g*~}{J2$Pyn*(s5~ zskF*x%I%zx1CeOwHe?i%cLhTWvGD(tu>ibvypmj-!6%a}X&74#(HtxZPswuxbUO3k z!00$`A9EwS^~PI?4HT9c=%R-C1kU{mx9r|6)^z>Rg#eSEX*F7X+Zx=pEp4J#c^PFA4R5SatJV|A z17qx29qH5$(NH#~e1%!aXM*W;ihaL3>q*H~fez`0fC8L7dQZg1*&?_|Zj&@$C06bB zLdhflDWafION0v8;F7CN-IFeA7*@b--CR2i*^Qz+@nl;$-cJ^3)8WR0JrC&3Jo&20 zp-X7W*LF@dXdpBiqQ$K?nwVMhvpxGk%QvK32SK0II;b?w7Di(>f|cMM(_me^Vx7Ss z?%Ym*lbmWbTR!H_3=Fgh==~fED0&R@pp{uU4+(XZf&Su}K_O@PuBo9Ag$Z$HnSULz zNtAn#1sI;dm@cv7oP{QxuP7SuRED9{-O)T_HKq)!5XYqe|La5CV6gz}u&%*;lPqf( zfBxn^{0(so3_~o(*r3Rw5DjqbyD(!twVnn5f9no!^hb~MNw4%v&-6|2^iR*_ zKHLb3Jn{Fy@7Tbe7+?P#v+28o&RZXE&!Zltnf(fHHyW!mI}IpW1Qp9C(Qr@tf9P(Q zA~a8jw6!L?VecmzsL^oo{d^C+Z1uM81Vo<(P;dB$kNAnN_>0f@jlb})oxzgOlE0h= zExpSSY@+4;^NWn=i7t;XGKDbfc2i9dwJ0MKa&AY)Tn%~TBIGnj1@EIpd|HJl?UY*( zM(vDj=w2;N_h82y4BL(`{KHTDf5mV7$B+DoAKNQk`74>hEzSEKYofL^55POevfh=J z2r$USTmU|IC74_Wm5Vpu#O*eLJl@|Imt3m{>GceKOQdSEIp`(o)o|Ph9UT10umAhc z{{Z1c;6Q=}4IV_8|4`vVh7BD)gcwocM2ZzHUc{JD<3^4Y3l%DrCQT`leYGpLjxJ*J1JUyUH*)*vKs7h7V zNvqbXP+R4Sl*{Niu-jfmYRYbGB(vMlvV+SGD%DOqSBZ*suT)CDE$0D?Rgz?(j)e^$ zMx0pjV#bXfKZYDx@?^@De=T3e?BpXz!KF^JY_zgbOH{P(=KcIR7B-yoXwKsbwJ6i1 zO5qNbI!)`(PRf2(`q^(@KdxD;vKto`sZp?DlO7$Hylt+uN##P7Dav(ClqhlL?Mygx zx?zyP z&Km5-vz!9!%rl!scXu*p`MDyHJUz)P%xHCfv_7V5@fPTC!d5eN-3wLvPvtj#4<}Qw`_93Bq2ny zB-Zq*&@<0g%WD;?f94S-8km&v@HVHQit4VMc*16tnaqr(H?01Qk+!9z<8iE9n#(0R zrb2v*E;eZbuS_H%9EeLxFU2%dO*iGVQ%^qyHPiqL`V!2&@+wkNmI4#yCsA0MCz?v8 ziE+-T;C!l+|9SYrFsFNpyeFDJhpm-HrpRInu2ymkD^W#}e{w3TV;;rO%vDRGv{F&G z<+fXIzXdm3amOW>zEjRcx71QeDl)gHLF=StShh8Xc`mPJhdWj z!>)=w>Zn%KTB^B77NzaF4R^}PSAWN3x50B6ME8_(KL$Bukw+%EWRtt?_!MoPmA|%EWtZj!~)Q8n!X4XBYl4tC;S>ushTs zR5|5yPe#uKufGO6Y_Z2CyKJ-1Mmue_*JitIx8H_4Zn@{CyKcMh#yfAl_vX8AzyAh2 z@VMV=`OE)F!R+QFlRoPSCR!^w21Vc^=PephWcDm^fACV0kSF3!q`4BJ4(RO`5EUNu z&>a(nw$W*Tt7N8{BK)(r(?GNiA~omObtz(Xb!S`TB(}#Dbo{vF1kaVZR*Eq=ARrd3g2bdJ?8i4 zzkmP#e+OUy1vo$g7SMoqi_i0(Hz|@(&n_^bMRpwMy`7n86{s;%hN7_w;#f{Q8nQ}N zXcZS4#bqqfQJ7VLGe7hl<8rk_TsL-iKpNK2hBw4v4t2Og9`?{}@{yqe4OfyzOmGz_ z;>qPO|5Ov4xFkr5>&$BO^pX>8P*}e4jaBHRf0paSC}AmlT_vXCn@Z5)a*a3zw%nyZ zAFizhZ-iqUyb(QG!(_+s*OEKoX)iA})0oFZW-^tzOlCIInRGNy3(DiMX=XBI_+!Z^F^49- z=;dEG0Z%FQq8*$V4JKN#lXf~`GttS9C_j>)RDME>Te9g_D~skc^|?=e_S2vL1ZY47 zI#BI#Zg~)TWC(MGF*~$z3|=We$4RX9P1l!>tNZoQ%~=tawBY zVI`z0+$iXHLOt)j3R@dv=}on|Rjzi`t6v3cSiyR(gznU=sbZ)mY3EZl`jmF4f6)ua zF3L&mAI`rjw7N9$t`?NwF5zu!lu#Vimhs#?~>XWi_ipv6e1(rM0pQ zbu0c<%GSl*qfOZpV^`+qn-$TkHAy@zFn$HutTy(IjDT%yWjkBi*4DPS#cghNyIbD& z|JJv^1#WPKJ6z%x*SN<;ZgQ2oe_ZA^*SXJyZgizPUFQC=Y&+ehT6=o9ie?OY84FXM zw&#--vQS!P#0Dz<0t{5N*S(^61~8E1wR3%Kj?~3(e)YRw{`S|u{{?V>1w3E^7udiD z9&ig7pq zN>4j%qT;k*NIR0;4r_0Obfmq^XjZq{)vtzitYtlGTG!gvx90PsA01{oQreF!>jrBz zooUBzY*j6x=S+gF$uJ=)p{Uk1DR4b)YFFFZ*T#0XwY_a_V>#Mje~u%#|Clw_;&IrU zer&s9fy+CSS(k7Wv#tvu?QYlG-uK3LzV*Fte)k*4-_A3%C5`GnvXs+<d32+~qHa|9Q-1 zK66$tSi!U{0BrA@fAgFBu>f(jxy@(H;01fk1dO}N-N+SR^xwzu8wZ-;x_D~Cn>v_iyf7o%n3y^ruci!`#2Yu*8 zKYG%a-t?zOed<-ede$dC^N`Q6ypat1A7ft6t;c=tb-#Pw_uluv2Y&E{KYZe+9mr&l zJj+*2c_w4t>^Uxe^rb(2>Q~?T*T;VLwcmRjpMCR~hrZ*BT=uaezx&%qfBOH`zkc?& z-~I20Kk8o}e`M!>AN-d$|Bd6nfByI1|NjTT02DygE5G|gvH?WE1XREUWWWY=K={Ky z?>oQxd%(l5BNbZG{ZAQ!!%UGHDtp!bi+4f6eI==tV1y@ zLNdI-I?TW_oWnl^#6T3pK_tXNG{i$h#6%=RKUBg+RK!Pw#7LCHNu(-RRK-$98nbcZA1yf0W00q{n)+$9u%beALH%BRYM9QR8%B5t=rgX}ugvzLt%BiHvsU1k11#%dsTOvNX%H zM9Z{P%e7?7wsgz4gv+>;%ekb>y0pu?#LK+Y%e~~wzVyq#1kAt`%)ung!ZggoM9jog z%*ABP#&pcbgv`j4%*mw8%CyYO#LUdpf6UF~%+B=8&jiiT6wT2j&C)c@(?reGRL#|7 z&DM0y*Cfjdkj>eo&Dylh+r-V>)Xm-G&EE9Q-vrL!6wcu!&f+xA<3!HnRL%`9N)XweX&hGTi?*z~A6wmP_&+;_S^F+_||5VTQWY6|=f6w=X z&-j$j`J~VKw9os*&-~QS{p8R7^w0kU&;S+C0VU7^HP8b^&;(V`1!d3%bbNnI>+WKK0W-1=K(l)IlZGLN(MwMbt!9)J0{~Ms?Ijh15uu)JgxP)JnC~e@n&GOx4s) z<W!7eO)@Oy*XqDD!rPg5$f@{UrY}M9n<<@TX)^7#Z za23~aCD(E_*KC2edX7F_1Av|*nkz- zfhE|2HQ0ki*o0Nsg=N@=b=Zf6*oc+biKW$A#R;mE6gtf85Ho+{?w>%+=h@<=oEo+|LEw&=uX$CEe0B-P1+g)K%TpW!=_w z-PeWP*p=PcrQO=K-P^_8+|}LP<=x)(-QNY?;1yoc{ej{&-s45y-sgqh z=#}2-rQYhb-s{EQ?A6}w<=*b~-tPt9@D<~-}PnR_I2O)h2Qv< z-}$BA`nBKt#ozqZ-~Hv^{`KGg1>gV{-~lG!0yf|SM&JZi;00#j26o^FhTsU6;0dPS z3bx=2#^4Or;OF&%4))*=2H_AE;Sna`5;oxzM&T4z;T2}#7IxtmhT#~N;Tfjk8n)pZ z#^D^+;T`7Te;)SX9|qze7UCf$;vzQUBSzvRR^laQ;wE=4h7YX{P3Cw&rWb=4{sHZRX}~_U3N}=WrJ1 zaVF<-e>UfHM(1=^=XGZ1c6R4?hUa*e=Xs{*dba0##^-$2=Y8hqSnh#;2Iznm=z%8a zf;Q-bM(BiA=!ItJhIZ(OhUkcv=!vH2ini#B#^{XJ=#A#+j`rw}2I-I%>5(Ssk~Zm+ zM(LDR>6K>bmUiivhUu7=>6xbKnzrei#_62af9ak7=INgH>7NGbpcd+(ChDR#>Z3;L zq*m&sX6mMP>ZgY4sFv!frs{;|fvd*qtk&wS=IXBY>aPatuommFChM{`>$67dv{vi2 zX6v?g>$isMxR&d=rt7-4>$}G5yw>Zz=Ig%p>%Ru*{1Eg;U@0lHtyp_?&MbP89@Lw(jf3?(Ej??dI<8_U`Wn@9-AyfAJ>o^8YsP^G5IVR`2y@@Ah`@_lEEImhbtd z@A|gy`^N9(o`e17@Ba4h{|4{?7w`cm@B%mR14r-#SMUXA@CJAA2Z!(om+%Rv@Cvu^ z3&-#b*YFMJ@DBIz4+rrO7x57%@e()j6G!nBSMe2R@fLUS7l-i}m+={=@fx>re+$=x z9M|z3=kXr*@gE2BAQ$o>C-NdU@*_v`BvD*Y|zr_kQ>He+T%07x;lE_<}e1 zgGcy;SNMfz_=b1*hlluxm-vaN_=>mqi^uqk*Z7U+_>TAZj|cgX7x|GV`I0wzgBO65 zSNWA^`IdM2mxuY7m-(5efBBlX`J2c2od4JPo#*+U_xYa(`k)v3p(pyHH~OPT`lMI- zrDyu4clxJ?`ly%ssi*p?xB9Ec`mERbt>^l#_xi5~`>+@Lu_yboH~X_k`?Od4wP*Xb zcl)=8`?#0;xu^TOxBI)t`@Gltz32PB_xryG{JM%D4Q>$NbFK{LSb5&iDM!2mR0&{n01=(l`CnNBz_%eFb3s)_48ahyB=> z{n@Ae+PD4N$Nk*b{oUvN-uM0A2mas}{^2M7;y3=|NB-nj{^e)>=6C+*hyLi7{^_Uw z>bL&u$Nucs{_W@ffA07G?+5?z7yt49C;##{|MN%x^jH7&XaDwh|M!Rg_?Q3rr~mr5 z|NF=P{MY~e=YN1uAaEeTf(8#FOsH@n!-ftYLX0SJBE^apFJjE7aU;i$9zTK%DRLyq zk|s~0OsR4u%a$%*!i*_%Ce4~QZ{p0Ub0^Q9K7RrYDs(8(f1*Z@B2B7vDbuD-pF(Y# zXDZdIR$@ta&r%&YnMm4lQ~#>C&c8qfV`QHS5-{ zU&D?qdp7Obf3|Pq&aHbl@7}(D1OE>$d^qvq#*ZUUu6#N3=FXo(k1l;W_3GBIW6!RA zJNNG1zk?4iemwc|=Fg)~uYNuI_U_-qk1u~d{rdLr zn4p3SGT5Ml4?-BBgcDL&p@kP>n4yLna@e7VAA%U7e~2TJSfYt1qL`wJE3(+4i!Z_$ zql`1sSfhw`rS)`FiBAKL;OETG{lOfs>rIb@rS*4X%Vwt6u zTXNZ@mtTSzrkG=rS*Dq1qM4?eYqHs0voKb!xCGpvBx5ttg_28+pM$CLL05L(^6Zl zwbx?1Dg)bc+pV|Xf*Y>5NeOb?4?+p7rXLebzba^J!}IW z-UhonFLoUTnZ+`TRTg_Is4P}lES-Wn1z(xAWWK^S;Nkswc%M3D>TdT>e1H0UKcD)} zsUtV%n-!TyZ?SqBkKSf$`;Xdq!&67?B6H@*WamWV$kZNp|HyRL%+!cOmXdYMsYq)& z=GuoJI0ki-rjNM~)MOp^^wc#S_a0=Ox`*oRoIdU!RAdmD3^JU8Q5sIR9Kyp77( zusD@-It5qo4!U7O%E5zsmr)Dkn7$25@;GNwfdYjLJGo&QC2(f7yx?72#>N%39MgQ7 zreH6jVPk52z_iX(@E);m<0>A^n zXLG29N_rn|?GWc&9w6S^cni1A7&wY5z^@f5wAk9(;WAoi#y zH|0=*mv+gCpK3BTZ;dWj)M<#H=^8fY)dwxRO~lXjeVe!QxMTGY@t|RHbADHFtQkf6 z!j!S4u$z0Cg(H1wY1l&Q4PItbNotw3Z%gqI_X-an_1Y%4lp2FqMDlr3AC!?(KF0mM zQ$y;98*(W2;O~1(q=7_VP9?$}?}A8!$;q7Rx!|}ARrD1qGxzya@ULLghOg0$xi#tV zAN#098ccugi+SMHZlFkuoyy%wfv+Br7kz`v-1<^2XzkGyeM@NET3auJtp`m--x2$_ zzRUwBdZD62y?Gp^)UnX+*kD;>FYfm)(h^>1s->Qz~u)974tuvRMCk_B0kDA$N94`9&Eu zi}*=5--E}OU!pY;dwN5joSJeAePEvW*%05GTfbq9F-<&Z40-bv<(Gjh(wAd=A4yw& z1#BYq+C#olbNTO*0a8E0_fy>Ela9svuB3Wf=m&~_;t7y? zH~FSvy7+Kc7->$S-1_BTi+=4EgjjgWZA(+haBn!ouA$hh10_d>1YsVFVz*6~j2OdV zk%BS_WtAQu6GS?-lqtBW)MyV!_LwQtiGk9Sh#=bKrZ{BD>C(};a8#C3;Y4MZokzXA?@zoD3 z%~dnK(TDz;>ZEnB>h6%}5yGmT`)GX8tTFm1s;GVlWmi8K6Fr{MRzHH9tLN;|$Ft_@ z$BDt}M~G;C&Rsp9bX3pJMHdE{xd`oCgkdhmIv3-ZD1sA3lrdAt&J-&137Yc>I&%rb zxddP?1)WPJ%w%UXtOEzt4|=z2!dvQFw+ zCvTBGt8_i9YFV##t=F_*buO&FCEegkH??F~Tp8Au`AnND6KctVU0I2i?4&Cj1#DQ5 zA!2}ySZE^-z%3j)i2$Aq;iM=b|irOB$SV06`$VpUk&6;>8O zc%~9!Rl)A61gkpfu12*!hxR;&X?-5+c^=nFl@UBtVrvcAQ$uZif#!LE-ufcL^CHmt zC(!dJN$XCjXMU%=_0LMrpH;0dsXZ@gT4_2DP2XB;@YI@G|6=j{#oGF^&GRzU`U>oM zCDFPo>Dh(yzOeN4Uoma3V!f~8+UNu?o!C}K_SR9`UZZ(mqqn`z@V*YT{SEZ~P13eo zCiU)?xBXq|{ky8|4Yl_TO&de!W$4@L4c>ZFTZ6^hU~Ox(c^jd&CfM7QXlqV-n^Emd zw2z5tZ^8OnaP0uW2N2s?WFL##-b(Ye(%ah@zBZt}9rU$J+SyVcTi(u5`Z%g~uG+`d zw1YYysBh;Pd^}S--y-wzt?dGvPXM(GVV^M3E=u}DD0Te*cef0v2< zAB+D#R`y#q|636Izp($m3AQZhm!UXsqXTbaIPYL(fp>5mIUyh?auno%g35W97I>G= z*~(IgR;*h z+=J5KK{@veW$+6X_e*u~OAS}83##?pUPG|g#O<>L=liVOep|2~;ts&UfdqFj85~4` zU!mczFyPl%`0M}M2w@Eo)RJK>75s(0q5695O60nI`Z@3;d%M{K*FY1c5)p@XrbGa1uU@;{Ad~e!=j5#Uj7rczOb& zC-R2L$S{@n8x8r5&O5?DjsU!)AaYc~8<8R-a^5i|a!kcLu11b)cm^F}(DRH2#AxE3 zuplR_ypuNMB*Z%fBc~F)(Ihg8;-5x`PRlU-GuY4>9N$C;nTUKdIb^2t&(cC?>HKqy z&^dsA9t@qA@aHc`Ll@-yi^|YN75|btbVy3)P(mj<;=~AD z*oX@!ga{FcD0Gt}ZmQ5ji+JclFC*dwgg!9hlL-CNh+i%YC?f%tFsP0MH9}Yyf%P&W zVu&CnVaO5*S%qO+Bn$~7a3qotMw5{!N)$s!V;E5!8=a5iL|9a{7b zU38Zby$gu$fzf*s(X2E&D;M2YM(?Xc57f~I8c|XgP3lE+hUlD0^w1K0XcaxOMIS+; z$8hvvUGrJ6fdj>T3^bgZ(+R=GQ#L}E|QbgZ6> ztxgd?l^TC4P5g9v{As)xlNZO3#A%dxT8(&3ZG26gShlu4zLq6^h8KUPQ@pM#zD^;2 zwmbf8k9d7=e7#nT9g1Uz#p%X)x>>Bq7>j32h%@c+Ot&}-iD%7-v*+U3DUuDTi4AFz zjp>PvcnK~qfg?%qlmxy;vZ*$)sZO%FKCzi4*}_X~>6GMjC2|y!-0nnfk7R3aVyjkC zNEk{Gh9!B%M4nl)Z7i{ELbBbS*zT6>KoUD8iR{FKgu)R}^`&cZawbBh*H{s)X=PCv z6*U7Zu!5$zoH|HCUdzN@Ojb=$Tn+@57nW31&@@rfHZ!vJP}DN@iO4Xpbn^`T3QaDDClrQ!EDeq?FtqaUkI9x))Czo`3kyy%wf8l(^KtZz`By+q4`K-eVE$2V zfiWt2P<4H%vW|IRZ0^6(s`?f>W{zSqs=5#tZBs|Ef|iyk;GnDvl~B-BH?Y<iduHwk;+;oirQwXI%a+m>AsPl!&1tDq+%~1Q9;YpBRJMK z^rIfsMM_ymT17W7Dor2iDywRMNGycM7Z_N;+=Jd5S$i7Wdih7B!V?S49sT{Hv#eo& zrjeDSR|w!6_R%*iIVdK>D=ZC&&k2glw{;5+NzC&AQX-R!5s5|40df9O>3-oUVM+N= zCqJljkeaS}U|b&Xp#%|^6_Q*Q6rZo5Z{Y=e@Buz(o7iX>TLTG58(1Jbu_!d9Jp5y! zM`&tPN=Z~|sg+BBxr2{Sc$%Vyk)^Z0lXs{oVB-mO^p#T5)-bd}B;>&p3cN#8rImHy z@p<|duBLWg#x|a2_TE~?HilL(BWt(lPi6ns&(zk_3Knc==_aSD=O6jW(j_P`CM!5D zS5{Tez!HW?Dl)P4@Cr?~bcO%hKGI4$N?OKBS|-XmrdF;9AR%8yRnOSk!_X2I6w8{c zqHSvC=x^l~qNr)?7oHjr`RU!|gM0sP9RKhBe=_bp!rgnFLXXc4lz+^SPJd`g#=zvo z^feRDgRQFk*iI%>wKt)@GjXzvo3V))&vK;Z;z+pM^m0j6#6s7Ohax`re z%SsGGn#0O_mXflSSdONxl{lUrmeqKH35V4L;f=D@MDeq&)g&nb*0m3E6pm{jm6%!z zxTJZeZK5^6#`!7QGOuhu8G($8Kk0x2fZ}j9*yD|ir?Da>T&7VXw&{9c2d2-iP37yU zMp)M3Oo+xsaW>2@rZgiYt>s;2P}iT0bhu6oQ#u@S$((LH!@5}zZ1=0IARth=3~2*- zwe>lWYi7MT=PJOV?7vu1r_3ler_xVoCL9|}R2rh!G9#_Gx2&iL)LL0l80KgPR2375 zG3R_9Bw|g?WXr0k@0JPLEv_y3Ra#eIAXbi|Fleo)F|n&;N7g}tD(bVFuAB?Usoy#l zeB0>dY@W5cs_Hl{G2LxnpbKQK{k1LTnA4CM#GSe~_{R-%ns&8cfljV;Lyv|LS2V6J z^f^@!q_?;uzibd6eBCC4FFCoQ5m&!%kBUC*W+N~_Oi zTrhiQv+h4Q&*!`+UC-zJH>=MVg3ob#=ZhhPTo+3bFJTwUG0doom3W~PK>}uA$F*yx zW;Kj>w{{dPw%0t1y54VGgmvvvz%yPbr%)}t=Mq4tA8erWK~sG(-?>F_bE z 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 From e4c3f556c27fb3102369006ea8575870bc4701a1 Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 11 Jan 2011 13:56:59 -0800 Subject: [PATCH 10/41] bug 7942: we were not using the right values for record creation when the proto=icmp. Using the right vals status 7942: resolved fixed --- .../cloud/network/security/SecurityGroupManagerImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index 7d92f5c1f2c..f063958a460 100644 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -613,21 +613,21 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG return null; } } - IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocol, startPort, endPort, ngVO.getId()); + IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocol, startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, ngVO.getId()); if (ingressRule != null) { continue; //rule already exists. } - ingressRule = new IngressRuleVO(securityGroup.getId(), startPort, endPort, protocol, ngVO.getId(), ngVO.getName(), ngVO.getAccountName()); + ingressRule = new IngressRuleVO(securityGroup.getId(), startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, protocol, ngVO.getId(), ngVO.getName(), ngVO.getAccountName()); ingressRule = _ingressRuleDao.persist(ingressRule); newRules.add(ingressRule); } if(cidrList != null) { for (String cidr: cidrList) { - IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndCidr(securityGroup.getId(),protocol, startPort, endPort, cidr); + IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndCidr(securityGroup.getId(),protocol, startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, cidr); if (ingressRule != null) { continue; } - ingressRule = new IngressRuleVO(securityGroup.getId(), startPort, endPort, protocol, cidr); + ingressRule = new IngressRuleVO(securityGroup.getId(), startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, protocol, cidr); ingressRule = _ingressRuleDao.persist(ingressRule); newRules.add(ingressRule); } From 2e84e9fc92a1c45c7099c4d73c68132abded84cc Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 14:19:11 -0800 Subject: [PATCH 11/41] bug 7738: volume page - snapshot tab - create template from snapshot dialog - add isFeatured dropdown. --- ui/jsp/volume.jsp | 7 +++++++ ui/scripts/cloud.core.volume.js | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/ui/jsp/volume.jsp b/ui/jsp/volume.jsp index c94a9943e37..b50325a90ce 100644 --- a/ui/jsp/volume.jsp +++ b/ui/jsp/volume.jsp @@ -754,6 +754,13 @@ +
diff --git a/ui/scripts/cloud.core.volume.js b/ui/scripts/cloud.core.volume.js index ac2d76c7154..45624ed6ef1 100644 --- a/ui/scripts/cloud.core.volume.js +++ b/ui/scripts/cloud.core.volume.js @@ -57,6 +57,11 @@ function afterLoadVolumeJSP() { initDialog("dialog_create_template_from_snapshot", 450); initDialog("dialog_confirmation_delete_snapshot"); initDialog("dialog_download_volume"); + + if(isAdmin()) + $("#dialog_create_template_from_snapshot").find("#isfeatured_container").show(); + else + $("#dialog_create_template_from_snapshot").find("#isfeatured_container").hide(); $.ajax({ data: createURL("command=listOsTypes"), @@ -1046,6 +1051,11 @@ function doCreateTemplateFromSnapshotInVolumePage($actionLink, $subgridItem) { var password = thisDialog.find("#password").val(); array1.push("&passwordEnabled="+password); + if(thisDialog.find("#isfeatured_container").css("display")!="none") { + var isFeatured = thisDialog.find("#isfeatured").val(); + array1.push("&isfeatured="+isFeatured); + } + var id = jsonObj.id; var apiCommand = "command=createTemplate&snapshotid="+id+array1.join(""); doActionToSubgridItem(id, $actionLink, apiCommand, $subgridItem); From e28882b8ee88ccc154976d7d4c801774e1a7cded Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 11 Jan 2011 14:18:19 -0800 Subject: [PATCH 12/41] eliminating use of multiple variables; using the same var for all protocols (denoting ports for tcp/udp and type/code for icmp) --- .../cloud/network/security/SecurityGroupManagerImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index f063958a460..4270d31d860 100644 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -613,21 +613,21 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG return null; } } - IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocol, startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, ngVO.getId()); + IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocol, startPortOrType, endPortOrCode, ngVO.getId()); if (ingressRule != null) { continue; //rule already exists. } - ingressRule = new IngressRuleVO(securityGroup.getId(), startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, protocol, ngVO.getId(), ngVO.getName(), ngVO.getAccountName()); + ingressRule = new IngressRuleVO(securityGroup.getId(), startPortOrType, endPortOrCode, protocol, ngVO.getId(), ngVO.getName(), ngVO.getAccountName()); ingressRule = _ingressRuleDao.persist(ingressRule); newRules.add(ingressRule); } if(cidrList != null) { for (String cidr: cidrList) { - IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndCidr(securityGroup.getId(),protocol, startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, cidr); + IngressRuleVO ingressRule = _ingressRuleDao.findByProtoPortsAndCidr(securityGroup.getId(),protocol, startPortOrType, endPortOrCode, cidr); if (ingressRule != null) { continue; } - ingressRule = new IngressRuleVO(securityGroup.getId(), startPort != null ? startPort : startPortOrType, endPort != null ? endPort : endPortOrCode, protocol, cidr); + ingressRule = new IngressRuleVO(securityGroup.getId(), startPortOrType, endPortOrCode, protocol, cidr); ingressRule = _ingressRuleDao.persist(ingressRule); newRules.add(ingressRule); } From 5dfe399c1a2c6072814c273afd5bdd35ab23c94e Mon Sep 17 00:00:00 2001 From: alena Date: Tue, 11 Jan 2011 13:38:04 -0800 Subject: [PATCH 13/41] bug 7803: introduces new parameter "is_default" for the network. DeployVm requires 1 default network to be specified, other networks (if any) have to be secondary status 7803: resolved fixed Fix overview: 1) Parameter "isDefault" should be defined as a part of createNetwork * Virtual network is always default * Parameter can be specified only for DirectNetwork * Once parameter is set, there is no way to change it as we don't provide updateNetwork command. 2) Added isDefault parameter to listNetworks command so you can sort by that. 3) DeployVmCmd: * at least one default network should be set * if more than 1 default network is set - throw an error 4) Return isDefault information as a part of Nic object for the vm response in deploy/stop/start/listVm --- .../cloud/api/commands/CreateNetworkCmd.java | 7 ++++ .../cloud/api/commands/ListNetworksCmd.java | 7 ++++ .../cloud/api/response/NetworkResponse.java | 21 +++++++--- .../com/cloud/api/response/NicResponse.java | 17 ++++++-- api/src/com/cloud/network/Network.java | 2 + .../src/com/cloud/api/ApiResponseHelper.java | 7 ++-- .../ConfigurationManagerImpl.java | 2 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 4 +- .../src/com/cloud/network/NetworkManager.java | 4 +- .../com/cloud/network/NetworkManagerImpl.java | 40 +++++++++++++++---- server/src/com/cloud/network/NetworkVO.java | 18 +++++++-- .../VirtualNetworkApplianceManagerImpl.java | 11 ++--- .../cloud/server/ConfigurationServerImpl.java | 2 +- .../SecondaryStorageManagerImpl.java | 4 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 15 ++++++- setup/db/create-schema.sql | 3 +- 16 files changed, 120 insertions(+), 44 deletions(-) diff --git a/api/src/com/cloud/api/commands/CreateNetworkCmd.java b/api/src/com/cloud/api/commands/CreateNetworkCmd.java index c2ec0a7392b..c033afbc02f 100644 --- a/api/src/com/cloud/api/commands/CreateNetworkCmd.java +++ b/api/src/com/cloud/api/commands/CreateNetworkCmd.java @@ -73,6 +73,9 @@ public class CreateNetworkCmd extends BaseCmd { @Parameter(name=ApiConstants.IS_SHARED, type=CommandType.BOOLEAN, description="true is network offering supports vlans") private Boolean isShared; + + @Parameter(name=ApiConstants.IS_DEFAULT, type=CommandType.BOOLEAN, description="true if network is default, false otherwise") + private Boolean isDefault; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -124,6 +127,10 @@ public class CreateNetworkCmd extends BaseCmd { public boolean getIsShared() { return isShared == null ? false : isShared; } + + public Boolean isDefault() { + return isDefault; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/com/cloud/api/commands/ListNetworksCmd.java b/api/src/com/cloud/api/commands/ListNetworksCmd.java index 614fc56a1c6..17b062c06fc 100644 --- a/api/src/com/cloud/api/commands/ListNetworksCmd.java +++ b/api/src/com/cloud/api/commands/ListNetworksCmd.java @@ -60,6 +60,9 @@ public class ListNetworksCmd extends BaseListCmd { @Parameter(name=ApiConstants.IS_SHARED, type=CommandType.BOOLEAN, description="true if network is shared, false otherwise") private Boolean isShared; + + @Parameter(name=ApiConstants.IS_DEFAULT, type=CommandType.BOOLEAN, description="true if network is default, false otherwise") + private Boolean isDefault; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -92,6 +95,10 @@ public class ListNetworksCmd extends BaseListCmd { public Boolean getIsShared() { return isShared; } + + public Boolean isDefault() { + return isDefault; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/com/cloud/api/response/NetworkResponse.java b/api/src/com/cloud/api/response/NetworkResponse.java index 1ea5540ad84..a229d1bc06f 100644 --- a/api/src/com/cloud/api/response/NetworkResponse.java +++ b/api/src/com/cloud/api/response/NetworkResponse.java @@ -3,7 +3,6 @@ package com.cloud.api.response; import java.util.List; import com.cloud.api.ApiConstants; -import com.cloud.network.Networks; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @@ -54,14 +53,13 @@ public class NetworkResponse extends BaseResponse{ @SerializedName("isshared") @Param(description="true if network is shared, false otherwise") private Boolean isShared; - @SerializedName("issystem") @Param(description="true if network is system, false otherwise") + @SerializedName("issystem") @Param(description="true if network is system, false otherwise") private Boolean isSystem; - @SerializedName("state") @Param(description="state of the network") + @SerializedName("state") @Param(description="state of the network") private String state; - - //TODO - add description - @SerializedName("related") + + @SerializedName("related") @Param(description="related to what other network configuration") private Long related; @SerializedName("broadcasturi") @Param(description="broadcast uri of the network") @@ -88,6 +86,9 @@ public class NetworkResponse extends BaseResponse{ @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain associated with the network") private String domain; + @SerializedName("isdefault") @Param(description="true if network is default, false otherwise") + private Boolean isDefault; + @SerializedName("service") @Param(description="the list of services") private List services; @@ -306,4 +307,12 @@ public class NetworkResponse extends BaseResponse{ public void setServices(List services) { this.services = services; } + + public Boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(Boolean isDefault) { + this.isDefault = isDefault; + } } diff --git a/api/src/com/cloud/api/response/NicResponse.java b/api/src/com/cloud/api/response/NicResponse.java index d68fa797ddb..b69f0449d11 100644 --- a/api/src/com/cloud/api/response/NicResponse.java +++ b/api/src/com/cloud/api/response/NicResponse.java @@ -43,13 +43,14 @@ public class NicResponse extends BaseResponse { @SerializedName("broadcasturi") @Param(description="the broadcast uri of the nic") private String broadcastUri; - //TODO - add description - @SerializedName("traffictype") + @SerializedName("traffictype") @Param(description="the traffic type of the nic") private String trafficType; - //TODO - add description - @SerializedName("type") + @SerializedName("type") @Param(description="the type of the nic") private String type; + + @SerializedName("isdefault") @Param(description="true if nic is default, false otherwise") + private Boolean isDefault; public Long getId() { return id; @@ -122,4 +123,12 @@ public class NicResponse extends BaseResponse { public void setType(String type) { this.type = type; } + + public Boolean getIsDefault() { + return isDefault; + } + + public void setIsDefault(Boolean isDefault) { + this.isDefault = isDefault; + } } diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index a3355ff28fe..92301a31a6d 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -201,5 +201,7 @@ public interface Network extends ControlledEntity { boolean isShared(); String getReservationId(); + + boolean isDefault(); } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index da9c3d3d4bc..b441203afea 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -1083,13 +1083,11 @@ public class ApiResponseHelper implements ResponseGenerator { if (singleNic.getIsolationUri() != null) { nicResponse.setIsolationUri(singleNic.getIsolationUri().toString()); } - } - //Set traffic type + } Network network = ApiDBUtils.findNetworkById(singleNic.getNetworkId()); nicResponse.setTrafficType(network.getTrafficType().toString()); - - //Set type nicResponse.setType(network.getGuestType().toString()); + nicResponse.setIsDefault(singleNic.isDefaultNic()); nicResponse.setObjectName("nic"); @@ -2225,6 +2223,7 @@ public class ApiResponseHelper implements ResponseGenerator { } response.setIsShared(network.isShared()); + response.setIsDefault(network.isDefault()); response.setState(network.getState().toString()); response.setRelated(network.getRelated()); response.setDns1(network.getDns1()); diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index c98834cd1b6..8617edfdc31 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -1234,7 +1234,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } } userNetwork.setBroadcastDomainType(broadcastDomainType); - _networkMgr.setupNetwork(systemAccount, offering, userNetwork, plan, null, null, true); + _networkMgr.setupNetwork(systemAccount, offering, userNetwork, plan, null, null, true, false); } } } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 0ee83f41d04..ef2a5b62ba1 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -709,9 +709,9 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx NicProfile defaultNic = new NicProfile(); defaultNic.setDefaultNic(true); defaultNic.setDeviceId(2); - networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, defaultOffering.get(0), plan, null, null, false).get(0), defaultNic)); + networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, defaultOffering.get(0), plan, null, null, false, false).get(0), defaultNic)); for (NetworkOfferingVO offering : offerings) { - networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, offering, plan, null, null, false).get(0), null)); + networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, offering, plan, null, null, false, false).get(0), null)); } ConsoleProxyVO proxy = new ConsoleProxyVO(id, _serviceOffering.getId(), name, _template.getId(), _template.getGuestOSId(), dataCenterId, systemAcct.getDomainId(), systemAcct.getId(), 0); try { diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index a5019883e2c..37b1562a2be 100644 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -108,8 +108,8 @@ public interface NetworkManager extends NetworkService { */ List listPublicIpAddressesInVirtualNetwork(long accountId, long dcId, Boolean sourceNat); - List setupNetwork(Account owner, NetworkOfferingVO offering, DeploymentPlan plan, String name, String displayText, boolean isShared) throws ConcurrentOperationException; - List setupNetwork(Account owner, NetworkOfferingVO offering, Network predefined, DeploymentPlan plan, String name, String displayText, boolean isShared) throws ConcurrentOperationException; + List setupNetwork(Account owner, NetworkOfferingVO offering, DeploymentPlan plan, String name, String displayText, boolean isShared, boolean isDefault) throws ConcurrentOperationException; + List setupNetwork(Account owner, NetworkOfferingVO offering, Network predefined, DeploymentPlan plan, String name, String displayText, boolean isShared, boolean isDefault) throws ConcurrentOperationException; List getSystemAccountNetworkOfferings(String... offeringNames); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 54091d727ff..34844091da3 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -134,6 +134,7 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; @@ -783,12 +784,12 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } @Override - public List setupNetwork(Account owner, NetworkOfferingVO offering, DeploymentPlan plan, String name, String displayText, boolean isShared) throws ConcurrentOperationException { - return setupNetwork(owner, offering, null, plan, name, displayText, isShared); + public List setupNetwork(Account owner, NetworkOfferingVO offering, DeploymentPlan plan, String name, String displayText, boolean isShared, boolean isDefault) throws ConcurrentOperationException { + return setupNetwork(owner, offering, null, plan, name, displayText, isShared, isDefault); } @Override @DB - public List setupNetwork(Account owner, NetworkOfferingVO offering, Network predefined, DeploymentPlan plan, String name, String displayText, boolean isShared) throws ConcurrentOperationException { + public List setupNetwork(Account owner, NetworkOfferingVO offering, Network predefined, DeploymentPlan plan, String name, String displayText, boolean isShared, boolean isDefault) throws ConcurrentOperationException { Transaction.currentTxn(); Account locked = _accountDao.acquireInLockTable(owner.getId()); if (locked == null) { @@ -830,7 +831,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag related = id; } - NetworkVO vo = new NetworkVO(id, config, offering.getId(), plan.getDataCenterId(), guru.getName(), owner.getDomainId(), owner.getId(), related, name, displayText, isShared); + NetworkVO vo = new NetworkVO(id, config, offering.getId(), plan.getDataCenterId(), guru.getName(), owner.getDomainId(), owner.getId(), related, name, displayText, isShared, isDefault); configs.add(_networksDao.persist(vo, vo.getGuestType() != null)); } @@ -879,6 +880,11 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag requested.setMode(config.getMode()); } NicProfile profile = concierge.allocate(config, requested, vm); + + if (vm != null && vm.getVirtualMachine().getType() == Type.User && config.isDefault()) { + profile.setDefaultNic(true); + } + if (profile == null) { continue; } @@ -1055,7 +1061,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag _nicDao.update(nic.getId(), nic); URI broadcastUri = nic.getBroadcastUri(); if (broadcastUri == null) { - network.getBroadcastUri(); + broadcastUri = network.getBroadcastUri(); } URI isolationUri = nic.getIsolationUri(); @@ -1284,6 +1290,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag String endIP = cmd.getEndIp(); String netmask = cmd.getNetmask(); String cidr = null; + Boolean isDefault = cmd.isDefault(); if (gateway != null && netmask != null) { cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); } @@ -1305,6 +1312,19 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag throw new InvalidParameterValueException("Unable to find network offeirng by id " + networkOfferingId); } + //allow isDefault to be set only for Virtual network + if (networkOffering.getTrafficType() == TrafficType.Guest) { + if (isDefault != null) { + throw new InvalidParameterValueException("Can specify isDefault parameter only for Public network. "); + } else { + isDefault = true; + } + } else { + if (isDefault == null) { + isDefault = false; + } + } + //Check if zone exists if (zoneId == null || ((_dcDao.findById(zoneId)) == null)) { throw new InvalidParameterValueException("Please specify a valid zone."); @@ -1355,7 +1375,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } } - List networks = setupNetwork(owner, networkOffering, userNetwork, plan, name, displayText, isShared); + List networks = setupNetwork(owner, networkOffering, userNetwork, plan, name, displayText, isShared, isDefault); Long networkId = null; Network network = null; @@ -1426,6 +1446,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag String type = cmd.getType(); Boolean isSystem = cmd.getIsSystem(); Boolean isShared = cmd.getIsShared(); + Boolean isDefault = cmd.isDefault(); Long accountId = null; if (isSystem == null) { @@ -1510,6 +1531,10 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag sc.addAnd("isShared", SearchCriteria.Op.EQ, isShared); } + if (isDefault != null) { + sc.addAnd("isDefault", SearchCriteria.Op.EQ, isDefault); + } + List networks = _networksDao.search(sc, searchFilter); return networks; @@ -1708,8 +1733,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override public boolean restartNetwork(RestartNetworkCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException { //This method reapplies Ip addresses, LoadBalancer and PortForwarding rules - Account caller = UserContext.current().getCaller(); - + Account caller = UserContext.current().getCaller(); Long networkId = cmd.getNetworkId(); Network network = null; if (networkId != null) { diff --git a/server/src/com/cloud/network/NetworkVO.java b/server/src/com/cloud/network/NetworkVO.java index ecf08519a7a..b0662251865 100644 --- a/server/src/com/cloud/network/NetworkVO.java +++ b/server/src/com/cloud/network/NetworkVO.java @@ -131,6 +131,9 @@ public class NetworkVO implements Network { @Column(name="reservation_id") String reservationId; + @Column(name="is_default") + boolean isDefault; + public NetworkVO() { } @@ -158,8 +161,8 @@ public class NetworkVO implements Network { this.guestType = guestType; } - public NetworkVO(long id, Network that, long offeringId, long dataCenterId, String guruName, long domainId, long accountId, long related, String name, String displayText, Boolean isShared) { - this(id, that.getTrafficType(), that.getGuestType(), that.getMode(), that.getBroadcastDomainType(), offeringId, dataCenterId, domainId, accountId, related, name, displayText, isShared); + public NetworkVO(long id, Network that, long offeringId, long dataCenterId, String guruName, long domainId, long accountId, long related, String name, String displayText, Boolean isShared, boolean isDefault) { + this(id, that.getTrafficType(), that.getGuestType(), that.getMode(), that.getBroadcastDomainType(), offeringId, dataCenterId, domainId, accountId, related, name, displayText, isShared, isDefault); this.gateway = that.getGateway(); this.dns1 = that.getDns1(); this.dns2 = that.getDns2(); @@ -184,9 +187,10 @@ public class NetworkVO implements Network { * @param accountId * @param name * @param displayText - * @param isShared TODO + * @param isShared + * @param isDefault */ - public NetworkVO(long id, TrafficType trafficType, GuestIpType guestType, Mode mode, BroadcastDomainType broadcastDomainType, long networkOfferingId, long dataCenterId, long domainId, long accountId, long related, String name, String displayText, Boolean isShared) { + public NetworkVO(long id, TrafficType trafficType, GuestIpType guestType, Mode mode, BroadcastDomainType broadcastDomainType, long networkOfferingId, long dataCenterId, long domainId, long accountId, long related, String name, String displayText, Boolean isShared, boolean isDefault) { this(trafficType, guestType, mode, broadcastDomainType, networkOfferingId, dataCenterId, State.Allocated); this.domainId = domainId; this.accountId = accountId; @@ -195,6 +199,7 @@ public class NetworkVO implements Network { this.name = name; this.displayText = displayText; this.isShared = isShared; + this.isDefault = isDefault; } @Override @@ -373,6 +378,11 @@ public class NetworkVO implements Network { public boolean isShared() { return isShared; } + + @Override + public boolean isDefault() { + return isDefault; + } public void setShared(boolean isShared) { this.isShared = isShared; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 9566ed0b46b..6c02479f1ac 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -313,7 +313,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian private int _networkRate; private int _multicastRate; String _networkDomain; - boolean _noDefaultRouteForDirectNetwork; private VMTemplateVO _template; @@ -700,8 +699,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } _systemAcct = _accountService.getSystemAccount(); - - _noDefaultRouteForDirectNetwork = Boolean.parseBoolean(configs.get(Config.DirectNetworkNoDefaultRoute.key())); s_logger.info("DomainRouterManager is configured."); @@ -1038,11 +1035,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); NetworkOfferingVO controlOffering = offerings.get(0); - NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false).get(0); + NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); List> networks = new ArrayList>(3); NetworkOfferingVO publicOffering = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemPublicNetwork).get(0); - List publicConfigs = _networkMgr.setupNetwork(_systemAcct, publicOffering, plan, null, null, false); + List publicConfigs = _networkMgr.setupNetwork(_systemAcct, publicOffering, plan, null, null, false, false); NicProfile defaultNic = new NicProfile(); defaultNic.setDefaultNic(true); defaultNic.setIp4Address(sourceNatIp.getAddress().addr()); @@ -1128,7 +1125,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); NetworkOfferingVO controlOffering = offerings.get(0); - NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false).get(0); + NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); List> networks = new ArrayList>(3); NicProfile gatewayNic = new NicProfile(); @@ -1227,7 +1224,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian buf.append(" domain=" + router.getDomain()); } - if (_noDefaultRouteForDirectNetwork && network.getGuestType() == GuestIpType.Direct) { + if (!network.isDefault() && network.getGuestType() == GuestIpType.Direct) { buf.append(" defaultroute=false"); } else { buf.append(" defaultroute=true"); diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 90efd4d745b..a4fe04634b7 100644 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -772,7 +772,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { } if (broadcastDomainType != null) { - NetworkVO network = new NetworkVO(id, trafficType, null, mode, broadcastDomainType, networkOfferingId, zoneId, domainId, accountId, related, null, null, true); + NetworkVO network = new NetworkVO(id, trafficType, null, mode, broadcastDomainType, networkOfferingId, zoneId, domainId, accountId, related, null, null, true, false); network.setGuruName(guruNames.get(network.getTrafficType())); network.setDns1(zone.getDns1()); network.setDns2(zone.getDns2()); diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index 65ed6246a4d..c2272b80601 100644 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -455,9 +455,9 @@ public class SecondaryStorageManagerImpl implements SecondaryStorageVmManager, V defaultNic.setDefaultNic(true); defaultNic.setDeviceId(2); try { - networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, defaultOffering.get(0), plan, null, null, false).get(0), defaultNic)); + networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, defaultOffering.get(0), plan, null, null, false, false).get(0), defaultNic)); for (NetworkOfferingVO offering : offerings) { - networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, offering, plan, null, null, false).get(0), null)); + networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, offering, plan, null, null, false, false).get(0), null)); } } catch (ConcurrentOperationException e) { s_logger.info("Unable to setup due to concurrent operation. " + e); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 913f7b0dbe5..c49fbee38c1 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -134,7 +134,6 @@ import com.cloud.network.ovs.OvsNetworkManager; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.RulesManager; import com.cloud.network.security.SecurityGroupManager; -import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.server.Criteria; @@ -2234,7 +2233,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager if (dc.getNetworkType() == NetworkType.Basic && networkList == null) { Network defaultNetwork = _networkMgr.getSystemNetworkByZoneAndTrafficType(dc.getId(), TrafficType.Guest); if (defaultNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default Direct network to start a vm"); + throw new InvalidParameterValueException("Unable to find a default network to start a vm"); } else { networkList = new ArrayList(); networkList.add(defaultNetwork.getId()); @@ -2246,6 +2245,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } List> networks = new ArrayList>(); + short defaultNetworkNumber = 0; for (Long networkId : networkList) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { @@ -2258,10 +2258,21 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new PermissionDeniedException("Unable to create a vm using network with id " + networkId + ", permission denied"); } } + + if (network.isDefault()) { + defaultNetworkNumber++; + } networks.add(new Pair(network, null)); } } + //at least one network default network has to be set + if (defaultNetworkNumber == 0) { + throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); + } else if (defaultNetworkNumber >1) { + throw new InvalidParameterValueException("Only 1 default network per vm is supported"); + } + long id = _vmDao.getNextInSequence(Long.class, "id"); String hostName = cmd.getName(); diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index afa3db648ff..0db72fcf6d5 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -151,6 +151,7 @@ CREATE TABLE `cloud`.`networks` ( `shared` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '0 if network is shared, 1 if network dedicated', `network_domain` varchar(255) COMMENT 'domain', `reservation_id` char(40) COMMENT 'reservation id', + `is_default` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if network is default', `created` datetime NOT NULL COMMENT 'date created', `removed` datetime COMMENT 'date removed if not null', PRIMARY KEY (`id`) @@ -214,7 +215,7 @@ CREATE TABLE `cloud`.`network_offerings` ( `service_offering_id` bigint unsigned UNIQUE COMMENT 'service offering id that this network offering is tied to', `created` datetime NOT NULL COMMENT 'time the entry was created', `removed` datetime DEFAULT NULL COMMENT 'time the entry was removed', - `default` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if network is default', + `default` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if network offering is default', `availability` varchar(255) NOT NULL COMMENT 'availability of the network', `dns_service` int(1) unsigned NOT NULL DEFAULT 0 COMMENT 'true if network offering provides dns service', `gateway_service` int(1) unsigned NOT NULL DEFAULT 0 COMMENT 'true if network offering provides gateway service', From 45805db737d91b50390337edca62f98739226d56 Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 11 Jan 2011 14:24:33 -0800 Subject: [PATCH 14/41] adding descriptions to lun commands, to be consumed by the xsl transformer --- api/src/com/cloud/api/commands/ListPreallocatedLunsCmd.java | 2 +- api/src/com/cloud/api/commands/RegisterPreallocatedLunCmd.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api/src/com/cloud/api/commands/ListPreallocatedLunsCmd.java b/api/src/com/cloud/api/commands/ListPreallocatedLunsCmd.java index 645f0a6ca5b..981c155c3ed 100644 --- a/api/src/com/cloud/api/commands/ListPreallocatedLunsCmd.java +++ b/api/src/com/cloud/api/commands/ListPreallocatedLunsCmd.java @@ -30,7 +30,7 @@ import com.cloud.api.Parameter; import com.cloud.api.response.ListResponse; import com.cloud.api.response.PreallocatedLunResponse; -@Implementation(responseObject=PreallocatedLunResponse.class) +@Implementation(description="List PreallocatedLuns",responseObject=PreallocatedLunResponse.class) public class ListPreallocatedLunsCmd extends BaseListCmd { public static final Logger s_logger = Logger.getLogger(ListPreallocatedLunsCmd.class.getName()); diff --git a/api/src/com/cloud/api/commands/RegisterPreallocatedLunCmd.java b/api/src/com/cloud/api/commands/RegisterPreallocatedLunCmd.java index d3aca457b2c..1fb47e40d5b 100644 --- a/api/src/com/cloud/api/commands/RegisterPreallocatedLunCmd.java +++ b/api/src/com/cloud/api/commands/RegisterPreallocatedLunCmd.java @@ -24,8 +24,7 @@ import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.PreallocatedLunResponse; -//TODO - add description to @Implementation -@Implementation(responseObject=PreallocatedLunResponse.class) +@Implementation(description="Registers PreallocatedLun", responseObject=PreallocatedLunResponse.class) public class RegisterPreallocatedLunCmd extends BaseCmd { private static final String s_name = "registerPreallocatedLunsResponse"; From 5490c4ee025077a10a8651cfc273da344a1092f7 Mon Sep 17 00:00:00 2001 From: will Date: Tue, 11 Jan 2011 14:34:09 -0800 Subject: [PATCH 15/41] Added ability to allow users to select language support. --- .../classes/resources/messages_zh.properties | 2 ++ ui/index.jsp | 10 ++++--- ui/scripts/cloud.core.init.js | 27 ++++++++----------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/client/WEB-INF/classes/resources/messages_zh.properties b/client/WEB-INF/classes/resources/messages_zh.properties index 1cb4c4780fa..dd2c0a69705 100644 --- a/client/WEB-INF/classes/resources/messages_zh.properties +++ b/client/WEB-INF/classes/resources/messages_zh.properties @@ -1,6 +1,8 @@ #Titles label.cloud.console=Cloud Management Console[zh] +label.menu.dashboard=儀器板 + #Labels #Messages \ No newline at end of file diff --git a/ui/index.jsp b/ui/index.jsp index 7dd72e2b76a..e56560a4f31 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -1,4 +1,8 @@ -<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> + + + <% long now = System.currentTimeMillis(); %> @@ -114,8 +118,8 @@
diff --git a/ui/scripts/cloud.core.init.js b/ui/scripts/cloud.core.init.js index 1ebf664cf3c..bafc4f299e9 100644 --- a/ui/scripts/cloud.core.init.js +++ b/ui/scripts/cloud.core.init.js @@ -47,7 +47,7 @@ $(document).ready(function() { type: "text/css", href: $.cookie("theme") }); - $("#theme_button p").text($.cookie("theme_name")); + $("#theme_button p").text($.cookie("theme.name")); } $("#theme_button").click(function(event) { var $menu = $(this).find("#theme_menu"); @@ -73,14 +73,14 @@ $(document).ready(function() { href: id }); name = target.text(); - $.cookie("theme_name", name) + $.cookie("theme.name", name) $.cookie("theme", id); } else { if ($currentTheme != null) { $currentTheme.remove(); } $.cookie("theme", null); - $.cookie("theme_name", null); + $.cookie("theme.name", null); name = "Default Theme"; } $("#theme_button p").text(name); @@ -89,6 +89,10 @@ $(document).ready(function() { }); // Setup Language option + if ($.cookie("lang") != null) { + $("#lang_button p").text($.cookie("lang.name")); + } + $("#lang_button").click(function(event) { var $menu = $(this).find("#lang_menu"); if ($menu.css("display") == "none") { @@ -101,18 +105,9 @@ $(document).ready(function() { $("#lang_button #lang_menu").click(function(event) { var target = $(event.target); var id = target.attr("id"); - var name = "English"; - switch (id) { - case "lang_chinese" : - // Change Language here - name = "Chinese"; - break; - default: - name = "English"; - break; - } - $("#lang_button p").text(name); - $(this).hide(); + $.cookie("lang", id); + $.cookie("lang.name", target.text()); + location.replace('/client'); return false; }); @@ -594,7 +589,7 @@ $(document).ready(function() { var activeTab = null; function logout(refresh) { g_mySession = null; - g_sessionKey = null; + g_sessionKey = null; g_username = null; g_account = null; g_domainid = null; From 6090582d0a6fa61091cedff6b1a14ef78b6e8c7f Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 14:44:08 -0800 Subject: [PATCH 16/41] bug 7861: zonetree - add "Secondary Storage" node under zone node. --- ui/index.jsp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/index.jsp b/ui/index.jsp index e56560a4f31..fa789c868de 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -837,14 +837,22 @@ From 75ab3a554cefe945b7d7e7553cb876a7a4e06c7a Mon Sep 17 00:00:00 2001 From: will Date: Tue, 11 Jan 2011 14:55:49 -0800 Subject: [PATCH 17/41] Added a missing semi-colon --- ui/scripts/cloud.core.init.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scripts/cloud.core.init.js b/ui/scripts/cloud.core.init.js index bafc4f299e9..17f4abefec8 100644 --- a/ui/scripts/cloud.core.init.js +++ b/ui/scripts/cloud.core.init.js @@ -73,7 +73,7 @@ $(document).ready(function() { href: id }); name = target.text(); - $.cookie("theme.name", name) + $.cookie("theme.name", name); $.cookie("theme", id); } else { if ($currentTheme != null) { From 1fd84fbcfa9b4d517053eb09a171f867326b9139 Mon Sep 17 00:00:00 2001 From: anthony Date: Tue, 11 Jan 2011 15:18:35 -0800 Subject: [PATCH 18/41] xen hyperviosr used 128 M memory --- .../com/cloud/hypervisor/xen/resource/CitrixResourceBase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 5b15cfd2234..2461b668bbd 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -4035,7 +4035,8 @@ public abstract class CitrixResourceBase implements ServerResource { } } // assume the memory Virtualization overhead is 1/64 - ram = (ram - dom0Ram) * 63/64; + // xen hypervisor used 128 M + ram = (ram - dom0Ram - (128 * 1024 * 1024)) * 63/64; cmd.setMemory(ram); cmd.setDom0MinMemory(dom0Ram); From 6ebbff60f44558a7f5d85159a8214434db451e5b Mon Sep 17 00:00:00 2001 From: alena Date: Tue, 11 Jan 2011 15:01:03 -0800 Subject: [PATCH 19/41] Fixed Api xml doc generator --- api/src/com/cloud/api/Implementation.java | 3 - .../cloud/api/response/NetworkResponse.java | 2 +- .../api/response/SecurityGroupResponse.java | 2 +- .../cloud/api/response/ServiceResponse.java | 2 +- .../cloud/api/response/UserVmResponse.java | 2 +- api/src/com/cloud/serializer/Param.java | 9 +- .../com/cloud/api/doc/ApiXmlDocWriter.java | 97 ++++++++++++------- server/src/com/cloud/api/doc/Argument.java | 12 ++- 8 files changed, 81 insertions(+), 48 deletions(-) diff --git a/api/src/com/cloud/api/Implementation.java b/api/src/com/cloud/api/Implementation.java index ff28bc2cfe0..8a6aa4abb19 100644 --- a/api/src/com/cloud/api/Implementation.java +++ b/api/src/com/cloud/api/Implementation.java @@ -9,9 +9,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({TYPE}) public @interface Implementation { -// String createMethod() default ""; -// String method() default ""; -// Class manager() default ManagementServer.class; Class responseObject(); String description() default ""; } diff --git a/api/src/com/cloud/api/response/NetworkResponse.java b/api/src/com/cloud/api/response/NetworkResponse.java index a229d1bc06f..aed50cae27a 100644 --- a/api/src/com/cloud/api/response/NetworkResponse.java +++ b/api/src/com/cloud/api/response/NetworkResponse.java @@ -89,7 +89,7 @@ public class NetworkResponse extends BaseResponse{ @SerializedName("isdefault") @Param(description="true if network is default, false otherwise") private Boolean isDefault; - @SerializedName("service") @Param(description="the list of services") + @SerializedName("service") @Param(description="the list of services", responseObject = ServiceResponse.class) private List services; public Long getId() { diff --git a/api/src/com/cloud/api/response/SecurityGroupResponse.java b/api/src/com/cloud/api/response/SecurityGroupResponse.java index aeddef21b93..705ac8addec 100644 --- a/api/src/com/cloud/api/response/SecurityGroupResponse.java +++ b/api/src/com/cloud/api/response/SecurityGroupResponse.java @@ -41,7 +41,7 @@ public class SecurityGroupResponse extends BaseResponse { @SerializedName("domain") @Param(description="the domain name of the security group") private String domainName; - @SerializedName("ingressrule") @Param(description="the list of ingress rules associated with the security group") + @SerializedName("ingressrule") @Param(description="the list of ingress rules associated with the security group", responseObject = IngressRuleResponse.class) private List ingressRules; public Long getId() { diff --git a/api/src/com/cloud/api/response/ServiceResponse.java b/api/src/com/cloud/api/response/ServiceResponse.java index d28c9f496ec..2f75de3529b 100644 --- a/api/src/com/cloud/api/response/ServiceResponse.java +++ b/api/src/com/cloud/api/response/ServiceResponse.java @@ -28,7 +28,7 @@ public class ServiceResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) @Param(description="the service name") private String name; - @SerializedName("capability") @Param(description="the list of capabilities") + @SerializedName("capability") @Param(description="the list of capabilities", responseObject = CapabilityResponse.class) private List capabilities; public String getName() { diff --git a/api/src/com/cloud/api/response/UserVmResponse.java b/api/src/com/cloud/api/response/UserVmResponse.java index 40625421b9c..113c49f2ec8 100644 --- a/api/src/com/cloud/api/response/UserVmResponse.java +++ b/api/src/com/cloud/api/response/UserVmResponse.java @@ -142,7 +142,7 @@ public class UserVmResponse extends BaseResponse { @SerializedName("jobstatus") @Param(description="shows the current pending asynchronous job status") private Integer jobStatus; - @SerializedName("nic") @Param(description="the list of nics associated with vm") + @SerializedName("nic") @Param(description="the list of nics associated with vm", responseObject = NicResponse.class) private List nics; public Long getObjectId() { diff --git a/api/src/com/cloud/serializer/Param.java b/api/src/com/cloud/serializer/Param.java index cf119a2de1b..ebc0fb62e9f 100644 --- a/api/src/com/cloud/serializer/Param.java +++ b/api/src/com/cloud/serializer/Param.java @@ -18,13 +18,16 @@ package com.cloud.serializer; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Param { String name() default ""; String propName() default ""; String description() default ""; - boolean expose() default true; + + // 2 parameters below are used by cloudstack api + boolean expose() default true; + Class responseObject() default Object.class; } diff --git a/server/src/com/cloud/api/doc/ApiXmlDocWriter.java b/server/src/com/cloud/api/doc/ApiXmlDocWriter.java index 70e602bcdb8..95ed8945228 100644 --- a/server/src/com/cloud/api/doc/ApiXmlDocWriter.java +++ b/server/src/com/cloud/api/doc/ApiXmlDocWriter.java @@ -47,6 +47,7 @@ import com.cloud.api.BaseCmd; import com.cloud.api.BaseListCmd; import com.cloud.api.Implementation; import com.cloud.api.Parameter; +import com.cloud.api.response.BaseResponse; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import com.thoughtworks.xstream.XStream; @@ -230,7 +231,7 @@ public class ApiXmlDocWriter { else System.out.println("Command " + apiCommand.getName() + " misses description"); - //Get request parameters + //Set request parameters Field[] fields = clas.getDeclaredFields(); //Get fields from superclass @@ -246,46 +247,13 @@ public class ApiXmlDocWriter { } superClass = superClass.getSuperclass(); } - - for (Field f : fields) { - Parameter parameterAnnotation = f.getAnnotation(Parameter.class); - if (parameterAnnotation != null && parameterAnnotation.expose()) { - Argument reqArg = new Argument(parameterAnnotation.name()); - reqArg.setRequired(parameterAnnotation.required()); - if (!parameterAnnotation.description().isEmpty()) - reqArg.setDescription(parameterAnnotation.description()); - else { - //System.out.println("Description is missing for the request parameter " + parameterAnnotation.name() + " of the command " + apiCommand.getName()); - } - request.add(reqArg); - } - } + request = setRequestFields(fields); - Class responseClas = impl.responseObject(); //Get response parameters + Class responseClas = impl.responseObject(); Field[] responseFields = responseClas.getDeclaredFields(); - for (Field responseField : responseFields) { - SerializedName nameAnnotation = responseField.getAnnotation(SerializedName.class); - Param descAnnotation = responseField.getAnnotation(Param.class); - Argument respArg = new Argument(nameAnnotation.value()); - boolean toExpose = true; - if (descAnnotation != null) { - String description = descAnnotation.description(); - toExpose = descAnnotation.expose(); - if (description != null && !description.isEmpty()) { - respArg.setDescription(description); - } else if (toExpose){ - //System.out.println("Description is missing for the response parameter " + nameAnnotation.value().toString() + " of the command " + apiCommand.getName()); - } - } else { - //System.out.println("Description is missing for the response parameter " + nameAnnotation.value().toString() + " of the command " + apiCommand.getName()); - } - - if (toExpose) { - response.add(respArg); - } - } + response = setResponseFields(responseFields); apiCommand.setRequest(request); apiCommand.setResponse(response); @@ -293,6 +261,61 @@ public class ApiXmlDocWriter { out.writeObject(apiCommand); } + + private static ArrayList setRequestFields(Field[] fields) { + ArrayList arguments = new ArrayList(); + for (Field f : fields) { + Parameter parameterAnnotation = f.getAnnotation(Parameter.class); + if (parameterAnnotation != null && parameterAnnotation.expose()) { + Argument reqArg = new Argument(parameterAnnotation.name()); + reqArg.setRequired(parameterAnnotation.required()); + if (!parameterAnnotation.description().isEmpty()) { + reqArg.setDescription(parameterAnnotation.description()); + } + arguments.add(reqArg); + } + } + return arguments; + } + + private static ArrayList setResponseFields(Field[] responseFields) { + ArrayList arguments = new ArrayList(); + for (Field responseField : responseFields) { + SerializedName nameAnnotation = responseField.getAnnotation(SerializedName.class); + Param paramAnnotation = responseField.getAnnotation(Param.class); + Argument respArg = new Argument(nameAnnotation.value()); + boolean toExpose = true; + if (paramAnnotation != null) { + String description = paramAnnotation.description(); + Class fieldClass = paramAnnotation.responseObject(); + toExpose = paramAnnotation.expose(); + if (description != null && !description.isEmpty()) { + respArg.setDescription(description); + } + + if (fieldClass != null) { + Class superClass = fieldClass.getSuperclass(); + if (superClass != null) { + String superName = superClass.getName(); + if (superName.equals(BaseResponse.class.getName())) { + ArrayList fieldArguments = new ArrayList(); + Field[] fields = fieldClass.getDeclaredFields(); + fieldArguments = setResponseFields(fields); + respArg.setArguments(fieldArguments); + } + } + } + } + + if (toExpose) { + arguments.add(respArg); + } + + + } + return arguments; + } + private static void zipDir(String zipFileName, String dir) throws Exception { File dirObj = new File(dir); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName)); diff --git a/server/src/com/cloud/api/doc/Argument.java b/server/src/com/cloud/api/doc/Argument.java index c3927842c2b..2846bb550b4 100644 --- a/server/src/com/cloud/api/doc/Argument.java +++ b/server/src/com/cloud/api/doc/Argument.java @@ -18,10 +18,13 @@ package com.cloud.api.doc; +import java.util.List; + public class Argument{ private String name; private String description; private Boolean required; + private List arguments; public Argument(String name) { this.name = name; @@ -45,5 +48,12 @@ public class Argument{ public void setRequired(Boolean required) { this.required = required; } - + + public List getArguments() { + return arguments; + } + + public void setArguments(List arguments) { + this.arguments = arguments; + } } From c0b520c59682faece1104550978c8319c22b8d55 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 16:03:55 -0800 Subject: [PATCH 20/41] bug 7861: add JavaScript file, JSP file for secondary storage page. --- ui/jsp/secondarystorage.jsp | 93 ++++++++++++++++++ ui/scripts/cloud.core.resource.js | 35 ++++++- ui/scripts/cloud.core.secondarystorage.js | 109 ++++++++++++++++++++++ 3 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 ui/jsp/secondarystorage.jsp create mode 100644 ui/scripts/cloud.core.secondarystorage.js diff --git a/ui/jsp/secondarystorage.jsp b/ui/jsp/secondarystorage.jsp new file mode 100644 index 00000000000..63071e8ac5e --- /dev/null +++ b/ui/jsp/secondarystorage.jsp @@ -0,0 +1,93 @@ +<%@ page import="java.util.*" %> +<%@ page import="com.cloud.utils.*" %> + +<% + Locale browserLocale = request.getLocale(); + CloudResourceBundle t = CloudResourceBundle.getBundle("resources/resource", browserLocale); +%> + +
+ +
+
+ +

Secondary Storage +

+
+
+ +
+
+ <%=t.t("Details")%>
+
+
+ +
+
+
+
+ (title)
+ +
+
+
+

+ Waiting …

+
+
+
+
+
+ <%=t.t("ID")%>:
+
+
+
+
+
+
+
+
+
+ <%=t.t("name")%>:
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ Add Secondary Storage
+ +
+
+
+ diff --git a/ui/scripts/cloud.core.resource.js b/ui/scripts/cloud.core.resource.js index bb0e58c7604..f07d01a8617 100644 --- a/ui/scripts/cloud.core.resource.js +++ b/ui/scripts/cloud.core.resource.js @@ -114,6 +114,38 @@ function buildZoneTree() { return false; }); + $("#secondarystorage_header").unbind("click").bind("click", function(event) { + selectRowInZoneTree($(this)); + + clearMiddleMenu(); + hideMiddleMenu(); + + if(currentRightPanelJSP != "jsp/secondarystorage.jsp") { + removeDialogs(); + + var $thisNode = $(this); + $("#right_panel").load("jsp/secondarystorage.jsp", function(){ + currentRightPanelJSP = "jsp/secondarystorage.jsp"; + + /* + $(this).data("onRefreshFn", function() { + var zoneObj = $midmenuItem1.data("jsonObj"); + if(zoneObj == null) + return; + $("#zone_"+zoneObj.id).find("#secondarystorage_header").click(); + }); + */ + + afterLoadSecondaryStorageJSP($thisNode); + }); + } + else { + //secondarystoragePopulateMiddleMenu($(this)); + } + + return false; + }); + $("#network_header").unbind("click").bind("click", function(event) { selectRowInZoneTree($(this)); @@ -308,8 +340,9 @@ function zoneJSONToTreeNode(jsonObj, $zoneNode) { var zoneid = jsonObj.id; $zoneNode.attr("id", "zone_" + zoneid); $zoneNode.data("jsonObj", jsonObj); + $zoneNode.find("#secondarystorage_header").data("zoneObj", jsonObj); - if(jsonObj.networktype == "Advanced") { + if(jsonObj.networktype == "Advanced") { $zoneNode.find("#network_header").show().data("jsonObj", jsonObj); } diff --git a/ui/scripts/cloud.core.secondarystorage.js b/ui/scripts/cloud.core.secondarystorage.js new file mode 100644 index 00000000000..7d6e8733159 --- /dev/null +++ b/ui/scripts/cloud.core.secondarystorage.js @@ -0,0 +1,109 @@ + /** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +function afterLoadSecondaryStorageJSP($midmenuItem1) { + var $topButtonContainer = clearButtonsOnTop(); + $("#top_buttons").appendTo($topButtonContainer); + + //initDialog("dialog_add_external_cluster_in_secondaryStorage_page", 320); + + secondaryStorageRefreshDataBinding(); +} + +function secondaryStorageRefreshDataBinding() { + var $secondaryStorageNode = $selectedSubMenu; + secondaryStorageJsonToRightPanel($secondaryStorageNode); +} + +function secondaryStorageJsonToRightPanel($midmenuItem1) { + $("#right_panel_content").data("$midmenuItem1", $midmenuItem1); + + /* + bindEventHandlerToDialogAddVlanForZone(); + bindAddPodButton($("#add_pod_button"), $midmenuItem1); + bindAddSecondaryStorageButton($("#add_secondarystorage_button"), $midmenuItem1); + */ + + secondaryStorageJsonToDetailsTab(); +} + +function secondaryStorageJsonToDetailsTab() { + var $midmenuItem1 = $("#right_panel_content").data("$midmenuItem1"); + if($midmenuItem1 == null) + return; + + var zoneObj = $midmenuItem1.data("zoneObj"); + if(zoneObj == null) + return; + + var $thisTab = $("#right_panel_content").find("#tab_content_details"); + $thisTab.find("#tab_container").hide(); + $thisTab.find("#tab_spinning_wheel").show(); + + var jsonObj; + $.ajax({ + data: createURL("command=listHosts&type=SecondaryStorage&zoneid="+zoneObj.id), + dataType: "json", + async: false, + success: function(json) { + var items = json.listhostsresponse.host; + if(items != null && items.length > 0) { + jsonObj = items[0]; + $midmenuItem1.data("jsonObj", jsonObj); + } + } + }); + + $thisTab.find("#id").text(fromdb(jsonObj.id)); + $thisTab.find("#grid_header_title").text(fromdb(jsonObj.name)); + $thisTab.find("#name").text(fromdb(jsonObj.name)); + + //actions *** + var $actionLink = $thisTab.find("#action_link"); + $actionLink.bind("mouseover", function(event) { + $(this).find("#action_menu").show(); + return false; + }); + $actionLink.bind("mouseout", function(event) { + $(this).find("#action_menu").hide(); + return false; + }); + var $actionMenu = $thisTab.find("#action_link #action_menu"); + $actionMenu.find("#action_list").empty(); + //buildActionLinkForTab("Delete Secondary Storage", secondarystorageActionMap, $actionMenu, $midmenuItem1, $thisTab); + + $thisTab.find("#tab_spinning_wheel").hide(); + $thisTab.find("#tab_container").show(); +} + +function secondaryStorageJsonClearRightPanel() { + secondaryStorageJsonClearDetailsTab(); +} + +function secondaryStorageJsonClearDetailsTab() { + var $thisTab = $("#right_panel_content").find("#tab_content_details"); + $thisTab.find("#grid_header_title").text(""); + $thisTab.find("#id").text(""); + + $thisTab.find("#name").text(""); + + //actions *** + var $actionMenu = $thisTab.find("#action_link #action_menu"); + $actionMenu.find("#action_list").empty(); + $actionMenu.find("#action_list").append($("#no_available_actions").clone().show()); +} \ No newline at end of file From cdc1b6db233652f400bf557479449ead19014327 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 16:06:15 -0800 Subject: [PATCH 21/41] bug 7861: include secondarystorage.js in index.jsp --- ui/index.jsp | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/index.jsp b/ui/index.jsp index fa789c868de..762145fadb3 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -50,6 +50,7 @@ + From 620e7e0305d5753413ba2775ded811fdce632488 Mon Sep 17 00:00:00 2001 From: alena Date: Tue, 11 Jan 2011 16:06:27 -0800 Subject: [PATCH 22/41] Implemented list by trafficType in listNetworks command --- api/src/com/cloud/api/commands/ListNetworksCmd.java | 7 +++++++ .../com/cloud/configuration/ConfigurationManagerImpl.java | 4 +++- server/src/com/cloud/network/NetworkManagerImpl.java | 5 +++++ server/src/com/cloud/server/ConfigurationServerImpl.java | 6 ++++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/api/src/com/cloud/api/commands/ListNetworksCmd.java b/api/src/com/cloud/api/commands/ListNetworksCmd.java index 17b062c06fc..b9400f9b444 100644 --- a/api/src/com/cloud/api/commands/ListNetworksCmd.java +++ b/api/src/com/cloud/api/commands/ListNetworksCmd.java @@ -63,6 +63,9 @@ public class ListNetworksCmd extends BaseListCmd { @Parameter(name=ApiConstants.IS_DEFAULT, type=CommandType.BOOLEAN, description="true if network is default, false otherwise") private Boolean isDefault; + + @Parameter(name=ApiConstants.TRAFFIC_TYPE, type=CommandType.STRING, description="type of the traffic") + private String trafficType; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -100,6 +103,10 @@ public class ListNetworksCmd extends BaseListCmd { return isDefault; } + public String getTrafficType() { + return trafficType; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 8617edfdc31..b666a28da25 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -1216,6 +1216,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura Account systemAccount = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM); BroadcastDomainType broadcastDomainType = null; + boolean isNetworkDefault = false; if (offering.getTrafficType() == TrafficType.Management) { broadcastDomainType = BroadcastDomainType.Native; } else if (offering.getTrafficType() == TrafficType.Control) { @@ -1228,13 +1229,14 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } } else if (offering.getTrafficType() == TrafficType.Guest) { if (zone.getNetworkType() == NetworkType.Basic) { + isNetworkDefault = true; broadcastDomainType = BroadcastDomainType.Native; } else { continue; } } userNetwork.setBroadcastDomainType(broadcastDomainType); - _networkMgr.setupNetwork(systemAccount, offering, userNetwork, plan, null, null, true, false); + _networkMgr.setupNetwork(systemAccount, offering, userNetwork, plan, null, null, true, isNetworkDefault); } } } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 34844091da3..4100d407fcd 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1444,6 +1444,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); String type = cmd.getType(); + String trafficType = cmd.getTrafficType(); Boolean isSystem = cmd.getIsSystem(); Boolean isShared = cmd.getIsShared(); Boolean isDefault = cmd.isDefault(); @@ -1535,6 +1536,10 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag sc.addAnd("isDefault", SearchCriteria.Op.EQ, isDefault); } + if (trafficType != null) { + sc.addAnd("trafficType", SearchCriteria.Op.EQ, trafficType); + } + List networks = _networksDao.search(sc, searchFilter); return networks; diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index a4fe04634b7..ea8c55a716d 100644 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -752,7 +752,8 @@ public class ConfigurationServerImpl implements ConfigurationServer { BroadcastDomainType broadcastDomainType = null; TrafficType trafficType= offering.getTrafficType(); - + + boolean isNetworkDefault = false; if (trafficType == TrafficType.Management || trafficType == TrafficType.Storage) { broadcastDomainType = BroadcastDomainType.Native; } else if (trafficType == TrafficType.Control) { @@ -765,6 +766,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { } } else if (offering.getTrafficType() == TrafficType.Guest) { if (zone.getNetworkType() == NetworkType.Basic) { + isNetworkDefault = true; broadcastDomainType = BroadcastDomainType.Native; } else { continue; @@ -772,7 +774,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { } if (broadcastDomainType != null) { - NetworkVO network = new NetworkVO(id, trafficType, null, mode, broadcastDomainType, networkOfferingId, zoneId, domainId, accountId, related, null, null, true, false); + NetworkVO network = new NetworkVO(id, trafficType, null, mode, broadcastDomainType, networkOfferingId, zoneId, domainId, accountId, related, null, null, true, isNetworkDefault); network.setGuruName(guruNames.get(network.getTrafficType())); network.setDns1(zone.getDns1()); network.setDns2(zone.getDns2()); From 6c45b92a68f47d47a21703f91a033e6374327177 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 16:15:40 -0800 Subject: [PATCH 23/41] bug 7861: secondary storage page - implement details tab. --- ui/jsp/secondarystorage.jsp | 62 ++++++++++++++++++++++- ui/scripts/cloud.core.secondarystorage.js | 11 +++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/ui/jsp/secondarystorage.jsp b/ui/jsp/secondarystorage.jsp index 63071e8ac5e..1d040cc818e 100644 --- a/ui/jsp/secondarystorage.jsp +++ b/ui/jsp/secondarystorage.jsp @@ -72,7 +72,67 @@
- + +
+
+
+ <%=t.t("zone")%>:
+
+
+
+
+
+
+
+
+
+ <%=t.t("type")%>:
+
+
+
+
+
+
+
+
+
+ <%=t.t("ip.address")%>:
+
+
+
+
+
+
+
+
+
+ <%=t.t("state")%>:
+
+
+
+
+
+
+
+
+
+ <%=t.t("version")%>:
+
+
+
+
+
+
+
+
+
+ <%=t.t("last.disconnected")%>:
+
+
+
+
+
+
diff --git a/ui/scripts/cloud.core.secondarystorage.js b/ui/scripts/cloud.core.secondarystorage.js index 7d6e8733159..52c63384170 100644 --- a/ui/scripts/cloud.core.secondarystorage.js +++ b/ui/scripts/cloud.core.secondarystorage.js @@ -72,7 +72,16 @@ function secondaryStorageJsonToDetailsTab() { $thisTab.find("#id").text(fromdb(jsonObj.id)); $thisTab.find("#grid_header_title").text(fromdb(jsonObj.name)); $thisTab.find("#name").text(fromdb(jsonObj.name)); - + + $thisTab.find("#zonename").text(fromdb(jsonObj.zonename)); + $thisTab.find("#type").text(jsonObj.type); + $thisTab.find("#ipaddress").text(jsonObj.ipaddress); + + setHostStateInRightPanel(fromdb(jsonObj.state), $thisTab.find("#state")) + + $thisTab.find("#version").text(jsonObj.version); + setDateField(jsonObj.disconnected, $thisTab.find("#disconnected")); + //actions *** var $actionLink = $thisTab.find("#action_link"); $actionLink.bind("mouseover", function(event) { From 72b4552f01ae403a54e1f82f9cb8b059cd5de1da Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 11 Jan 2011 16:40:36 -0800 Subject: [PATCH 24/41] bug 7861: secondary storage page - implement "Delete Secondary Storage" action. --- ui/jsp/zone.jsp | 4 +- ui/scripts/cloud.core.secondarystorage.js | 56 ++++++++++++++++++++--- ui/scripts/cloud.core.zone.js | 4 ++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/ui/jsp/zone.jsp b/ui/jsp/zone.jsp index b4004953eb9..9d54c1ae090 100644 --- a/ui/jsp/zone.jsp +++ b/ui/jsp/zone.jsp @@ -22,8 +22,10 @@
<%=t.t("details")%>
+ + + + + diff --git a/ui/scripts/cloud.core.secondarystorage.js b/ui/scripts/cloud.core.secondarystorage.js index a8dfcb888dd..a140dea0d09 100644 --- a/ui/scripts/cloud.core.secondarystorage.js +++ b/ui/scripts/cloud.core.secondarystorage.js @@ -18,10 +18,8 @@ function afterLoadSecondaryStorageJSP($midmenuItem1) { var $topButtonContainer = clearButtonsOnTop(); - $("#top_buttons").appendTo($topButtonContainer); - - //initDialog("dialog_add_external_cluster_in_secondaryStorage_page", 320); - + $("#top_buttons").appendTo($topButtonContainer); + initDialog("dialog_add_secondarystorage"); secondaryStorageRefreshDataBinding(); } @@ -31,15 +29,9 @@ function secondaryStorageRefreshDataBinding() { } function secondaryStorageJsonToRightPanel($midmenuItem1) { - $("#right_panel_content").data("$midmenuItem1", $midmenuItem1); - - /* - bindEventHandlerToDialogAddVlanForZone(); - bindAddPodButton($("#add_pod_button"), $midmenuItem1); - bindAddSecondaryStorageButton($("#add_secondarystorage_button"), $midmenuItem1); - */ - - secondaryStorageJsonToDetailsTab(); + $("#right_panel_content").data("$midmenuItem1", $midmenuItem1); + bindAddSecondaryStorageButton($midmenuItem1.data("zoneObj")); + secondaryStorageJsonToDetailsTab(); } function secondaryStorageJsonToDetailsTab() { diff --git a/ui/scripts/cloud.core.zone.js b/ui/scripts/cloud.core.zone.js index 80d50941262..d0a765fc5f0 100644 --- a/ui/scripts/cloud.core.zone.js +++ b/ui/scripts/cloud.core.zone.js @@ -52,7 +52,7 @@ function zoneJsonToRightPanel($leftmenuItem1) { bindAddPodButton($("#add_pod_button"), $leftmenuItem1); //bindAddVLANButton($("#add_vlan_button"), $leftmenuItem1); - bindAddSecondaryStorageButton($("#add_secondarystorage_button"), $leftmenuItem1); + bindAddSecondaryStorageButton($leftmenuItem1.data("jsonObj")); var pods; var zoneObj = $leftmenuItem1.data("jsonObj"); @@ -581,13 +581,8 @@ function bindAddVLANButton($button, $leftmenuItem1) { } -function bindAddSecondaryStorageButton($button, $leftmenuItem1) { - $button.show(); - $button.unbind("click").bind("click", function(event) { - //if($("#tab_content_secondarystorage").css("display") == "none") - // $("#tab_secondarystorage").click(); - - var zoneObj = $leftmenuItem1.data("jsonObj"); +function bindAddSecondaryStorageButton(zoneObj) { + $("#add_secondarystorage_button").unbind("click").bind("click", function(event) { $("#dialog_add_secondarystorage").find("#zone_name").text(fromdb(zoneObj.name)); $("#dialog_add_secondarystorage").find("#info_container").hide(); @@ -615,16 +610,8 @@ function bindAddSecondaryStorageButton($button, $leftmenuItem1) { dataType: "json", success: function(json) { $thisDialog.find("#spinning_wheel").hide(); - $thisDialog.dialog("close"); - - $("#zone_"+zoneId).find("#secondarystorage_header").click(); - /* - var $subgridItem = $("#secondary_storage_tab_template").clone(true); - secondaryStorageJSONToTemplate(json.addsecondarystorageresponse.secondarystorage, $subgridItem); - $subgridItem.find("#after_action_info").text("Secondary storage was added successfully."); - $subgridItem.find("#after_action_info_container").removeClass("error").addClass("success").show(); - $("#tab_content_secondarystorage").find("#tab_container").append($subgridItem.show()); - */ + $thisDialog.dialog("close"); + $("#zone_"+zoneId).find("#secondarystorage_header").click(); }, error: function(XMLHttpResponse) { handleError(XMLHttpResponse, function() { From ad4ed5b2fdc8778c7e0f01b5ebfad1c864e657d0 Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Tue, 11 Jan 2011 11:19:21 -0800 Subject: [PATCH 31/41] added hypervisor type to vm --- api/src/com/cloud/vm/VirtualMachine.java | 3 + core/src/com/cloud/vm/ConsoleProxyVO.java | 59 +--- core/src/com/cloud/vm/DomainRouterVO.java | 54 +--- .../com/cloud/vm/SecondaryStorageVmVO.java | 58 +--- core/src/com/cloud/vm/UserVmVO.java | 37 +-- core/src/com/cloud/vm/VMInstanceVO.java | 56 +--- .../cloud/agent/manager/AgentManagerImpl.java | 2 +- .../src/com/cloud/configuration/Config.java | 8 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 4 +- .../VirtualNetworkApplianceManagerImpl.java | 6 +- .../SecondaryStorageManagerImpl.java | 4 +- server/src/com/cloud/vm/ItWorkDao.java | 16 +- server/src/com/cloud/vm/ItWorkDaoImpl.java | 45 +++ server/src/com/cloud/vm/ItWorkVO.java | 56 ++-- .../src/com/cloud/vm/UserVmManagerImpl.java | 17 +- .../com/cloud/vm/VirtualMachineManager.java | 7 +- .../cloud/vm/VirtualMachineManagerImpl.java | 286 ++++++++++++------ .../cloud/vm/VirtualMachineProfileImpl.java | 12 +- .../src/com/cloud/vm/dao/VMInstanceDao.java | 2 +- .../com/cloud/vm/dao/VMInstanceDaoImpl.java | 4 +- setup/db/create-schema.sql | 22 +- .../com/cloud/utils/time/InaccurateClock.java | 9 +- 22 files changed, 381 insertions(+), 386 deletions(-) diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index b6ccd7724e2..de435368305 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import com.cloud.acl.ControlledEntity; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.fsm.FiniteState; import com.cloud.utils.fsm.StateMachine; @@ -223,4 +224,6 @@ public interface VirtualMachine extends RunningOn, ControlledEntity { public long getServiceOfferingId(); Type getType(); + + HypervisorType getHypervisorType(); } diff --git a/core/src/com/cloud/vm/ConsoleProxyVO.java b/core/src/com/cloud/vm/ConsoleProxyVO.java index 0fd99fc9978..e863cb2d233 100644 --- a/core/src/com/cloud/vm/ConsoleProxyVO.java +++ b/core/src/com/cloud/vm/ConsoleProxyVO.java @@ -28,6 +28,8 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; /** * ConsoleProxyVO domain object @@ -97,64 +99,11 @@ public class ConsoleProxyVO extends VMInstanceVO implements ConsoleProxy { /** * Correct constructor to use. */ - public ConsoleProxyVO(long id, long serviceOfferingId, String name, long templateId, long guestOSId, long dataCenterId, long domainId, long accountId, int activeSession) { - super(id, serviceOfferingId, name, name, Type.ConsoleProxy, templateId, guestOSId, domainId, accountId, false); + public ConsoleProxyVO(long id, long serviceOfferingId, String name, long templateId, HypervisorType hypervisorType, long guestOSId, long dataCenterId, long domainId, long accountId, int activeSession) { + super(id, serviceOfferingId, name, name, Type.ConsoleProxy, templateId, hypervisorType, guestOSId, domainId, accountId, false); this.activeSession = activeSession; } - public ConsoleProxyVO(ConsoleProxyVO that) { - this(that.id, that.serviceOfferingId, that.instanceName, that.guestMacAddress, that.guestIpAddress, that.guestNetmask, that.privateMacAddress, that.privateIpAddress, that.privateNetmask, that.templateId, that.guestOSId, that.publicMacAddress, that.publicIpAddress, that.publicNetmask, that.vlanDbId, that.vlanId, that.podId, that.dataCenterId, that.domainId, that.accountId, that.gateway, that.hostId, that.dns1, that.dns2, that.domain, that.ramSize, that.activeSession); - this.vncPassword = that.vncPassword; - this.sslEnabled = that.sslEnabled; - this.sessionDetails = that.sessionDetails; - } - - public ConsoleProxyVO( - long id, - long serviceOfferingId, - String name, - String guestMacAddress, - String guestIpAddress, - String guestNetMask, - String privateMacAddress, - String privateIpAddress, - String privateNetmask, - long templateId, - long guestOSId, - String publicMacAddress, - String publicIpAddress, - String publicNetmask, - Long vlanDbId, - String vlanId, - long podId, - long dataCenterId, - long domainId, - long accountId, - String gateway, - Long hostId, - String dns1, - String dns2, - String domain, - int ramSize, - int activeSession) { - super(id, serviceOfferingId, name, name, Type.ConsoleProxy, templateId, guestOSId, - privateMacAddress, privateIpAddress, privateNetmask, dataCenterId, podId, domainId, accountId, true, hostId); - this.gateway = gateway; - this.publicIpAddress = publicIpAddress; - this.publicNetmask = publicNetmask; - this.publicMacAddress = publicMacAddress; - this.guestIpAddress = guestIpAddress; - this.guestMacAddress = guestMacAddress; - this.guestNetmask = guestNetMask; - this.vlanDbId = vlanDbId; - this.vlanId = vlanId; - this.dns1 = dns1; - this.dns2 = dns2; - this.domain = domain; - this.ramSize = ramSize; - this.activeSession = activeSession; - } - protected ConsoleProxyVO() { super(); } diff --git a/core/src/com/cloud/vm/DomainRouterVO.java b/core/src/com/cloud/vm/DomainRouterVO.java index 3eb47f2aa19..3daa59d5838 100755 --- a/core/src/com/cloud/vm/DomainRouterVO.java +++ b/core/src/com/cloud/vm/DomainRouterVO.java @@ -26,6 +26,7 @@ import javax.persistence.Enumerated; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.router.VirtualRouter; import com.cloud.utils.net.NetUtils; @@ -92,66 +93,17 @@ public class DomainRouterVO extends VMInstanceVO implements VirtualRouter { @Enumerated(EnumType.STRING) private Role role = Role.DHCP_FIREWALL_LB_PASSWD_USERDATA; - public DomainRouterVO(DomainRouterVO that) { - this(that.id, that.serviceOfferingId, that.instanceName, that.privateMacAddress, that.privateIpAddress, that.privateNetmask, that.templateId, that.guestOSId, that.guestMacAddress, that.guestIpAddress, that.guestNetmask, that.accountId, that.domainId, that.publicMacAddress, that.publicIpAddress, that.publicNetmask, that.vlanDbId, that.vlanId, that.podId, that.dataCenterId, that.ramSize, that.gateway, that.domain, that.dns1, that.dns2); - this.vnet = that.vnet; - this.role = that.role; - } - - public DomainRouterVO(long id, - long serviceOfferingId, - String name, - String privateMacAddress, - String privateIpAddress, - String privateNetmask, - long templateId, - long guestOSId, - String guestMacAddress, - String guestIpAddress, - String guestNetmask, - long accountId, - long domainId, - String publicMacAddress, - String publicIpAddress, - String publicNetMask, - Long vlanDbId, String vlanId, - long podId, - long dataCenterId, - int ramSize, - String gateway, - String domain, - String dns1, - String dns2) { - super(id, serviceOfferingId, name, name, Type.DomainRouter, templateId, guestOSId, privateMacAddress, privateIpAddress, privateNetmask, dataCenterId, podId, domainId, accountId, true, null); - this.privateMacAddress = privateMacAddress; - this.guestMacAddress = guestMacAddress; - this.guestIpAddress = guestIpAddress; - this.publicIpAddress = publicIpAddress; - this.publicMacAddress = publicMacAddress; - this.publicNetmask = publicNetMask; - this.vlanDbId = vlanDbId; - this.vlanId = vlanId; - this.ramSize = ramSize; - this.gateway = gateway; - this.domain = domain; - this.dns1 = dns1; - this.dns2 = dns2; - this.dataCenterId = dataCenterId; - this.accountId = accountId; - this.domainId = domainId; - this.guestNetmask = guestNetmask; - } - public DomainRouterVO(long id, long serviceOfferingId, String name, long templateId, + HypervisorType hypervisorType, long guestOSId, long domainId, long accountId, long networkConfigurationId, boolean haEnabled) { - super(id, serviceOfferingId, name, name, Type.DomainRouter, templateId, guestOSId, domainId, accountId, haEnabled); + super(id, serviceOfferingId, name, name, Type.DomainRouter, templateId, hypervisorType, guestOSId, domainId, accountId, haEnabled); this.networkId = networkConfigurationId; } diff --git a/core/src/com/cloud/vm/SecondaryStorageVmVO.java b/core/src/com/cloud/vm/SecondaryStorageVmVO.java index 5afb42c8633..c4d2e6ecad5 100644 --- a/core/src/com/cloud/vm/SecondaryStorageVmVO.java +++ b/core/src/com/cloud/vm/SecondaryStorageVmVO.java @@ -26,6 +26,8 @@ import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; /** * SecondaryStorageVmVO domain object @@ -89,63 +91,11 @@ public class SecondaryStorageVmVO extends VMInstanceVO implements SecondaryStora - public SecondaryStorageVmVO(long id, long serviceOfferingId, String name, long templateId, long guestOSId, long dataCenterId, + public SecondaryStorageVmVO(long id, long serviceOfferingId, String name, long templateId, HypervisorType hypervisorType, long guestOSId, long dataCenterId, long domainId, long accountId) { - super(id, serviceOfferingId, name, name, Type.SecondaryStorageVm, templateId, guestOSId, domainId, accountId, true); + super(id, serviceOfferingId, name, name, Type.SecondaryStorageVm, templateId, hypervisorType, guestOSId, domainId, accountId, true); } - public SecondaryStorageVmVO( - long id, - long serviceOfferingId, - String name, - String guestMacAddress, - String guestIpAddress, - String guestNetMask, - String privateMacAddress, - String privateIpAddress, - String privateNetmask, - long templateId, - long guestOSId, - String publicMacAddress, - String publicIpAddress, - String publicNetmask, - Long vlanDbId, - String vlanId, - long podId, - long dataCenterId, - long domainId, - long accountId, - String gateway, - Long hostId, - String dns1, - String dns2, - String domain, - int ramSize, - String guid, - String nfsShare) { - super(id, serviceOfferingId, name, name, Type.SecondaryStorageVm, templateId, guestOSId, - privateMacAddress, privateIpAddress, privateNetmask, dataCenterId, podId, domainId, accountId, true, hostId); - this.gateway = gateway; - this.publicIpAddress = publicIpAddress; - this.publicNetmask = publicNetmask; - this.publicMacAddress = publicMacAddress; - this.guestIpAddress = guestIpAddress; - this.guestMacAddress = guestMacAddress; - this.guestNetmask = guestNetMask; - this.vlanDbId = vlanDbId; - this.vlanId = vlanId; - this.dns1 = dns1; - this.dns2 = dns2; - this.domain = domain; - this.ramSize = ramSize; - this.setGuid(guid); - this.nfsShare = nfsShare; - } - - public SecondaryStorageVmVO(SecondaryStorageVmVO that) { - this(that.id, that.serviceOfferingId, that.instanceName, that.guestMacAddress, that.guestIpAddress, that.guestNetmask, that.privateMacAddress, that.privateIpAddress, that.privateNetmask, that.templateId, that.guestOSId, that.publicMacAddress, that.publicIpAddress, that.publicNetmask, that.vlanDbId, that.vlanId, that.podId, that.dataCenterId, that.domainId, that.accountId, that.gateway, that.hostId, that.dns1, that.dns2, that.domain, that.ramSize, that.guid, that.nfsShare); - } - protected SecondaryStorageVmVO() { super(); } diff --git a/core/src/com/cloud/vm/UserVmVO.java b/core/src/com/cloud/vm/UserVmVO.java index 40ac9912bda..89ba6f70e5d 100755 --- a/core/src/com/cloud/vm/UserVmVO.java +++ b/core/src/com/cloud/vm/UserVmVO.java @@ -23,6 +23,7 @@ import javax.persistence.Entity; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.uservm.UserVm; @Entity @@ -66,6 +67,7 @@ public class UserVmVO extends VMInstanceVO implements UserVm { transient String password; + @Override public String getPassword() { return password; } @@ -140,49 +142,18 @@ public class UserVmVO extends VMInstanceVO implements UserVm { String instanceName, String displayName, long templateId, + HypervisorType hypervisorType, long guestOsId, boolean haEnabled, long domainId, long accountId, long serviceOfferingId, String userData, String name) { - super(id, serviceOfferingId, name, instanceName, Type.User, templateId, guestOsId, domainId, accountId, haEnabled); + super(id, serviceOfferingId, name, instanceName, Type.User, templateId, hypervisorType, guestOsId, domainId, accountId, haEnabled); this.userData = userData; this.displayName = displayName != null ? displayName : null; } - public UserVmVO(long id, - String name, - long templateId, - long guestOSId, - long accountId, - long domainId, - long serviceOfferingId, - String guestMacAddress, - String guestIpAddress, - String guestNetMask, - String externalIpAddress, - String externalMacAddress, - Long vlanDbId, - Long routerId, - long podId, - long dcId, - boolean haEnabled, - String displayName, - String userData) { - super(id, serviceOfferingId, name, name, Type.User, templateId, guestOSId, guestMacAddress, guestIpAddress, guestNetMask, dcId, podId, domainId, accountId, haEnabled, null); - this.domainRouterId = routerId; - this.guestIpAddress = guestIpAddress; - this.guestNetmask = guestNetMask; - this.guestMacAddress = guestMacAddress; - this.externalIpAddress = externalIpAddress; - this.externalMacAddress = externalMacAddress; - this.setUserData(userData); - this.setExternalVlanDbId(vlanDbId); - this.isoId = null; - this.displayName = displayName; - } - protected UserVmVO() { super(); } diff --git a/core/src/com/cloud/vm/VMInstanceVO.java b/core/src/com/cloud/vm/VMInstanceVO.java index 3714f5778ee..bd22f50feb2 100644 --- a/core/src/com/cloud/vm/VMInstanceVO.java +++ b/core/src/com/cloud/vm/VMInstanceVO.java @@ -35,6 +35,7 @@ import javax.persistence.TableGenerator; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.StateMachine; import com.cloud.utils.fsm.FiniteStateObject; @@ -139,12 +140,17 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject -1) { - this.templateId = vmTemplateId; - } else { - this.templateId = null; - } - this.guestOSId = guestOSId; - this.privateIpAddress = privateIpAddress; - this.privateMacAddress = privateMacAddress; - this.privateNetmask = privateNetmask; - this.hostId = hostId; - this.dataCenterId = dataCenterId; - this.podId = podId; - this.type = type; - this.haEnabled = haEnabled; - this.instanceName = instanceName; - this.updated = 0; - this.updateTime = new Date(); - this.vncPassword = Long.toHexString(new Random().nextLong()); - this.state = State.Creating; - this.serviceOfferingId = serviceOfferingId; - this.domainId = domainId; - this.accountId = accountId; - } - protected VMInstanceVO() { } @@ -239,6 +202,11 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject avoid) { VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl( - vm, template, offering, null, null, null); + vm, template, offering, null, null); DeployDestination dest = null; DataCenterDeployment plan = new DataCenterDeployment(dc.getId(), pod.getId(), sp.getClusterId(), null); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 7e14f81c86a..abbc40bac62 100644 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -213,7 +213,13 @@ public enum Config { SSOKey("Hidden", ManagementServer.class, String.class, "security.singlesignon.key", null, "A Single Sign-On key used for logging into the cloud", null), SSOAuthTolerance("Advanced", ManagementServer.class, Long.class, "security.singlesignon.tolerance.millis", "300000", "The allowable clock difference in milliseconds between when an SSO login request is made and when it is received.", null), //NetworkType("Hidden", ManagementServer.class, String.class, "network.type", "vlan", "The type of network that this deployment will use.", "vlan,direct"), - HashKey("Hidden", ManagementServer.class, String.class, "security.hash.key", null, "for generic key-ed hash", null); + HashKey("Hidden", ManagementServer.class, String.class, "security.hash.key", null, "for generic key-ed hash", null), + + VmOpWaitInterval("Advanced", ManagementServer.class, Integer.class, "vm.op.wait.interval", "120", "Seconds to wait before checking if a previous operation has succeeded", null), + VmOpLockStateRetry("Advanced", ManagementServer.class, Integer.class, "vm.op.lock.state.retry", "5", "Times to retry locking the state of a VM for operations", "-1 means try forever"), + VmOpCleanupInterval("Advanced", ManagementServer.class, Long.class, "vm.op.cleanup.interval", "86400", "Interval to run the thread that cleans up the vm operations in seconds", "Seconds"), + VmOpCleanupWait("Advanced", ManagementServer.class, Long.class, "vm.op.cleanup.wait", "3600", "Seconds to wait before cleanuping up any vm work items", "Seconds"), + VmOpCancelInterval("Advanced", ManagementServer.class, Long.class, "vm.op.cancel.interval", "3600", "Seconds to wait before cancelling a operation", "Seconds"); private final String _category; private final Class _componentClass; diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index ef2a5b62ba1..a82f7229c54 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -562,7 +562,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx ConsoleProxyVO proxy = _consoleProxyDao.findById(proxyVmId); Account systemAcct = _accountMgr.getSystemAccount(); User systemUser = _accountMgr.getSystemUser(); - return _itMgr.start(proxy, null, systemUser, systemAcct, null); + return _itMgr.start(proxy, null, systemUser, systemAcct); } public ConsoleProxyVO assignProxyFromRunningPool(long dataCenterId) { @@ -713,7 +713,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx for (NetworkOfferingVO offering : offerings) { networks.add(new Pair(_networkMgr.setupNetwork(systemAcct, offering, plan, null, null, false, false).get(0), null)); } - ConsoleProxyVO proxy = new ConsoleProxyVO(id, _serviceOffering.getId(), name, _template.getId(), _template.getGuestOSId(), dataCenterId, systemAcct.getDomainId(), systemAcct.getId(), 0); + ConsoleProxyVO proxy = new ConsoleProxyVO(id, _serviceOffering.getId(), name, _template.getId(), _template.getHypervisorType(), _template.getGuestOSId(), dataCenterId, systemAcct.getDomainId(), systemAcct.getId(), 0); try { proxy = _itMgr.allocate(proxy, _template, _serviceOffering, networks, plan, null, systemAcct); } catch (InsufficientCapacityException e) { diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 6c02479f1ac..d811faeb1f2 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1069,7 +1069,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian networks.add(new Pair(controlConfig, null)); router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), _template.getId(), - _template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); + _template.getHypervisorType(), _template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); router = _itMgr.allocate(router, _template, _offering, networks, plan, null, owner); if(router != null){ EventUtils.saveEvent(User.UID_SYSTEM, owner.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_ROUTER_CREATE, "successfully create router : " + router.getName(), startEventId); @@ -1134,7 +1134,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian networks.add(new Pair(controlConfig, null)); router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), _template.getId(), - _template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); + _template.getHypervisorType(), _template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA()); router.setRole(Role.DHCP_USERDATA); router = _itMgr.allocate(router, _template, _offering, networks, plan, null, owner); if(router != null){ @@ -1440,7 +1440,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian private DomainRouterVO start(DomainRouterVO router, User user, Account caller) throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Starting router " + router); - if (_itMgr.start(router, null, user, caller, null) != null) { + if (_itMgr.start(router, null, user, caller) != null) { return _routerDao.findById(router.getId()); } else { return null; diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index c2272b80601..1410de77c9d 100644 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -261,7 +261,7 @@ public class SecondaryStorageManagerImpl implements SecondaryStorageVmManager, V SecondaryStorageVmVO secStorageVm = _secStorageVmDao.findById(secStorageVmId); Account systemAcct = _accountMgr.getSystemAccount(); User systemUser = _accountMgr.getSystemUser(); - return _itMgr.start(secStorageVm, null, systemUser, systemAcct, null); + return _itMgr.start(secStorageVm, null, systemUser, systemAcct); } @@ -463,7 +463,7 @@ public class SecondaryStorageManagerImpl implements SecondaryStorageVmManager, V s_logger.info("Unable to setup due to concurrent operation. " + e); return new HashMap(); } - SecondaryStorageVmVO secStorageVm = new SecondaryStorageVmVO(id, _serviceOffering.getId(), name, _template.getId(), + SecondaryStorageVmVO secStorageVm = new SecondaryStorageVmVO(id, _serviceOffering.getId(), name, _template.getId(), _template.getHypervisorType(), _template.getGuestOSId(), dataCenterId, systemAcct.getDomainId(), systemAcct.getId()); try { secStorageVm = _itMgr.allocate(secStorageVm, _template, _serviceOffering, networks, plan, null, systemAcct); diff --git a/server/src/com/cloud/vm/ItWorkDao.java b/server/src/com/cloud/vm/ItWorkDao.java index 51c60943a9c..93b30c1654c 100644 --- a/server/src/com/cloud/vm/ItWorkDao.java +++ b/server/src/com/cloud/vm/ItWorkDao.java @@ -18,7 +18,21 @@ package com.cloud.vm; import com.cloud.utils.db.GenericDao; +import com.cloud.vm.VirtualMachine.State; public interface ItWorkDao extends GenericDao { - + /** + * find a work item based on the instanceId and the state. + * + * @param instanceId vm instance id + * @param state state + * @return ItWorkVO if found; null if not. + */ + ItWorkVO findByInstance(long instanceId, State state); + + /** + * cleanup rows that are either Done or Cancelled and been that way + * for at least wait time. + */ + void cleanup(long wait); } diff --git a/server/src/com/cloud/vm/ItWorkDaoImpl.java b/server/src/com/cloud/vm/ItWorkDaoImpl.java index 87ee53ae2fd..b7df1275f5b 100644 --- a/server/src/com/cloud/vm/ItWorkDaoImpl.java +++ b/server/src/com/cloud/vm/ItWorkDaoImpl.java @@ -20,10 +20,55 @@ package com.cloud.vm; import javax.ejb.Local; 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.time.InaccurateClock; +import com.cloud.vm.ItWorkVO.Step; +import com.cloud.vm.VirtualMachine.State; @Local(value=ItWorkDao.class) public class ItWorkDaoImpl extends GenericDaoBase implements ItWorkDao { + protected final SearchBuilder AllFieldsSearch; + protected final SearchBuilder CleanupSearch; + protected ItWorkDaoImpl() { super(); + + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("instance", AllFieldsSearch.entity().getInstanceId(), Op.EQ); + AllFieldsSearch.and("op", AllFieldsSearch.entity().getState(), Op.EQ); + AllFieldsSearch.and("step", AllFieldsSearch.entity().getStep(), Op.EQ); + AllFieldsSearch.done(); + + CleanupSearch = createSearchBuilder(); + CleanupSearch.and("step", CleanupSearch.entity().getState(), Op.IN); + CleanupSearch.and("time", CleanupSearch.entity().getUpdatedAt(), Op.LT); + CleanupSearch.done(); + } + + @Override + public ItWorkVO findByInstance(long instanceId, State state) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("instance", instanceId); + sc.setParameters("op", state); + + return findOneBy(sc); + } + + @Override + public void cleanup(long wait) { + SearchCriteria sc = CleanupSearch.create(); + sc.setParameters("step", Step.Done, Step.Cancelled); + sc.setParameters("time", InaccurateClock.getTimeInSeconds() - wait); + + remove(sc); + } + + @Override + public boolean update(String id, ItWorkVO work) { + work.setUpdatedAt(InaccurateClock.getTimeInSeconds()); + + return super.update(id, work); } } diff --git a/server/src/com/cloud/vm/ItWorkVO.java b/server/src/com/cloud/vm/ItWorkVO.java index c150a125851..56539d31920 100644 --- a/server/src/com/cloud/vm/ItWorkVO.java +++ b/server/src/com/cloud/vm/ItWorkVO.java @@ -17,48 +17,43 @@ */ package com.cloud.vm; -import java.util.Date; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import com.cloud.utils.db.GenericDao; +import com.cloud.utils.time.InaccurateClock; +import com.cloud.vm.VirtualMachine.State; @Entity @Table(name="op_it_work") public class ItWorkVO { - enum Type { - Start, - Cleanup; - } - enum ResourceType { Volume, Nic } enum Step { + Reserve, Prepare, Start, Started, + Cancelled, + Done } @Id @Column(name="id") String id; - @Column(name=GenericDao.CREATED_COLUMN) - Date created; + @Column(name="created_at") + long createdAt; @Column(name="mgmt_server_id") long managementServerId; @Column(name="type") - Type type; + State type; @Column(name="thread") String threadName; @@ -66,9 +61,8 @@ public class ItWorkVO { @Column(name="state") Step step; - @Column(name="cancel_taken") - @Temporal(value=TemporalType.TIMESTAMP) - Date taken; + @Column(name="updated_at") + long updatedAt; @Column(name="instance_id") long instanceId; @@ -103,7 +97,7 @@ public class ItWorkVO { protected ItWorkVO() { } - protected ItWorkVO(String id, long managementServerId, Type type, long instanceId) { + protected ItWorkVO(String id, long managementServerId, State type, long instanceId) { this.id = id; this.managementServerId = managementServerId; this.type = type; @@ -111,25 +105,27 @@ public class ItWorkVO { this.step = Step.Prepare; this.instanceId = instanceId; this.resourceType = null; + this.createdAt = InaccurateClock.getTimeInSeconds(); + this.updatedAt = createdAt; } public String getId() { return id; } - public Date getCreated() { - return created; + public Long getCreatedAt() { + return createdAt; } - + public long getManagementServerId() { return managementServerId; } - public Type getType() { + public State getState() { return type; } - public void setType(Type type) { + public void setState(State type) { this.type = type; } @@ -145,7 +141,19 @@ public class ItWorkVO { this.step = state; } - public Date getTaken() { - return taken; + public long getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(long updatedAt) { + this.updatedAt = updatedAt; + } + + public long getSecondsTaskIsInactive() { + return InaccurateClock.getTimeInSeconds() - this.updatedAt; + } + + public long getSecondsTaskHasBeenCreated() { + return InaccurateClock.getTimeInSeconds() - this.createdAt; } } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index c49fbee38c1..e11c465f853 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2291,8 +2291,15 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } } - UserVmVO vm = new UserVmVO(id, instanceName, cmd.getDisplayName(), - template.getId(), template.getGuestOSId(), offering.getOfferHA(), domainId, owner.getId(), offering.getId(), userData, hostName); + HypervisorType hypervisorType = null; + if (template == null || template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) { + hypervisorType = cmd.getHypervisor(); + } else { + hypervisorType = template.getHypervisorType(); + } + + UserVmVO vm = new UserVmVO(id, instanceName, cmd.getDisplayName(), template.getId(), hypervisorType, + template.getGuestOSId(), offering.getOfferHA(), domainId, owner.getId(), offering.getId(), userData, hostName); if (_itMgr.allocate(vm, template, offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, cmd.getHypervisor(), owner) == null) { @@ -2346,7 +2353,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager AccountVO owner = _accountDao.findById(vm.getAccountId()); try { - vm = _itMgr.start(vm, null, caller, owner, cmd.getHypervisor()); + vm = _itMgr.start(vm, null, caller, owner); } catch (Exception e) { e.printStackTrace(); } @@ -2500,9 +2507,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager userId = accountAndUserValidation(vmId, account, userId, vm); UserVO user = _userDao.findById(userId); - VolumeVO disk = _volsDao.findByInstance(vmId).get(0); - HypervisorType hyperType = _volsDao.getHypervisorType(disk.getId()); - return _itMgr.start(vm, null, user, account, hyperType); + return _itMgr.start(vm, null, user, account); } @Override diff --git a/server/src/com/cloud/vm/VirtualMachineManager.java b/server/src/com/cloud/vm/VirtualMachineManager.java index a866e168450..c3020e1311b 100644 --- a/server/src/com/cloud/vm/VirtualMachineManager.java +++ b/server/src/com/cloud/vm/VirtualMachineManager.java @@ -35,7 +35,6 @@ import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; -import com.cloud.vm.VirtualMachine.Event; /** * Manages allocating resources to vms. @@ -71,7 +70,7 @@ public interface VirtualMachineManager extends Manager { HypervisorType hyperType, Account owner) throws InsufficientCapacityException; - T start(T vm, Map params, User caller, Account account, HypervisorType hyperType) throws InsufficientCapacityException, ResourceUnavailableException; + T start(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException; boolean stop(T vm, User caller, Account account) throws ResourceUnavailableException; @@ -79,9 +78,9 @@ public interface VirtualMachineManager extends Manager { void registerGuru(VirtualMachine.Type type, VirtualMachineGuru guru); - boolean stateTransitTo(VMInstanceVO vm, Event e, Long id); + boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId); - T advanceStart(T vm, Map params, User caller, Account account, HypervisorType hyperType) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; + T advanceStart(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; boolean advanceStop(T vm, boolean forced, User caller, Account account) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException; diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 787db4ac2c0..ab41c08edd4 100644 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; 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.naming.ConfigurationException; @@ -38,8 +41,6 @@ import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; import com.cloud.cluster.ClusterManager; -import com.cloud.cluster.ClusterManagerListener; -import com.cloud.cluster.ManagementServerHostVO; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; @@ -81,12 +82,13 @@ import com.cloud.utils.Pair; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateListener; import com.cloud.utils.fsm.StateMachine2; -import com.cloud.vm.ItWorkVO.Type; +import com.cloud.vm.ItWorkVO.Step; import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.ConsoleProxyDao; @@ -97,39 +99,47 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @Local(value=VirtualMachineManager.class) -public class VirtualMachineManagerImpl implements VirtualMachineManager, ClusterManagerListener { +public class VirtualMachineManagerImpl implements VirtualMachineManager { private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class); String _name; - @Inject private StorageManager _storageMgr; - @Inject private NetworkManager _networkMgr; - @Inject private AgentManager _agentMgr; - @Inject private VMInstanceDao _vmDao; - @Inject private ServiceOfferingDao _offeringDao; - @Inject private VMTemplateDao _templateDao; - @Inject private UserDao _userDao; - @Inject private AccountDao _accountDao; - @Inject private DomainDao _domainDao; - @Inject private ClusterManager _clusterMgr; - @Inject private ItWorkDao _workDao; - @Inject private UserVmDao _userVmDao; - @Inject private DomainRouterDao _routerDao; - @Inject private ConsoleProxyDao _consoleDao; - @Inject private SecondaryStorageVmDao _secondaryDao; - @Inject private UsageEventDao _usageEventDao; - @Inject private NicDao _nicsDao; + @Inject protected StorageManager _storageMgr; + @Inject protected NetworkManager _networkMgr; + @Inject protected AgentManager _agentMgr; + @Inject protected VMInstanceDao _vmDao; + @Inject protected ServiceOfferingDao _offeringDao; + @Inject protected VMTemplateDao _templateDao; + @Inject protected UserDao _userDao; + @Inject protected AccountDao _accountDao; + @Inject protected DomainDao _domainDao; + @Inject protected ClusterManager _clusterMgr; + @Inject protected ItWorkDao _workDao; + @Inject protected UserVmDao _userVmDao; + @Inject protected DomainRouterDao _routerDao; + @Inject protected ConsoleProxyDao _consoleDao; + @Inject protected SecondaryStorageVmDao _secondaryDao; + @Inject protected UsageEventDao _usageEventDao; + @Inject protected NicDao _nicsDao; @Inject(adapter=DeploymentPlanner.class) - private Adapters _planners; + protected Adapters _planners; @Inject(adapter=StateListener.class) - private Adapters> _stateListner; + protected Adapters> _stateListner; + Map> _vmGurus = new HashMap>(); Map _hvGurus = new HashMap(); - private StateMachine2 _stateMachine; + protected StateMachine2 _stateMachine; - private int _retry; - private long _nodeId; + ScheduledExecutorService _executor = null; + + protected int _retry; + protected long _nodeId; + protected long _cleanupWait; + protected long _cleanupInterval; + protected long _cancelWait; + protected long _opWaitInterval; + protected int _lockStateRetry; @Override public void registerGuru(VirtualMachine.Type type, VirtualMachineGuru guru) { @@ -153,7 +163,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster s_logger.debug("Allocating entries for VM: " + vm); } - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, serviceOffering, owner, params, hyperType); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, serviceOffering, owner, params); vm.setDataCenterId(plan.getDataCenterId()); if (plan.getPodId() != null) { @@ -338,6 +348,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster @Override public boolean start() { + _executor.scheduleAtFixedRate(new CleanupTask(), _cleanupInterval, _cleanupInterval, TimeUnit.SECONDS); return true; } @@ -364,8 +375,14 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster _hvGurus.put(guru.getHypervisorType(), guru); } + _cancelWait = NumbersUtil.parseLong(params.get(Config.VmOpCancelInterval.key()), 3600); + _cleanupWait = NumbersUtil.parseLong(params.get(Config.VmOpCleanupWait.key()), 3600); + _cleanupInterval = NumbersUtil.parseLong(params.get(Config.VmOpCleanupInterval.key()), 86400) * 1000; + _opWaitInterval = NumbersUtil.parseLong(params.get(Config.VmOpWaitInterval.key()), 120) * 1000; + _lockStateRetry = NumbersUtil.parseInt(params.get(Config.VmOpLockStateRetry.key()), 5); + + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Vm-Operations-Cleanup")); _nodeId = _clusterMgr.getId(); - _clusterMgr.registerListener(this); setStateMachine(); @@ -381,9 +398,9 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster } @Override - public T start(T vm, Map params, User caller, Account account, HypervisorType hyperType) throws InsufficientCapacityException, ResourceUnavailableException { + public T start(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException { try { - return advanceStart(vm, params, caller, account, hyperType); + return advanceStart(vm, params, caller, account); } catch (ConcurrentOperationException e) { throw new CloudRuntimeException("Unable to start a VM due to concurrent operation", e); } @@ -400,55 +417,130 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster return null; } + protected boolean checkWorkItems(VMInstanceVO vm, State state) throws ConcurrentOperationException { + while (true) { + ItWorkVO vo = _workDao.findByInstance(vm.getId(), state); + if (vo == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find work for " + vm); + } + return true; + } + + if (vo.getStep() == Step.Done || vo.getStep() == Step.Cancelled) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Work for " + vm + " is " + vo.getStep()); + } + return true; + } + + if (vo.getSecondsTaskIsInactive() > _cancelWait) { + s_logger.warn("The task item for vm " + vm + " has been inactive for " + vo.getSecondsTaskIsInactive()); + return false; + } + + try { + Thread.sleep(_opWaitInterval); + } catch (InterruptedException e) { + s_logger.info("Waiting for " + vm + " but is interrupted"); + throw new ConcurrentOperationException("Waiting for " + vm + " but is interrupted"); + } + s_logger.debug("Waiting some more to make sure there's no activity on " + vm); + } + + + } + + @DB + protected Pair changeToStartState(VirtualMachineGuru vmGuru, T vm, User caller, Account account) throws ConcurrentOperationException { + long vmId = vm.getId(); + + ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getId()); + int retry = _lockStateRetry; + while (retry-- > 0) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + if (_vmDao.updateIf(vm, Event.StartRequested, null, work.getId())) { + + Journal journal = new Journal.LogJournal("Creating " + vm, s_logger); + work = _workDao.persist(work); + ReservationContextImpl context = new ReservationContextImpl(work.getId(), journal, caller, account); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); + } + return new Pair(vmGuru.findById(vmId), context); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Determining why we're unable to update the state to Starting for " + vm); + } + + try { + VMInstanceVO instance = _vmDao.lockRow(vmId, true); + if (instance == null) { + throw new ConcurrentOperationException("Unable to acquire lock on " + vm); + } + + State state = instance.getState(); + if (state == State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is already started: " + vm); + } + return new Pair(vmGuru.findById(vmId), null); + } + + if (state.isTransitional()) { + if (!checkWorkItems(vm, state)) { + throw new ConcurrentOperationException("There are concurrent operations on the VM " + vm); + } else { + continue; + } + } + + if (state != State.Stopped) { + s_logger.debug("VM " + vm + " is not in a state to be started: " + state); + return null; + } + + } finally { + txn.commit(); + } + } + + throw new ConcurrentOperationException("Unable to change the state of " + vm); + } + @Override - public T advanceStart(T vm, Map params, User caller, Account account, HypervisorType hyperType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { - State state = vm.getState(); - if (state == State.Running) { - s_logger.debug("VM is already started: " + vm); + public T advanceStart(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + long vmId = vm.getId(); + + VirtualMachineGuru vmGuru = getVmGuru(vm); + + Pair start = changeToStartState(vmGuru, vm, caller, account); + assert (start != null) : "Should never happen"; + + vm = start.first(); + ReservationContext ctx = start.second(); + + if (ctx == null) { // No need to start because it's already running. return vm; } - if (state == State.Starting) { - - } - - if (state != State.Stopped) { - s_logger.debug("VM " + vm + " is not in a state to be started: " + state); - return null; - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creating actual resources for VM " + vm); - } - - Journal journal = new Journal.LogJournal("Creating " + vm, s_logger); - - ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, ItWorkVO.Type.Start, vm.getId()); - work = _workDao.persist(work); - - ReservationContextImpl context = new ReservationContextImpl(work.getId(), journal, caller, account); - ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId()); VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodId(), null, null); - HypervisorGuru hvGuru; - if (hyperType != null && !hyperType.equals(HypervisorType.None)) { - hvGuru = _hvGurus.get(hyperType); - } else { - hvGuru = _hvGurus.get(template.getHypervisorType()); - } - @SuppressWarnings("unchecked") - VirtualMachineGuru vmGuru = (VirtualMachineGuru)_vmGurus.get(vm.getType()); - - vm.setReservationId(work.getId()); + HypervisorGuru hvGuru = _hvGurus.get(vm.getHypervisorType()); + + Journal journal = start.second().getJournal(); ExcludeList avoids = new ExcludeList(); int retry = _retry; DeployDestination dest = null; while (retry-- != 0) { // It's != so that it can match -1. - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, null, params, hyperType); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, null, params); for (DeploymentPlanner planner : _planners) { dest = planner.plan(vmProfile, plan, avoids); @@ -474,12 +566,9 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster stateTransitTo(vm, Event.OperationRetry, dest.getHost().getId()); } - vm.setDataCenterId(dest.getDataCenter().getId()); - vm.setPodId(dest.getPod().getId()); - try { _storageMgr.prepare(vmProfile, dest); - _networkMgr.prepare(vmProfile, dest, context); + _networkMgr.prepare(vmProfile, dest, ctx); } catch (ConcurrentOperationException e) { stateTransitTo(vm, Event.OperationFailed, null); throw e; @@ -497,17 +586,17 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster return null; } - vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, context); + vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); VirtualMachineTO vmTO = hvGuru.implement(vmProfile); Commands cmds = new Commands(OnError.Revert); cmds.addCommand(new StartCommand(vmTO)); - vmGuru.finalizeDeployment(cmds, vmProfile, dest, context); + vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); try { Answer[] answers = _agentMgr.send(dest.getHost().getId(), cmds); - if (getStartAnswer(answers).getResult() && vmGuru.finalizeStart(cmds, vmProfile, dest, context)) { + if (getStartAnswer(answers).getResult() && vmGuru.finalizeStart(cmds, vmProfile, dest, ctx)) { if (!stateTransitTo(vm, Event.OperationSucceeded, dest.getHost().getId())) { throw new CloudRuntimeException("Unable to transition to a new state."); } @@ -633,22 +722,13 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster stateTransitTo(vm, Event.OperationSucceeded, null); if (cleanup) { - ItWorkVO work = new ItWorkVO(reservationId, _nodeId, Type.Cleanup, vm.getId()); + ItWorkVO work = new ItWorkVO(reservationId, _nodeId, State.Stopping, vm.getId()); _workDao.persist(work); } return stopped; } - @Override - public void onManagementNodeJoined(List nodeList, long selfNodeId) { - - } - - @Override - public void onManagementNodeLeft(List nodeList, long selfNodeId) { - } - private void setStateMachine() { _stateMachine = new StateMachine2(); @@ -698,21 +778,36 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster _stateMachine.registerListeners(_stateListner); } - @Override - public boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long id) { + protected boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) { + vm.setReservationId(reservationId); if (vm instanceof UserVmVO) { - return _stateMachine.transitTO(vm, e, id, _userVmDao); + return _stateMachine.transitTO(vm, e, hostId, _userVmDao); } else if (vm instanceof ConsoleProxyVO) { - return _stateMachine.transitTO(vm, e, id, _consoleDao); + return _stateMachine.transitTO(vm, e, hostId, _consoleDao); } else if (vm instanceof SecondaryStorageVmVO) { - return _stateMachine.transitTO(vm, e, id, _secondaryDao); + return _stateMachine.transitTO(vm, e, hostId, _secondaryDao); } else if (vm instanceof DomainRouterVO) { - return _stateMachine.transitTO(vm, e, id, _routerDao); + return _stateMachine.transitTO(vm, e, hostId, _routerDao); } else { - return _stateMachine.transitTO(vm, e, id, _vmDao); + return _stateMachine.transitTO(vm, e, hostId, _vmDao); } } + @Override + public boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId) { + if (vm instanceof UserVmVO) { + return _stateMachine.transitTO(vm, e, hostId, _userVmDao); + } else if (vm instanceof ConsoleProxyVO) { + return _stateMachine.transitTO(vm, e, hostId, _consoleDao); + } else if (vm instanceof SecondaryStorageVmVO) { + return _stateMachine.transitTO(vm, e, hostId, _secondaryDao); + } else if (vm instanceof DomainRouterVO) { + return _stateMachine.transitTO(vm, e, hostId, _routerDao); + } else { + return _stateMachine.transitTO(vm, e, hostId, _vmDao); + } + } + @Override public boolean remove(T vm, User user, Account caller) { return _vmDao.remove(vm.getId()); @@ -742,4 +837,17 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Cluster return true; } + + protected class CleanupTask implements Runnable { + + @Override + public void run() { + s_logger.trace("VM Operation Thread Running"); + try { + _workDao.cleanup(_cleanupWait); + } catch (Exception e) { + s_logger.error("VM Operations failed due to ", e); + } + } + } } diff --git a/server/src/com/cloud/vm/VirtualMachineProfileImpl.java b/server/src/com/cloud/vm/VirtualMachineProfileImpl.java index 60b7135e000..28551ac3989 100644 --- a/server/src/com/cloud/vm/VirtualMachineProfileImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineProfileImpl.java @@ -51,9 +51,8 @@ public class VirtualMachineProfileImpl implements Virtua BootloaderType _bootloader; VirtualMachine.Type _type; - HypervisorType _hyperType; - public VirtualMachineProfileImpl(T vm, VMTemplateVO template, ServiceOfferingVO offering, Account owner, Map params, HypervisorType hyperType) { + public VirtualMachineProfileImpl(T vm, VMTemplateVO template, ServiceOfferingVO offering, Account owner, Map params) { _vm = vm; _template = template; _offering = offering; @@ -63,11 +62,10 @@ public class VirtualMachineProfileImpl implements Virtua _params = new HashMap(); } _type = vm.getType(); - _hyperType = hyperType; } public VirtualMachineProfileImpl(T vm) { - this(vm, null, null, null, null, null); + this(vm, null, null, null, null); } public VirtualMachineProfileImpl(VirtualMachine.Type type) { @@ -112,11 +110,7 @@ public class VirtualMachineProfileImpl implements Virtua @Override public HypervisorType getHypervisorType() { - if (_hyperType != null && !_hyperType.equals(HypervisorType.None)) { - return _hyperType; - } - getTemplate(); - return _template.getHypervisorType(); + return _vm.getHypervisorType(); } @Override diff --git a/server/src/com/cloud/vm/dao/VMInstanceDao.java b/server/src/com/cloud/vm/dao/VMInstanceDao.java index d5675245bcd..8ad0edcd9f5 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDao.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDao.java @@ -53,7 +53,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< */ public List listNonExpungedByZoneAndTemplate(long zoneId, long templateId); - boolean updateIf(VMInstanceVO vm, VirtualMachine.Event event, Long hostId); + boolean updateIf(VMInstanceVO vm, VirtualMachine.Event event, Long hostId, String reservationId); /** * Find vm instance with names like. diff --git a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index bd70da428e7..5875a4420e1 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -154,7 +154,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem } @Override - public boolean updateIf(VMInstanceVO vm, VirtualMachine.Event event, Long hostId) { + public boolean updateIf(VMInstanceVO vm, VirtualMachine.Event event, Long hostId, String reservationId) { State oldState = vm.getState(); State newState = oldState.getNextState(event); @@ -173,10 +173,12 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("update", vm.getUpdated()); vm.incrUpdated(); + vm.setReservationId(reservationId); UpdateBuilder ub = getUpdateBuilder(vm); ub.set(vm, "state", newState); ub.set(vm, "hostId", hostId); ub.set(vm, _updateTimeAttr, new Date()); + int result = update(vm, sc); if (result == 0 && s_logger.isDebugEnabled()) { diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 0db72fcf6d5..8bd572757b2 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -98,11 +98,11 @@ DROP TABLE IF EXISTS `cloud`.`host_tags`; CREATE TABLE `cloud`.`op_it_work` ( `id` char(40) COMMENT 'id', `mgmt_server_id` bigint unsigned COMMENT 'management server id', - `created` timestamp NOT NULL COMMENT 'when was this work detail created', + `created_on` bigint unsigned NOT NULL COMMENT 'when was this work detail created', `thread` varchar(255) NOT NULL COMMENT 'thread name', `type` char(32) NOT NULL COMMENT 'type of work', `state` char(32) NOT NULL COMMENT 'state', - `cancel_taken` timestamp COMMENT 'time it was taken over', + `updated_on` bigint unsigned NOT NULL COMMENT 'time it was taken over', `instance_id` bigint unsigned NOT NULL COMMENT 'vm instance', `resource_type` char(32) COMMENT 'type of resource being worked on', `resource_id` bigint unsigned COMMENT 'resource id being worked on', @@ -746,7 +746,23 @@ CREATE TABLE `cloud`.`vm_instance` ( `domain_id` bigint unsigned NOT NULL, `service_offering_id` bigint unsigned NOT NULL COMMENT 'service offering id', `reservation_id` char(40) COMMENT 'reservation id', - PRIMARY KEY (`id`) + `hypervisor_type` char(32) COMMENT 'hypervisor type', + PRIMARY KEY (`id`), + INDEX `i_vm_instance__removed`(`removed`), + INDEX `i_vm_instance__type`(`type`), + INDEX `i_vm_instance__pod_id`(`pod_id`), + INDEX `i_vm_instance__update_time`(`update_time`), + INDEX `i_vm_instance__update_count`(`update_count`), + INDEX `i_vm_instance__state`(`state`), + INDEX `i_vm_instance__data_center_id`(`data_center_id`), + CONSTRAINT `fk_vm_instance__host_id` FOREIGN KEY `fk_vm_instance__host_id` (`host_id`) REFERENCES `host` (`id`), + CONSTRAINT `fk_vm_instance__last_host_id` FOREIGN KEY `fk_vm_instance__last_host_id` (`last_host_id`) REFERENCES `host`(`id`), + CONSTRAINT `fk_vm_instance__template_id` FOREIGN KEY `fk_vm_instance__template_id` (`vm_template_id`) REFERENCES `vm_template` (`id`), + INDEX `i_vm_instance__template_id`(`vm_template_id`), + CONSTRAINT `fk_vm_instance__account_id` FOREIGN KEY `fk_vm_instance__account_id` (`account_id`) REFERENCES `account` (`id`), + INDEX `i_vm_instance__account_id`(`account_id`), + CONSTRAINT `fk_vm_instance__service_offering_id` FOREIGN KEY `fk_vm_instance__service_offering_id` (`service_offering_id`) REFERENCES `service_offering` (`id`), + INDEX `i_vm_instance__service_offering_id`(`service_offering_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`user_vm` ( diff --git a/utils/src/com/cloud/utils/time/InaccurateClock.java b/utils/src/com/cloud/utils/time/InaccurateClock.java index 821b39fce31..3e61db3b847 100644 --- a/utils/src/com/cloud/utils/time/InaccurateClock.java +++ b/utils/src/com/cloud/utils/time/InaccurateClock.java @@ -36,8 +36,8 @@ public class InaccurateClock extends Thread { @Override public void run() { while (true) { - time = System.currentTimeMillis(); - try { + try { + time = System.currentTimeMillis(); Thread.sleep(1000); } catch(Exception e) { } @@ -51,4 +51,9 @@ public class InaccurateClock extends Thread { return System.currentTimeMillis(); } } + + public static long getTimeInSeconds() { + // This is obviously not accurate because it >> 10 is / 1024 but it's close enough since we're inaccurate. + return getTime() >> 10; + } } From 7f597e594c70d5b4a7122f207be52347cf21c6ea Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Tue, 11 Jan 2011 11:43:57 -0800 Subject: [PATCH 32/41] added work list to vm start --- .../com/cloud/deploy/DeploymentPlanner.java | 33 ++- .../src/com/cloud/vm/UserVmManagerImpl.java | 3 + .../cloud/vm/VirtualMachineManagerImpl.java | 202 +++++++++--------- setup/db/create-index-fk.sql | 18 -- 4 files changed, 137 insertions(+), 119 deletions(-) diff --git a/api/src/com/cloud/deploy/DeploymentPlanner.java b/api/src/com/cloud/deploy/DeploymentPlanner.java index 668b6952f6a..c010d5a7ff6 100644 --- a/api/src/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/com/cloud/deploy/DeploymentPlanner.java @@ -10,6 +10,7 @@ import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.Host; import com.cloud.org.Cluster; import com.cloud.storage.StoragePool; @@ -50,11 +51,11 @@ public interface DeploymentPlanner extends Adapter { Set _hostIds; Set _poolIds; - public void add(InsufficientCapacityException e) { + public boolean add(InsufficientCapacityException e) { Class scope = e.getScope(); if (scope == null) { - return; + return false; } if (Host.class.isAssignableFrom(scope)) { @@ -67,7 +68,35 @@ public interface DeploymentPlanner extends Adapter { addCluster(e.getId()); } else if (StoragePool.class.isAssignableFrom(scope)) { addPool(e.getId()); + } else { + return false; } + + return true; + } + + public boolean add(ResourceUnavailableException e) { + Class scope = e.getScope(); + + if (scope == null) { + return false; + } + + if (Host.class.isAssignableFrom(scope)) { + addHost(e.getResourceId()); + } else if (Pod.class.isAssignableFrom(scope)) { + addPod(e.getResourceId()); + } else if (DataCenter.class.isAssignableFrom(scope)) { + addDataCenter(e.getResourceId()); + } else if (Cluster.class.isAssignableFrom(scope)) { + addCluster(e.getResourceId()); + } else if (StoragePool.class.isAssignableFrom(scope)) { + addPool(e.getResourceId()); + } else { + return false; + } + + return true; } public void addPool(long poolId) { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index e11c465f853..17434421845 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -2427,6 +2427,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager UserVmVO vm = profile.getVirtualMachine(); _networkGroupMgr.handleVmStateTransition(vm, State.Running); _ovsNetworkMgr.handleVmStateTransition(vm, State.Running); + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_START, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getName(), vm.getServiceOfferingId(), vm.getTemplateId(), null); + _usageEventDao.persist(usageEvent); + return true; } diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index ab41c08edd4..d583832e272 100644 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -69,16 +69,17 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageManager; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; import com.cloud.storage.Volume.VolumeType; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; -import com.cloud.uservm.UserVm; import com.cloud.utils.Journal; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; @@ -413,7 +414,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { } } - assert 1 == 0 : "Why there is no Start Answer???"; + assert false : "Why there is no Start Answer???"; return null; } @@ -452,12 +453,12 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { } @DB - protected Pair changeToStartState(VirtualMachineGuru vmGuru, T vm, User caller, Account account) throws ConcurrentOperationException { + protected Ternary changeToStartState(VirtualMachineGuru vmGuru, T vm, User caller, Account account) throws ConcurrentOperationException { long vmId = vm.getId(); ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getId()); int retry = _lockStateRetry; - while (retry-- > 0) { + while (retry-- != 0) { Transaction txn = Transaction.currentTxn(); txn.start(); if (_vmDao.updateIf(vm, Event.StartRequested, null, work.getId())) { @@ -469,7 +470,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); } - return new Pair(vmGuru.findById(vmId), context); + return new Ternary(vmGuru.findById(vmId), context, work); } if (s_logger.isDebugEnabled()) { @@ -487,7 +488,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { if (s_logger.isDebugEnabled()) { s_logger.debug("VM is already started: " + vm); } - return new Pair(vmGuru.findById(vmId), null); + return null; } if (state.isTransitional()) { @@ -517,112 +518,115 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { VirtualMachineGuru vmGuru = getVmGuru(vm); - Pair start = changeToStartState(vmGuru, vm, caller, account); - assert (start != null) : "Should never happen"; + Ternary start = changeToStartState(vmGuru, vm, caller, account); + if (start == null) { + return vmGuru.findById(vmId); + } vm = start.first(); ReservationContext ctx = start.second(); + ItWorkVO work = start.third(); - if (ctx == null) { // No need to start because it's already running. - return vm; - } + T startedVm = null; - ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId()); - VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); - - DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodId(), null, null); - - HypervisorGuru hvGuru = _hvGurus.get(vm.getHypervisorType()); - - Journal journal = start.second().getJournal(); - - ExcludeList avoids = new ExcludeList(); - int retry = _retry; - DeployDestination dest = null; - while (retry-- != 0) { // It's != so that it can match -1. - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, null, params); - - for (DeploymentPlanner planner : _planners) { - dest = planner.plan(vmProfile, plan, avoids); - if (dest != null) { - avoids.addHost(dest.getHost().getId()); - journal.record("Deployment found ", vmProfile, dest); - break; + try { + ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId()); + VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); + + DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodId(), null, null); + + HypervisorGuru hvGuru = _hvGurus.get(vm.getHypervisorType()); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, null, params); + + Journal journal = start.second().getJournal(); + + ExcludeList avoids = new ExcludeList(); + int retry = _retry; + while (retry-- != 0) { // It's != so that it can match -1. + + DeployDestination dest = null; + for (DeploymentPlanner planner : _planners) { + dest = planner.plan(vmProfile, plan, avoids); + if (dest != null) { + avoids.addHost(dest.getHost().getId()); + journal.record("Deployment found ", vmProfile, dest); + break; + } } - } - - if (dest == null) { - if (retry != (_retry -1)) { - stateTransitTo(vm, Event.OperationFailed, null); - } - throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId()); - } - - if (retry == (_retry -1)) { - if (!stateTransitTo(vm, Event.StartRequested, dest.getHost().getId())) { - throw new ConcurrentOperationException("Unable to start vm " + vm + " due to concurrent operations"); - } - } else { + + if (dest == null) { + throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId()); + } + stateTransitTo(vm, Event.OperationRetry, dest.getHost().getId()); - } - - try { - _storageMgr.prepare(vmProfile, dest); - _networkMgr.prepare(vmProfile, dest, ctx); - } catch (ConcurrentOperationException e) { - stateTransitTo(vm, Event.OperationFailed, null); - throw e; - } catch (ResourceUnavailableException e) { - s_logger.warn("Unable to contact storage.", e); - avoids.addCluster(dest.getCluster().getId()); - continue; - } catch (InsufficientCapacityException e) { - s_logger.warn("Insufficient capacity ", e); - avoids.add(e); - continue; - } catch (RuntimeException e) { - s_logger.warn("Failed to start instance " + vm, e); - stateTransitTo(vm, Event.OperationFailed, null); - return null; - } - - vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); - - VirtualMachineTO vmTO = hvGuru.implement(vmProfile); - - Commands cmds = new Commands(OnError.Revert); - cmds.addCommand(new StartCommand(vmTO)); - - vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); - try { - Answer[] answers = _agentMgr.send(dest.getHost().getId(), cmds); - if (getStartAnswer(answers).getResult() && vmGuru.finalizeStart(cmds, vmProfile, dest, ctx)) { - if (!stateTransitTo(vm, Event.OperationSucceeded, dest.getHost().getId())) { - throw new CloudRuntimeException("Unable to transition to a new state."); + + try { + _storageMgr.prepare(vmProfile, dest); + _networkMgr.prepare(vmProfile, dest, ctx); + } catch (ResourceUnavailableException e) { + if (!avoids.add(e)) { + if (e.getScope() == Volume.class || e.getScope() == Nic.class) { + throw e; + } else { + throw new CloudRuntimeException("Resource is not available to start the VM.", e); + } } - if(vm instanceof UserVm){ - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_START, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getName(), vm.getServiceOfferingId(), vm.getTemplateId(), null); - _usageEventDao.persist(usageEvent); + s_logger.info("Unable to contact resource.", e); + continue; + } catch (InsufficientCapacityException e) { + if (!avoids.add(e)) { + if (e.getScope() == Volume.class || e.getScope() == Nic.class) { + throw e; + } else { + throw new CloudRuntimeException("Insufficient capacity to start the VM.", e); + } } - return vm; + s_logger.info("Insufficient capacity ", e); + continue; + } catch (RuntimeException e) { + s_logger.warn("Failed to start instance " + vm, e); + throw new CloudRuntimeException("Failed to start " + vm, e); + } + + vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); + + VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + + Commands cmds = new Commands(OnError.Revert); + cmds.addCommand(new StartCommand(vmTO)); + + vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); + try { + Answer[] answers = _agentMgr.send(dest.getHost().getId(), cmds); + if (getStartAnswer(answers).getResult() && vmGuru.finalizeStart(cmds, vmProfile, dest, ctx)) { + if (!stateTransitTo(vm, Event.OperationSucceeded, dest.getHost().getId())) { + throw new CloudRuntimeException("Unable to transition to a new state."); + } + startedVm = vm; + break; + } + s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + answers[0].getDetails()); + } catch (AgentUnavailableException e) { + s_logger.debug("Unable to send the start command to host " + dest.getHost()); + continue; + } catch (OperationTimedoutException e) { + s_logger.debug("Unable to send the start command to host " + dest.getHost()); + continue; } - s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + answers[0].getDetails()); - } catch (AgentUnavailableException e) { - s_logger.debug("Unable to send the start command to host " + dest.getHost()); - continue; - } catch (OperationTimedoutException e) { - s_logger.debug("Unable to send the start command to host " + dest.getHost()); - continue; } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creation complete for VM " + vm); + } + } finally { + if (startedVm == null) { + stateTransitTo(vm, Event.OperationFailed, null); + } + work.setStep(Step.Done); + _workDao.update(work.getId(), work); } - stateTransitTo(vm, Event.OperationFailed, null); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creation complete for VM " + vm); - } - - return null; + return startedVm; } @Override diff --git a/setup/db/create-index-fk.sql b/setup/db/create-index-fk.sql index 033ec9f2343..921618f025c 100755 --- a/setup/db/create-index-fk.sql +++ b/setup/db/create-index-fk.sql @@ -95,24 +95,6 @@ ALTER TABLE `cloud`.`cluster_details` ADD CONSTRAINT `fk_cluster_details__cluste ALTER TABLE `cloud`.`vm_template` ADD INDEX `i_vm_template__removed`(`removed`); ALTER TABLE `cloud`.`vm_template` ADD INDEX `i_vm_template__public`(`public`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__removed`(`removed`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__type`(`type`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__pod_id`(`pod_id`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__update_time`(`update_time`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__update_count`(`update_count`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__state`(`state`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__data_center_id`(`data_center_id`); -ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `fk_vm_instance__host_id` FOREIGN KEY `fk_vm_instance__host_id` (`host_id`) REFERENCES `host` (`id`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__host_id`(`host_id`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__last_host_id`(`last_host_id`); - -ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `fk_vm_instance__template_id` FOREIGN KEY `fk_vm_instance__template_id` (`vm_template_id`) REFERENCES `vm_template` (`id`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__template_id`(`vm_template_id`); -ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `fk_vm_instance__account_id` FOREIGN KEY `fk_vm_instance__account_id` (`account_id`) REFERENCES `account` (`id`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__account_id`(`account_id`); -ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `fk_vm_instance__service_offering_id` FOREIGN KEY `fk_vm_instance__service_offering_id` (`service_offering_id`) REFERENCES `service_offering` (`id`); -ALTER TABLE `cloud`.`vm_instance` ADD INDEX `i_vm_instance__service_offering_id`(`service_offering_id`); - ALTER TABLE `cloud`.`service_offering` ADD CONSTRAINT `fk_service_offering__id` FOREIGN KEY `fk_service_offering__id`(`id`) REFERENCES `disk_offering`(`id`) ON DELETE CASCADE; ALTER TABLE `cloud`.`user_vm` ADD CONSTRAINT `fk_user_vm__domain_router_id` FOREIGN KEY `fk_user_vm__domain_router_id` (`domain_router_id`) REFERENCES `domain_router` (`id`); From 6e6e8ff8763a21fc71a3d3b950a05c8643c55c0a Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Tue, 11 Jan 2011 16:44:26 -0800 Subject: [PATCH 33/41] better expunge and destroy of volumes --- .../cloud/api/commands/DeleteVolumeCmd.java | 3 +- api/src/com/cloud/storage/StorageService.java | 3 +- api/src/com/cloud/storage/Volume.java | 12 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 61 +---- .../network/ovs/OvsNetworkManagerImpl.java | 13 +- .../VirtualNetworkApplianceManagerImpl.java | 47 +--- .../src/com/cloud/storage/StorageManager.java | 12 +- .../com/cloud/storage/StorageManagerImpl.java | 240 ++++++++---------- .../src/com/cloud/storage/dao/VolumeDao.java | 6 +- .../com/cloud/storage/dao/VolumeDaoImpl.java | 206 ++++----------- .../SecondaryStorageManagerImpl.java | 67 +---- server/src/com/cloud/vm/ItWorkDaoImpl.java | 4 +- server/src/com/cloud/vm/ItWorkVO.java | 10 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 2 - setup/db/create-schema.sql | 8 +- 15 files changed, 202 insertions(+), 492 deletions(-) diff --git a/api/src/com/cloud/api/commands/DeleteVolumeCmd.java b/api/src/com/cloud/api/commands/DeleteVolumeCmd.java index c0919c7895c..bd23f442885 100644 --- a/api/src/com/cloud/api/commands/DeleteVolumeCmd.java +++ b/api/src/com/cloud/api/commands/DeleteVolumeCmd.java @@ -26,6 +26,7 @@ import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.SuccessResponse; +import com.cloud.exception.ConcurrentOperationException; @Implementation(description="Deletes a detached disk volume.", responseObject=SuccessResponse.class) public class DeleteVolumeCmd extends BaseCmd { @@ -63,7 +64,7 @@ public class DeleteVolumeCmd extends BaseCmd { } @Override - public void execute(){ + public void execute() throws ConcurrentOperationException { boolean result = _storageService.deleteVolume(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/com/cloud/storage/StorageService.java b/api/src/com/cloud/storage/StorageService.java index f8fd6d290dc..e51e7392a6d 100644 --- a/api/src/com/cloud/storage/StorageService.java +++ b/api/src/com/cloud/storage/StorageService.java @@ -27,6 +27,7 @@ import com.cloud.api.commands.DeletePoolCmd; import com.cloud.api.commands.DeleteVolumeCmd; import com.cloud.api.commands.PreparePrimaryStorageForMaintenanceCmd; import com.cloud.api.commands.UpdateStoragePoolCmd; +import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; @@ -60,7 +61,7 @@ public interface StorageService { */ Volume createVolume(CreateVolumeCmd cmd); - boolean deleteVolume(DeleteVolumeCmd cmd) throws InvalidParameterValueException; + boolean deleteVolume(DeleteVolumeCmd cmd) throws ConcurrentOperationException; /** * Delete the storage pool * @param cmd - the command specifying poolId diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index 6856fb135d0..2e650fb5179 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -38,8 +38,7 @@ public interface Volume extends ControlledEntity, BasedOn { Creating("The volume is being created. getPoolId() should reflect the pool where it is being created."), Ready("The volume is ready to be used."), Used("The volume is used"), - Destroy("The volume is set to be desctroyed but can be recovered."), - Destroyed("The volume is destroyed. Should be removed."); + Destroy("The volume is set to be destroyed but can be recovered."); String _description; @@ -75,17 +74,13 @@ public interface Volume extends ControlledEntity, BasedOn { private final static StateMachine s_fsm = new StateMachine(); static { s_fsm.addTransition(Allocated, Event.Create, Creating); - s_fsm.addTransition(Allocated, Event.Destroy, Destroyed); + s_fsm.addTransition(Allocated, Event.Destroy, Destroy); s_fsm.addTransition(Creating, Event.OperationRetry, Creating); s_fsm.addTransition(Creating, Event.OperationFailed, Allocated); s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready); s_fsm.addTransition(Creating, Event.Destroy, Destroy); s_fsm.addTransition(Ready, Event.Destroy, Destroy); s_fsm.addTransition(Ready, Event.Start, Used); - s_fsm.addTransition(Destroy, Event.OperationSucceeded, Destroyed); - s_fsm.addTransition(Destroy, Event.OperationFailed, Destroy); - s_fsm.addTransition(Destroy, Event.OperationRetry, Destroy); - s_fsm.addTransition(Destroy, Event.Recover, Ready); } } @@ -95,8 +90,7 @@ public interface Volume extends ControlledEntity, BasedOn { OperationFailed, OperationSucceeded, OperationRetry, - Destroy, - Recover; + Destroy; } enum SourceType { diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index a82f7229c54..57c2032bb19 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -1557,64 +1557,13 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx } @Override - @DB public boolean destroyProxy(long vmId) { - AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); - if (asyncExecutor != null) { - AsyncJobVO job = asyncExecutor.getJob(); - - if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy console proxy " + vmId + ", update async job-" + job.getId()); - } - _asyncMgr.updateAsyncJobAttachment(job.getId(), "console_proxy", vmId); - } - - ConsoleProxyVO vm = _consoleProxyDao.findById(vmId); - if (vm == null || vm.getState() == State.Destroyed) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find vm or vm is destroyed: " + vmId); - } - return true; - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Destroying console proxy vm " + vmId); - } - - if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, null)) { - s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vmId); - return false; - } - - Transaction txn = Transaction.currentTxn(); - List vols = null; + ConsoleProxyVO proxy = _consoleProxyDao.findById(vmId); try { - vols = _volsDao.findByInstance(vmId); - if (vols.size() != 0) { - _storageMgr.destroy(vm, vols); - } - - return true; - } finally { - try { - txn.start(); - // release critical system resources used by the VM before we - // delete them - if (vm.getPublicIpAddress() != null) { -// freePublicIpAddress(vm.getPublicIpAddress(), vm.getDataCenterId(), vm.getPodId()); - } - vm.setPublicIpAddress(null); - - _consoleProxyDao.remove(vm.getId()); - - txn.commit(); - } catch (Exception e) { - s_logger.error("Caught this error: ", e); - txn.rollback(); - return false; - } finally { - s_logger.debug("console proxy vm is destroyed : " + vm.getName()); - } + return _itMgr.expunge(proxy, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to expunge " + proxy, e); + return false; } } diff --git a/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java b/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java index 09638b16a4d..88466315989 100644 --- a/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java +++ b/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java @@ -44,10 +44,10 @@ import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.vm.DomainRouterVO; -import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; @@ -108,7 +108,7 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { public boolean configure(String name, Map params) throws ConfigurationException { _name = name; - _isEnabled = _configDao.getValue(Config.OvsNetwork.key()).equalsIgnoreCase("true") ? true : false; + _isEnabled = Boolean.parseBoolean(_configDao.getValue(Config.OvsNetwork.key())); _serverId = ((ManagementServer)ComponentLocator.getComponent(ManagementServer.Name)).getId(); _executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS")); _cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup")); @@ -470,8 +470,9 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { return; } - if (delayMs == null) - delayMs = new Long(100l); + if (delayMs == null) { + delayMs = new Long(100l); + } for (Long vmId: affectedVms) { Transaction txn = Transaction.currentTxn(); @@ -613,14 +614,14 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { @Override public void UserVmCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest) throws GreTunnelException { - CheckAndCreateTunnel((VMInstanceVO)profile.getVirtualMachine(), dest); + CheckAndCreateTunnel(profile.getVirtualMachine(), dest); } @Override public void RouterCheckAndCreateTunnel(Commands cmds, VirtualMachineProfile profile, DeployDestination dest) throws GreTunnelException { - CheckAndCreateTunnel((VMInstanceVO)profile.getVirtualMachine(), dest); + CheckAndCreateTunnel(profile.getVirtualMachine(), dest); } @Override diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index d811faeb1f2..dd2248e2403 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -348,51 +348,12 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (s_logger.isDebugEnabled()) { s_logger.debug("Attempting to destroy router " + routerId); } - - DomainRouterVO router = _routerDao.acquireInLockTable(routerId); - + + DomainRouterVO router = _routerDao.findById(routerId); if (router == null) { - s_logger.debug("Unable to acquire lock on router " + routerId); - return false; + return true; } - - try { - if (router.getState() == State.Destroyed || router.getState() == State.Expunging || router.getRemoved() != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find router or router is destroyed: " + routerId); - } - return true; - } - - if (stopRouterInternal(router.getId())) { - return false; - } - - router = _routerDao.findById(routerId); - if (!_itMgr.stateTransitTo(router, VirtualMachine.Event.DestroyRequested, router.getHostId())) { - s_logger.debug("VM " + router.toString() + " is not in a state to be destroyed."); - return false; - } - } finally { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Release lock on router " + routerId + " for stop"); - } - _routerDao.releaseFromLockTable(routerId); - } - - router.setPublicIpAddress(null); - router.setVlanDbId(null); - _routerDao.update(router.getId(), router); - _routerDao.remove(router.getId()); - - List vols = _volsDao.findByInstance(routerId); - _storageMgr.destroy(router, vols); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Successfully destroyed router: " + routerId); - } - - return true; + return _itMgr.expunge(router, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); } @Override diff --git a/server/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java index 336750c1556..a269546790d 100755 --- a/server/src/com/cloud/storage/StorageManager.java +++ b/server/src/com/cloud/storage/StorageManager.java @@ -81,14 +81,6 @@ public interface StorageManager extends Manager { */ List unshare(VMInstanceVO vm, HostVO host); - /** - * destroy the storage volumes of a certain vm. - * - * @param vm vm to destroy. - * @param vols volumes to remove from storage pool - */ - void destroy(VMInstanceVO vm, List vols); - /** * Creates volumes for a particular VM. * @param account account to create volumes for. @@ -205,7 +197,7 @@ public interface StorageManager extends Manager { * Marks the specified volume as destroyed in the management server database. The expunge thread will delete the volume from its storage pool. * @param volume */ - void destroyVolume(VolumeVO volume); + void destroyVolume(VolumeVO volume) throws ConcurrentOperationException; /** Create capacity entries in the op capacity table * @param storagePool @@ -282,5 +274,5 @@ public interface StorageManager extends Manager { void release(VirtualMachineProfile profile); - void cleanupVolumes(Long vmId); + void cleanupVolumes(long vmId) throws ConcurrentOperationException; } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 97992cd6ac0..1d6a35a38ed 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -114,7 +114,6 @@ import com.cloud.host.dao.DetailsDao; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.NetworkManager; -import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.offering.ServiceOffering; import com.cloud.service.ServiceOfferingVO; @@ -141,7 +140,6 @@ import com.cloud.storage.snapshot.SnapshotScheduler; import com.cloud.template.TemplateManager; import com.cloud.user.Account; import com.cloud.user.AccountManager; -import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; @@ -874,7 +872,11 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag s_logger.debug(e.getMessage()); } if (rootCreated != null) { - destroyVolume(rootCreated); + try { + destroyVolume(rootCreated); + } catch (Exception e1) { + s_logger.warn("Unable to mark a volume as destroyed: " + rootCreated, e1); + } } throw new CloudRuntimeException("Unable to create volumes for " + vm, e); } @@ -953,55 +955,6 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag return true; } - @Override - public void destroy(VMInstanceVO vm, List vols) { - if (s_logger.isDebugEnabled() && vm != null) { - s_logger.debug("Destroying volumes of " + vm.toString()); - } - - for (VolumeVO vol : vols) { - _volsDao.detachVolume(vol.getId()); - _volsDao.destroyVolume(vol.getId()); - - // First delete the entries in the snapshot_policy and - // snapshot_schedule table for the volume. - // They should not get executed after the volume is destroyed. - _snapshotMgr.deletePoliciesForVolume(vol.getId()); - - String volumePath = vol.getPath(); - Long poolId = vol.getPoolId(); - if (poolId != null && volumePath != null && !volumePath.trim().isEmpty()) { - Answer answer = null; - StoragePoolVO pool = _storagePoolDao.findById(poolId); - String vmName = null; - if (vm != null) { - vmName = vm.getInstanceName(); - } - final DestroyCommand cmd = new DestroyCommand(pool, vol, vmName); - boolean removed = false; - List poolhosts = _storagePoolHostDao.listByPoolId(poolId); - for (StoragePoolHostVO poolhost : poolhosts) { - answer = _agentMgr.easySend(poolhost.getHostId(), cmd); - if (answer != null && answer.getResult()) { - removed = true; - break; - } - } - - if (removed) { - _volsDao.remove(vol.getId()); - } else { - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_STORAGE_MISC, vol.getDataCenterId(), vol.getPodId(), - "Storage cleanup required for storage pool: " + pool.getName(), "Volume folder: " + vol.getFolder() + ", Volume Path: " + vol.getPath() + ", Volume id: " +vol.getId()+ ", Volume Name: " +vol.getName()+ ", Storage PoolId: " +vol.getPoolId()); - s_logger.warn("destroy volume " + vol.getFolder() + " : " + vol.getPath() + " failed for Volume id : " +vol.getId()+ " Volume Name: " +vol.getName()+ " Storage PoolId : " +vol.getPoolId()); - } - } else { - _volsDao.remove(vol.getId()); - } - } - - } - @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; @@ -1597,13 +1550,13 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag String destPrimaryStorageVolumePath = cvAnswer.getVolumePath(); String destPrimaryStorageVolumeFolder = cvAnswer.getVolumeFolder(); - // Delete the volume on the source storage pool - final DestroyCommand cmd = new DestroyCommand(srcPool, volume, null); - Answer destroyAnswer = _agentMgr.easySend(sourceHostId, cmd); - - if (destroyAnswer == null || !destroyAnswer.getResult()) { - throw new CloudRuntimeException("Failed to delete the volume from the source primary storage pool."); + try { + destroyVolume(volume); + } catch (ConcurrentOperationException e) { + s_logger.warn("Concurrent Operation", e); } + + expungeVolume(volume); volume.setPath(destPrimaryStorageVolumePath); volume.setFolder(destPrimaryStorageVolumeFolder); @@ -1849,16 +1802,14 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag @Override @DB - public void destroyVolume(VolumeVO volume) { + public void destroyVolume(VolumeVO volume) throws ConcurrentOperationException { Transaction txn = Transaction.currentTxn(); txn.start(); - - Long volumeId = volume.getId(); - _volsDao.destroyVolume(volumeId); + + _volsDao.update(volume, Volume.Event.Destroy); + long volumeId = volume.getId(); - EventUtils.saveEvent(User.UID_SYSTEM, volume.getAccountId(), EventTypes.EVENT_VOLUME_DELETE, "Volume " +volume.getName()+ " deleted"); - - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), null, null , null); + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volumeId, volume.getName(), null, null , null); _usageEventDao.persist(usageEvent); // Delete the recurring snapshot policies for this volume. @@ -1994,24 +1945,6 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag return answer; } - protected class StorageGarbageCollector implements Runnable { - - public StorageGarbageCollector() { - } - - @Override - public void run() { - try { - s_logger.info("Storage Garbage Collection Thread is running."); - - cleanupStorage(true); - - } catch (Exception e) { - s_logger.error("Caught the following Exception", e); - } - } - } - @Override public void cleanupStorage(boolean recurring) { GlobalLock scanLock = GlobalLock.getInternLock(this.getClass().getName()); @@ -2083,19 +2016,10 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag } } - List vols = _volsDao.listRemovedButNotDestroyed(); + List vols = _volsDao.listVolumesToBeDestroyed(); for (VolumeVO vol : vols) { try { - Long poolId = vol.getPoolId(); - Answer answer = null; - StoragePoolVO pool = _storagePoolDao.findById(poolId); - final DestroyCommand cmd = new DestroyCommand(pool, vol, null); - answer = sendToPool(pool, cmd); - if (answer != null && answer.getResult()) { - s_logger.debug("Destroyed " + vol); - vol.setDestroyed(true); - _volsDao.update(vol.getId(), vol); - } + expungeVolume(vol); } catch (Exception e) { s_logger.warn("Unable to destroy " + vol.getId(), e); } @@ -2470,7 +2394,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag } @Override - public boolean deleteVolume(DeleteVolumeCmd cmd) throws InvalidParameterValueException { + public boolean deleteVolume(DeleteVolumeCmd cmd) throws ConcurrentOperationException { Account account = UserContext.current().getCaller(); Long volumeId = cmd.getId(); @@ -2513,20 +2437,13 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag } // Check that the volume is not already destroyed - if (volume.getDestroyed()) { - throw new InvalidParameterValueException("Please specify a volume that is not already destroyed."); + if (volume.getState() != Volume.State.Destroy) { + destroyVolume(volume); } - try { - // Destroy the volume - destroyVolume(volume); - } catch (Exception e) { - s_logger.warn("Error destroying volume:"+e); - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Error destroying volume:"+e); - } + expungeVolume(volume); return true; - } private boolean validateVolumeSizeRange(long size) throws InvalidParameterValueException { @@ -2800,41 +2717,90 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag //add code here } - @Override - public void cleanupVolumes(Long vmId) { - VMInstanceVO vm = _vmInstanceDao.findById(vmId); - List volumesForVm = _volsDao.findByInstance(vmId); - for(VolumeVO vol : volumesForVm){ - if(vol.getVolumeType().equals(VolumeType.ROOT)){ - if(vol.getState() != Volume.State.Destroyed) { - assert(vol.getState() == Volume.State.Destroy); - String volumePath = vol.getPath(); - Long poolId = vol.getPoolId(); - if (poolId != null && volumePath != null && !volumePath.trim().isEmpty()) { - Answer answer = null; - StoragePoolVO pool = _storagePoolDao.findById(poolId); - - final DestroyCommand cmd = new DestroyCommand(pool, vol, vm.getName()); - List poolhosts = _storagePoolHostDao.listByPoolId(poolId); - for (StoragePoolHostVO poolhost : poolhosts) { - answer = _agentMgr.easySend(poolhost.getHostId(), cmd); - if (answer != null && answer.getResult()) { - try { - _volsDao.update(vol, Volume.Event.OperationSucceeded); - } catch (ConcurrentOperationException e) { - s_logger.warn("Unable to update volume state. vm: " + vmId + ", vol: " + vol.getId() + " due to ConcurrentOperationException"); - } - break; - } - } - } - } - destroyVolume(vol); + public void expungeVolume(VolumeVO vol) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Expunging " + vol); + } + String vmName = null; + if (vol.getVolumeType() == VolumeType.ROOT && vol.getInstanceId() != null) { + VirtualMachine vm = _vmInstanceDao.findById(vol.getInstanceId()); + vmName = vm.getInstanceName(); + } + + String volumePath = vol.getPath(); + Long poolId = vol.getPoolId(); + if (poolId == null || volumePath == null || volumePath.trim().isEmpty()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Marking volume that was never created as destroyed: " + vol); + } + _volsDao.remove(vol.getId()); + return; + } + + StoragePoolVO pool = _storagePoolDao.findById(poolId); + if (pool == null) { + s_logger.debug("Removing volume as storage pool is gone: " + poolId); + _volsDao.remove(vol.getId()); + return; + } + + DestroyCommand cmd = new DestroyCommand(pool, vol, vmName); + Answer answer = this.sendToPool(pool, cmd); + + if (answer != null && answer.getResult()) { + _volsDao.remove(vol.getId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Volume successfully expunged from " + poolId); + } + } else { + s_logger.info("Will retry delete of " + vol + " from " + poolId); + } + } + + @Override @DB + public void cleanupVolumes(long vmId) throws ConcurrentOperationException { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cleaning storage for vm: " + vmId); + } + List volumesForVm = _volsDao.findByInstance(vmId); + List toBeExpunged = new ArrayList(); + Transaction txn = Transaction.currentTxn(); + txn.start(); + for (VolumeVO vol : volumesForVm) { + if (vol.getVolumeType().equals(VolumeType.ROOT)) { + destroyVolume(vol); + toBeExpunged.add(vol); } else { - //data volume - _volsDao.detachVolume(vol.getId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Detaching " + vol); + } + _volsDao.detachVolume(vol.getId()); } } + txn.commit(); + + for (VolumeVO expunge : toBeExpunged) { + expungeVolume(expunge); + } } + + protected class StorageGarbageCollector implements Runnable { + + public StorageGarbageCollector() { + } + + @Override + public void run() { + try { + s_logger.trace("Storage Garbage Collection Thread is running."); + + cleanupStorage(true); + + } catch (Exception e) { + s_logger.error("Caught the following Exception", e); + } + } + } + } diff --git a/server/src/com/cloud/storage/dao/VolumeDao.java b/server/src/com/cloud/storage/dao/VolumeDao.java index cfd3c9ada0c..e35b5e041ff 100755 --- a/server/src/com/cloud/storage/dao/VolumeDao.java +++ b/server/src/com/cloud/storage/dao/VolumeDao.java @@ -36,14 +36,10 @@ public interface VolumeDao extends GenericDao { List findByDetachedDestroyed(); List findByAccountAndPod(long accountId, long podId); List findByTemplateAndZone(long templateId, long zoneId); - List findVMInstancesByStorageHost(long hostId, Volume.MirrorState mState); - List findStrandedMirrorVolumes(); List findVmsStoredOnHost(long hostId); void deleteVolumesByInstance(long instanceId); void attachVolume(long volumeId, long vmId, long deviceId); void detachVolume(long volumeId); - void destroyVolume(long volumeId); - void recoverVolume(long volumeId); boolean isAnyVolumeActivelyUsingTemplateOnPool(long templateId, long poolId); List listRemovedButNotDestroyed(); List findCreatedByInstance(long id); @@ -59,4 +55,6 @@ public interface VolumeDao extends GenericDao { */ boolean update(VolumeVO vol, Volume.Event event) throws ConcurrentOperationException; HypervisorType getHypervisorType(long volumeId); + + List listVolumesToBeDestroyed(); } diff --git a/server/src/com/cloud/storage/dao/VolumeDaoImpl.java b/server/src/com/cloud/storage/dao/VolumeDaoImpl.java index 453d96a71bc..ecdb37b9bb0 100755 --- a/server/src/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VolumeDaoImpl.java @@ -32,8 +32,6 @@ import com.cloud.async.AsyncInstanceCreateStatus; import com.cloud.exception.ConcurrentOperationException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Volume; -import com.cloud.storage.Volume.Event; -import com.cloud.storage.Volume.MirrorState; import com.cloud.storage.Volume.VolumeType; import com.cloud.storage.VolumeVO; import com.cloud.utils.Pair; @@ -53,22 +51,14 @@ import com.cloud.utils.exception.CloudRuntimeException; public class VolumeDaoImpl extends GenericDaoBase implements VolumeDao { private static final Logger s_logger = Logger.getLogger(VolumeDaoImpl.class); protected final SearchBuilder DetachedAccountIdSearch; - protected final SearchBuilder AccountIdSearch; - protected final SearchBuilder AccountPodSearch; protected final SearchBuilder TemplateZoneSearch; protected final GenericSearchBuilder TotalSizeByPoolSearch; - protected final SearchBuilder InstanceIdSearch; - protected final SearchBuilder InstanceAndTypeSearch; - protected final SearchBuilder InstanceIdDestroyedSearch; - protected final SearchBuilder InstanceIdCreatedSearch; protected final SearchBuilder DetachedDestroyedSearch; - protected final SearchBuilder MirrorSearch; protected final GenericSearchBuilder ActiveTemplateSearch; protected final SearchBuilder RemovedButNotDestroyedSearch; - protected final SearchBuilder PoolIdSearch; - protected final SearchBuilder InstanceAndDeviceIdSearch; protected final SearchBuilder InstanceStatesSearch; - protected final SearchBuilder IdStateSearch; + + protected final SearchBuilder AllFieldsSearch; protected final Attribute _stateAttr; @@ -116,7 +106,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public List findByAccount(long accountId) { - SearchCriteria sc = AccountIdSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("destroyed", false); return listBy(sc); @@ -124,14 +114,14 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public List findByInstance(long id) { - SearchCriteria sc = InstanceIdSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); return listBy(sc); } @Override public List findByInstanceAndDeviceId(long instanceId, long deviceId){ - SearchCriteria sc = InstanceAndDeviceIdSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", instanceId); sc.setParameters("deviceId", deviceId); return listBy(sc); @@ -139,14 +129,14 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public List findByPoolId(long poolId) { - SearchCriteria sc = PoolIdSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("poolId", poolId); return listBy(sc); } @Override public List findCreatedByInstance(long id) { - SearchCriteria sc = InstanceIdCreatedSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); sc.setParameters("status", AsyncInstanceCreateStatus.Created); sc.setParameters("destroyed", false); @@ -164,7 +154,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public List findByInstanceAndType(long id, VolumeType vType) { - SearchCriteria sc = InstanceAndTypeSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); sc.setParameters("vType", vType.toString()); return listBy(sc); @@ -172,7 +162,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public List findByInstanceIdDestroyed(long vmId) { - SearchCriteria sc = InstanceIdDestroyedSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", vmId); sc.setParameters("destroyed", true); return listIncludingRemovedBy(sc); @@ -187,8 +177,8 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public List findByAccountAndPod(long accountId, long podId) { - SearchCriteria sc = AccountPodSearch.create(); - sc.setParameters("account", accountId); + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("accountId", accountId); sc.setParameters("pod", podId); sc.setParameters("destroyed", false); sc.setParameters("status", AsyncInstanceCreateStatus.Created); @@ -205,38 +195,6 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol return listIncludingRemovedBy(sc); } - @Override @DB - public List findVMInstancesByStorageHost(long hostId, Volume.MirrorState mirrState) { - - Transaction txn = Transaction.currentTxn(); - PreparedStatement pstmt = null; - List result = new ArrayList(); - - try { - String sql = SELECT_VM_SQL; - pstmt = txn.prepareAutoCloseStatement(sql); - pstmt.setLong(1, hostId); - pstmt.setString(2, mirrState.toString()); - ResultSet rs = pstmt.executeQuery(); - while (rs.next()) { - result.add(rs.getLong(1)); - } - return result; - } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + SELECT_VM_SQL, e); - } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + SELECT_VM_SQL, e); - } - } - - @Override - public List findStrandedMirrorVolumes() { - SearchCriteria sc = MirrorSearch.create(); - sc.setParameters("mirrorState", MirrorState.ACTIVE.toString()); - - return listIncludingRemovedBy(sc); - } - @Override public boolean isAnyVolumeActivelyUsingTemplateOnPool(long templateId, long poolId) { SearchCriteria sc = ActiveTemplateSearch.create(); @@ -251,7 +209,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol @Override public void deleteVolumesByInstance(long instanceId) { - SearchCriteria sc = InstanceIdSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", instanceId); expunge(sc); } @@ -276,34 +234,6 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol update(volumeId, volume); } - @Override - public void destroyVolume(long volumeId) { - VolumeVO volume = createForUpdate(volumeId); - volume.setDestroyed(true); - - Volume.State oldState = volume.getState(); - Volume.State newState = oldState.getNextState(Event.Destroy); - - assert newState != null : "Event "+ Event.Destroy + " cannot happen from " + oldState; - volume.setState(newState); - - update(volumeId, volume); - } - - @Override - public void recoverVolume(long volumeId) { - VolumeVO volume = createForUpdate(volumeId); - volume.setDestroyed(false); - - Volume.State oldState = volume.getState(); - Volume.State newState = oldState.getNextState(Event.Recover); - - assert newState != null : "Event "+ Event.Recover + " cannot happen from " + oldState; - volume.setState(newState); - - update(volumeId, volume); - } - @Override public boolean update(VolumeVO vol, Volume.Event event) throws ConcurrentOperationException { Volume.State oldState = vol.getState(); @@ -314,7 +244,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol UpdateBuilder builder = getUpdateBuilder(vol); builder.set(vol, _stateAttr, newState); - SearchCriteria sc = IdStateSearch.create(); + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("id", vol.getId()); sc.setParameters("state", oldState); @@ -325,6 +255,8 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol } return rows == 1; } + + @Override @DB public HypervisorType getHypervisorType(long volumeId) { /*lookup from cluster of pool*/ @@ -336,8 +268,9 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol pstmt = txn.prepareAutoCloseStatement(sql); pstmt.setLong(1, volumeId); ResultSet rs = pstmt.executeQuery(); - if (rs.next()) - return HypervisorType.getType(rs.getString(1)); + if (rs.next()) { + return HypervisorType.getType(rs.getString(1)); + } return HypervisorType.None; } catch (SQLException e) { throw new CloudRuntimeException("DB Exception on: " + SELECT_HYPERTYPE_FROM_VOLUME, e); @@ -347,98 +280,59 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol } protected VolumeDaoImpl() { - AccountIdSearch = createSearchBuilder(); - AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); - AccountIdSearch.and("destroyed", AccountIdSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); - AccountIdSearch.done(); + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); + AllFieldsSearch.and("destroyed", AllFieldsSearch.entity().getDestroyed(), Op.EQ); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ); + AllFieldsSearch.and("pod", AllFieldsSearch.entity().getPodId(), Op.EQ); + AllFieldsSearch.and("status", AllFieldsSearch.entity().getStatus(), Op.EQ); + AllFieldsSearch.and("instanceId", AllFieldsSearch.entity().getInstanceId(), Op.EQ); + AllFieldsSearch.and("deviceId", AllFieldsSearch.entity().getDeviceId(), Op.EQ); + AllFieldsSearch.and("poolId", AllFieldsSearch.entity().getPoolId(), Op.EQ); + AllFieldsSearch.and("vType", AllFieldsSearch.entity().getVolumeType(), Op.EQ); + AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ); + AllFieldsSearch.done(); DetachedAccountIdSearch = createSearchBuilder(); - DetachedAccountIdSearch.and("accountId", DetachedAccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); - DetachedAccountIdSearch.and("destroyed", DetachedAccountIdSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); - DetachedAccountIdSearch.and("instanceId", DetachedAccountIdSearch.entity().getInstanceId(), SearchCriteria.Op.NULL); + DetachedAccountIdSearch.and("accountId", DetachedAccountIdSearch.entity().getAccountId(), Op.EQ); + DetachedAccountIdSearch.and("destroyed", DetachedAccountIdSearch.entity().getDestroyed(), Op.EQ); + DetachedAccountIdSearch.and("instanceId", DetachedAccountIdSearch.entity().getInstanceId(), Op.NULL); DetachedAccountIdSearch.done(); - AccountPodSearch = createSearchBuilder(); - AccountPodSearch.and("account", AccountPodSearch.entity().getAccountId(), SearchCriteria.Op.EQ); - AccountPodSearch.and("pod", AccountPodSearch.entity().getPodId(), SearchCriteria.Op.EQ); - AccountPodSearch.and("destroyed", AccountPodSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); - AccountPodSearch.and("status", AccountPodSearch.entity().getStatus(), SearchCriteria.Op.EQ); - AccountPodSearch.done(); - TemplateZoneSearch = createSearchBuilder(); - TemplateZoneSearch.and("template", TemplateZoneSearch.entity().getTemplateId(), SearchCriteria.Op.EQ); - TemplateZoneSearch.and("zone", TemplateZoneSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TemplateZoneSearch.and("template", TemplateZoneSearch.entity().getTemplateId(), Op.EQ); + TemplateZoneSearch.and("zone", TemplateZoneSearch.entity().getDataCenterId(), Op.EQ); TemplateZoneSearch.done(); TotalSizeByPoolSearch = createSearchBuilder(SumCount.class); TotalSizeByPoolSearch.select("sum", Func.SUM, TotalSizeByPoolSearch.entity().getSize()); TotalSizeByPoolSearch.select("count", Func.COUNT, (Object[])null); - TotalSizeByPoolSearch.and("poolId", TotalSizeByPoolSearch.entity().getPoolId(), SearchCriteria.Op.EQ); - TotalSizeByPoolSearch.and("removed", TotalSizeByPoolSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + TotalSizeByPoolSearch.and("poolId", TotalSizeByPoolSearch.entity().getPoolId(), Op.EQ); + TotalSizeByPoolSearch.and("removed", TotalSizeByPoolSearch.entity().getRemoved(), Op.NULL); TotalSizeByPoolSearch.done(); - - InstanceIdCreatedSearch = createSearchBuilder(); - InstanceIdCreatedSearch.and("instanceId", InstanceIdCreatedSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - InstanceIdCreatedSearch.and("status", InstanceIdCreatedSearch.entity().getStatus(), SearchCriteria.Op.EQ); - InstanceIdCreatedSearch.and("destroyed", InstanceIdCreatedSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); - InstanceIdCreatedSearch.done(); - - InstanceIdSearch = createSearchBuilder(); - InstanceIdSearch.and("instanceId", InstanceIdSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - InstanceIdSearch.done(); - - InstanceAndDeviceIdSearch = createSearchBuilder(); - InstanceAndDeviceIdSearch.and("instanceId", InstanceAndDeviceIdSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - InstanceAndDeviceIdSearch.and("deviceId", InstanceAndDeviceIdSearch.entity().getDeviceId(), SearchCriteria.Op.EQ); - InstanceAndDeviceIdSearch.done(); - - PoolIdSearch = createSearchBuilder(); - PoolIdSearch.and("poolId", PoolIdSearch.entity().getPoolId(), SearchCriteria.Op.EQ); - PoolIdSearch.done(); - - InstanceAndTypeSearch= createSearchBuilder(); - InstanceAndTypeSearch.and("instanceId", InstanceAndTypeSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - InstanceAndTypeSearch.and("vType", InstanceAndTypeSearch.entity().getVolumeType(), SearchCriteria.Op.EQ); - InstanceAndTypeSearch.done(); - - InstanceIdDestroyedSearch = createSearchBuilder(); - InstanceIdDestroyedSearch.and("instanceId", InstanceIdDestroyedSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - InstanceIdDestroyedSearch.and("destroyed", InstanceIdDestroyedSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); - InstanceIdDestroyedSearch.done(); - DetachedDestroyedSearch = createSearchBuilder(); - DetachedDestroyedSearch.and("instanceId", DetachedDestroyedSearch.entity().getInstanceId(), SearchCriteria.Op.NULL); - DetachedDestroyedSearch.and("destroyed", DetachedDestroyedSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); + DetachedDestroyedSearch.and("instanceId", DetachedDestroyedSearch.entity().getInstanceId(), Op.NULL); + DetachedDestroyedSearch.and("destroyed", DetachedDestroyedSearch.entity().getDestroyed(), Op.EQ); DetachedDestroyedSearch.done(); - MirrorSearch = createSearchBuilder(); - MirrorSearch.and("mirrorVolume", MirrorSearch.entity().getMirrorVolume(), Op.NULL); - MirrorSearch.and("mirrorState", MirrorSearch.entity().getMirrorState(), Op.EQ); - MirrorSearch.done(); - ActiveTemplateSearch = createSearchBuilder(Long.class); - ActiveTemplateSearch.and("pool", ActiveTemplateSearch.entity().getPoolId(), SearchCriteria.Op.EQ); - ActiveTemplateSearch.and("template", ActiveTemplateSearch.entity().getTemplateId(), SearchCriteria.Op.EQ); - ActiveTemplateSearch.and("removed", ActiveTemplateSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + ActiveTemplateSearch.and("pool", ActiveTemplateSearch.entity().getPoolId(), Op.EQ); + ActiveTemplateSearch.and("template", ActiveTemplateSearch.entity().getTemplateId(), Op.EQ); + ActiveTemplateSearch.and("removed", ActiveTemplateSearch.entity().getRemoved(), Op.NULL); ActiveTemplateSearch.select(null, Func.COUNT, null); ActiveTemplateSearch.done(); RemovedButNotDestroyedSearch = createSearchBuilder(); - RemovedButNotDestroyedSearch.and("destroyed", RemovedButNotDestroyedSearch.entity().getDestroyed(), SearchCriteria.Op.EQ); - RemovedButNotDestroyedSearch.and("removed", RemovedButNotDestroyedSearch.entity().getRemoved(), SearchCriteria.Op.NNULL); + RemovedButNotDestroyedSearch.and("destroyed", RemovedButNotDestroyedSearch.entity().getDestroyed(), Op.EQ); + RemovedButNotDestroyedSearch.and("removed", RemovedButNotDestroyedSearch.entity().getRemoved(), Op.NNULL); RemovedButNotDestroyedSearch.done(); InstanceStatesSearch = createSearchBuilder(); - InstanceStatesSearch.and("instance", InstanceStatesSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - InstanceStatesSearch.and("states", InstanceStatesSearch.entity().getState(), SearchCriteria.Op.IN); + InstanceStatesSearch.and("instance", InstanceStatesSearch.entity().getInstanceId(), Op.EQ); + InstanceStatesSearch.and("states", InstanceStatesSearch.entity().getState(), Op.IN); InstanceStatesSearch.done(); - IdStateSearch = createSearchBuilder(); - IdStateSearch.and("id", IdStateSearch.entity().getId(), SearchCriteria.Op.EQ); - IdStateSearch.and("state", IdStateSearch.entity().getState(), SearchCriteria.Op.EQ); - IdStateSearch.done(); - _stateAttr = _allAttributes.get("state"); assert _stateAttr != null : "Couldn't get the state attribute"; } @@ -458,4 +352,12 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol public SumCount() { } } + + @Override + public List listVolumesToBeDestroyed() { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("state", Volume.State.Destroy); + + return listBy(sc); + } } diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index 1410de77c9d..258890bc2a3 100644 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -1109,68 +1109,15 @@ public class SecondaryStorageManagerImpl implements SecondaryStorageVmManager, V } @Override - @DB public boolean destroySecStorageVm(long vmId) { - AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); - if (asyncExecutor != null) { - AsyncJobVO job = asyncExecutor.getJob(); - - if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy secondary storage vm " + vmId + ", update async job-" + job.getId()); - } - _asyncMgr.updateAsyncJobAttachment(job.getId(), "secstorage_vm", vmId); + SecondaryStorageVmVO ssvm = _secStorageVmDao.findById(vmId); + + try { + return _itMgr.expunge(ssvm, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to expunge " + ssvm, e); + return false; } - - SecondaryStorageVmVO vm = _secStorageVmDao.findById(vmId); - if (vm == null || vm.getState() == State.Destroyed) { - String msg = "Unable to find vm or vm is destroyed: " + vmId; - if (s_logger.isDebugEnabled()) { - s_logger.debug(msg); - } - return true; - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Destroying secondary storage vm vm " + vmId); - } - - if (! _itMgr.stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, null)) { - String msg = "Unable to destroy the vm because it is not in the correct state: " + vmId; - s_logger.debug(msg); - return false; - } - - Transaction txn = Transaction.currentTxn(); - List vols = null; - try { - vols = _volsDao.findByInstance(vmId); - if (vols.size() != 0) { - _storageMgr.destroy(vm, vols); - } - - return true; - } finally { - try { - txn.start(); - // release critical system resources used by the VM before we - // delete them - if (vm.getPublicIpAddress() != null) { - freePublicIpAddress(vm.getPublicIpAddress(), vm.getDataCenterId(), vm.getPodId()); - } - vm.setPublicIpAddress(null); - - _secStorageVmDao.remove(vm.getId()); - - txn.commit(); - } catch (Exception e) { - s_logger.error("Caught this error: ", e); - txn.rollback(); - return false; - } finally { - s_logger.debug("secondary storage vm vm is destroyed : " - + vm.getName()); - } - } } @DB diff --git a/server/src/com/cloud/vm/ItWorkDaoImpl.java b/server/src/com/cloud/vm/ItWorkDaoImpl.java index b7df1275f5b..b7159fa417f 100644 --- a/server/src/com/cloud/vm/ItWorkDaoImpl.java +++ b/server/src/com/cloud/vm/ItWorkDaoImpl.java @@ -37,12 +37,12 @@ public class ItWorkDaoImpl extends GenericDaoBase implements I AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("instance", AllFieldsSearch.entity().getInstanceId(), Op.EQ); - AllFieldsSearch.and("op", AllFieldsSearch.entity().getState(), Op.EQ); + AllFieldsSearch.and("op", AllFieldsSearch.entity().getType(), Op.EQ); AllFieldsSearch.and("step", AllFieldsSearch.entity().getStep(), Op.EQ); AllFieldsSearch.done(); CleanupSearch = createSearchBuilder(); - CleanupSearch.and("step", CleanupSearch.entity().getState(), Op.IN); + CleanupSearch.and("step", CleanupSearch.entity().getType(), Op.IN); CleanupSearch.and("time", CleanupSearch.entity().getUpdatedAt(), Op.LT); CleanupSearch.done(); } diff --git a/server/src/com/cloud/vm/ItWorkVO.java b/server/src/com/cloud/vm/ItWorkVO.java index 56539d31920..ca94b3825f1 100644 --- a/server/src/com/cloud/vm/ItWorkVO.java +++ b/server/src/com/cloud/vm/ItWorkVO.java @@ -58,7 +58,7 @@ public class ItWorkVO { @Column(name="thread") String threadName; - @Column(name="state") + @Column(name="step") Step step; @Column(name="updated_at") @@ -121,11 +121,11 @@ public class ItWorkVO { return managementServerId; } - public State getState() { + public State getType() { return type; } - public void setState(State type) { + public void setType(State type) { this.type = type; } @@ -137,8 +137,8 @@ public class ItWorkVO { return step; } - public void setStep(Step state) { - this.step = state; + public void setStep(Step step) { + this.step = step; } public long getUpdatedAt() { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 17434421845..6c8dd565099 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1027,13 +1027,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager // Recover the VM's disks List volumes = _volsDao.findByInstanceIdDestroyed(vmId); for (VolumeVO volume : volumes) { - _volsDao.recoverVolume(volume.getId()); // Create an event Long templateId = volume.getTemplateId(); Long diskOfferingId = volume.getDiskOfferingId(); long sizeMB = volume.getSize()/(1024*1024); StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); - EventUtils.saveEvent(User.UID_SYSTEM, volume.getAccountId(), EventTypes.EVENT_VOLUME_CREATE, "Created volume: "+ volume.getName() +" with size: " + sizeMB + " MB in pool: " + pool.getName()); UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, templateId , sizeMB); _usageEventDao.persist(usageEvent); diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 8bd572757b2..1c86fce46b8 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -98,11 +98,11 @@ DROP TABLE IF EXISTS `cloud`.`host_tags`; CREATE TABLE `cloud`.`op_it_work` ( `id` char(40) COMMENT 'id', `mgmt_server_id` bigint unsigned COMMENT 'management server id', - `created_on` bigint unsigned NOT NULL COMMENT 'when was this work detail created', + `created_at` bigint unsigned NOT NULL COMMENT 'when was this work detail created', `thread` varchar(255) NOT NULL COMMENT 'thread name', `type` char(32) NOT NULL COMMENT 'type of work', - `state` char(32) NOT NULL COMMENT 'state', - `updated_on` bigint unsigned NOT NULL COMMENT 'time it was taken over', + `step` char(32) NOT NULL COMMENT 'state', + `updated_at` bigint unsigned NOT NULL COMMENT 'time it was taken over', `instance_id` bigint unsigned NOT NULL COMMENT 'vm instance', `resource_type` char(32) COMMENT 'type of resource being worked on', `resource_id` bigint unsigned COMMENT 'resource id being worked on', @@ -677,7 +677,7 @@ CREATE TABLE `cloud`.`user_ip_address` ( INDEX `i_user_ip_address__source_nat`(`source_nat`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE VIEW `cloud`.`user_ip_address_view` AS SELECT INET_NTOA(user_ip_address.public_ip_address) as public_ip_address, user_ip_address.data_center_id, user_ip_address.account_id, user_ip_address.domain_id, user_ip_address.source_nat, user_ip_address.allocated, user_ip_address.vlan_db_id, user_ip_address.one_to_one_nat, user_ip_address.state, user_ip_address.mac_address, user_ip_address.network_id as associated_network_id from user_ip_address; +CREATE VIEW `cloud`.`user_ip_address_view` AS SELECT INET_NTOA(user_ip_address.public_ip_address) as ip_address, user_ip_address.data_center_id, user_ip_address.account_id, user_ip_address.domain_id, user_ip_address.source_nat, user_ip_address.allocated, user_ip_address.vlan_db_id, user_ip_address.one_to_one_nat, user_ip_address.state, user_ip_address.mac_address, user_ip_address.network_id as associated_network_id from user_ip_address; CREATE TABLE `cloud`.`user_statistics` ( `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT, From 6d9442be5409813a7fca4541cc839ebef6945e5a Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Tue, 11 Jan 2011 18:02:00 -0800 Subject: [PATCH 34/41] Finished all merges and unit testing --- .../src/com/cloud/api/ApiResponseHelper.java | 2 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 3 ++ .../com/cloud/storage/StorageManagerImpl.java | 6 ++- .../cloud/vm/VirtualMachineManagerImpl.java | 54 +++++++++---------- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index b441203afea..e8baf8d2065 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -739,7 +739,7 @@ public class ApiResponseHelper implements ResponseGenerator { volResponse.setDeviceId(volume.getDeviceId()); Long instanceId = volume.getInstanceId(); - if (instanceId != null) { + if (instanceId != null && volume.getState() != Volume.State.Destroy) { VMInstanceVO vm = ApiDBUtils.findVMInstanceById(instanceId); volResponse.setVirtualMachineId(vm.getId()); volResponse.setVirtualMachineName(vm.getName()); diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 57c2032bb19..4e3ad207872 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -562,6 +562,9 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx ConsoleProxyVO proxy = _consoleProxyDao.findById(proxyVmId); Account systemAcct = _accountMgr.getSystemAccount(); User systemUser = _accountMgr.getSystemUser(); + if (proxy.getState() == VirtualMachine.State.Running) { + return proxy; + } return _itMgr.start(proxy, null, systemUser, systemAcct); } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 1d6a35a38ed..254844457bb 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -2723,8 +2723,10 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag } String vmName = null; if (vol.getVolumeType() == VolumeType.ROOT && vol.getInstanceId() != null) { - VirtualMachine vm = _vmInstanceDao.findById(vol.getInstanceId()); - vmName = vm.getInstanceName(); + VirtualMachine vm = _vmInstanceDao.findByIdIncludingRemoved(vol.getInstanceId()); + if (vm != null) { + vmName = vm.getInstanceName(); + } } String volumePath = vol.getPath(); diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index d583832e272..54cfe63342f 100644 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -461,7 +461,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { while (retry-- != 0) { Transaction txn = Transaction.currentTxn(); txn.start(); - if (_vmDao.updateIf(vm, Event.StartRequested, null, work.getId())) { + if (stateTransitTo(vm, Event.StartRequested, null, work.getId())) { Journal journal = new Journal.LogJournal("Creating " + vm, s_logger); work = _workDao.persist(work); @@ -470,6 +470,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); } + txn.commit(); return new Ternary(vmGuru.findById(vmId), context, work); } @@ -477,35 +478,32 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager { s_logger.debug("Determining why we're unable to update the state to Starting for " + vm); } - try { - VMInstanceVO instance = _vmDao.lockRow(vmId, true); - if (instance == null) { - throw new ConcurrentOperationException("Unable to acquire lock on " + vm); + VMInstanceVO instance = _vmDao.lockRow(vmId, true); + if (instance == null) { + throw new ConcurrentOperationException("Unable to acquire lock on " + vm); + } + + State state = instance.getState(); + if (state == State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is already started: " + vm); } - - State state = instance.getState(); - if (state == State.Running) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is already started: " + vm); - } - return null; - } - - if (state.isTransitional()) { - if (!checkWorkItems(vm, state)) { - throw new ConcurrentOperationException("There are concurrent operations on the VM " + vm); - } else { - continue; - } - } - - if (state != State.Stopped) { - s_logger.debug("VM " + vm + " is not in a state to be started: " + state); - return null; - } - - } finally { txn.commit(); + return null; + } + + if (state.isTransitional()) { + if (!checkWorkItems(vm, state)) { + throw new ConcurrentOperationException("There are concurrent operations on the VM " + vm); + } else { + continue; + } + } + + if (state != State.Stopped) { + s_logger.debug("VM " + vm + " is not in a state to be started: " + state); + txn.commit(); + return null; } } From 5b68027d3a26f7c5805b36fa244622eee497701b Mon Sep 17 00:00:00 2001 From: anthony Date: Tue, 11 Jan 2011 17:03:22 -0800 Subject: [PATCH 35/41] bug 7858: for untagged vlan, broadcastRUi is vlan://untagged status 7858: resolved fixed --- .../router/VirtualNetworkApplianceManagerImpl.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index dd2248e2403..5a4371d666f 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1008,13 +1008,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian defaultNic.setNetmask(sourceNatIp.getNetmask()); defaultNic.setTrafficType(TrafficType.Public); defaultNic.setMacAddress(sourceNatIp.getMacAddress()); - if (sourceNatIp.getVlanTag().equals(Vlan.UNTAGGED)) { - defaultNic.setBroadcastType(BroadcastDomainType.Native); - } else { - defaultNic.setBroadcastType(BroadcastDomainType.Vlan); - defaultNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag())); - defaultNic.setIsolationUri(IsolationType.Vlan.toUri(sourceNatIp.getVlanTag())); - } + defaultNic.setBroadcastType(BroadcastDomainType.Vlan); + defaultNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag())); + defaultNic.setIsolationUri(IsolationType.Vlan.toUri(sourceNatIp.getVlanTag())); defaultNic.setDeviceId(2); networks.add(new Pair(publicConfigs.get(0), defaultNic)); NicProfile gatewayNic = new NicProfile(); From beb97057e1ab4f369323d8b0c6f14834e18290d4 Mon Sep 17 00:00:00 2001 From: anthony Date: Tue, 11 Jan 2011 18:08:10 -0800 Subject: [PATCH 36/41] reconnect hosts after MS restart --- server/src/com/cloud/host/dao/HostDaoImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/com/cloud/host/dao/HostDaoImpl.java b/server/src/com/cloud/host/dao/HostDaoImpl.java index b6622300529..9a49ca11c0f 100644 --- a/server/src/com/cloud/host/dao/HostDaoImpl.java +++ b/server/src/com/cloud/host/dao/HostDaoImpl.java @@ -381,8 +381,10 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao if( event.equals(Event.Ping) || event.equals(Event.AgentConnected)) { ub.set(host, _pingTimeAttr, System.currentTimeMillis() >> 10); } + } + if ( event.equals(Event.ManagementServerDown)) { + ub.set(host, _pingTimeAttr, (( System.currentTimeMillis() >> 10) - ( 10 * 60 ))); } - int result = update(ub, sc, null); assert result <= 1 : "How can this update " + result + " rows? "; From 7add7643e32bd506062095cc83d1c96086d2e45a Mon Sep 17 00:00:00 2001 From: anthony Date: Tue, 11 Jan 2011 18:58:20 -0800 Subject: [PATCH 37/41] bug 7748: need to make sure domr is up before start user VM, if domr is not up within 5 minutes, throw exception status 7748: resolved fixed --- .../VirtualNetworkApplianceManagerImpl.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 5a4371d666f..a1fd1da0ce4 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1034,10 +1034,20 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian EventUtils.saveEvent(User.UID_SYSTEM, owner.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_ROUTER_CREATE, "router creation failed", startEventId); } - } - - + } State state = router.getState(); + + if ( state == State.Starting ) { + // wait 300 seconds + for ( int i = 0; i < 300; ) { + try { + Thread.sleep(2); + } catch (Exception e) { + } + i += 2; + state = router.getState(); + } + } if (state != State.Starting && state != State.Running) { long startEventId = EventUtils.saveStartedEvent(User.UID_SYSTEM, owner.getId(), EventTypes.EVENT_ROUTER_START, "Starting router : " +router.getName()); router = this.start(router, _accountService.getSystemUser(), _accountService.getSystemAccount()); @@ -1047,7 +1057,11 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian EventUtils.saveEvent(User.UID_SYSTEM, owner.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_ROUTER_START, "failed to start router", startEventId); } } - return router; + state = router.getState(); + if ( state == State.Running ) { + return router; + } + throw new CloudRuntimeException(router.getName() + " is not running , it is in " + state); } @Override From 870d0835a570a17dd5c376e72a69bbc52019061f Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 11 Jan 2011 19:03:20 -0800 Subject: [PATCH 38/41] bug 7722: open vswitch - add entities to tunnel table/vlan mapping table when host connect, this make these tables lock free(for table lock) --- .../xen/resource/CitrixResourceBase.java | 2 +- .../com/cloud/network/ovs/OvsListener.java | 57 ++++++- .../network/ovs/OvsNetworkManagerImpl.java | 143 +++++++++--------- .../ovs/OvsVlanExhaustedException.java | 7 + .../cloud/network/ovs/dao/GreTunnelDao.java | 1 + .../network/ovs/dao/GreTunnelDaoImpl.java | 8 + .../cloud/network/ovs/dao/VlanMappingDao.java | 5 + .../cloud/network/ovs/dao/VlanMappingVO.java | 6 +- setup/db/create-schema.sql | 2 +- 9 files changed, 155 insertions(+), 76 deletions(-) create mode 100644 server/src/com/cloud/network/ovs/OvsVlanExhaustedException.java diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 2461b668bbd..ce63fd67fa6 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -532,7 +532,7 @@ public abstract class CitrixResourceBase implements ServerResource { } } - private Network setupvSwitchNetwork(Connection conn) { + private synchronized Network setupvSwitchNetwork(Connection conn) { try { if (_host.vswitchNetwork == null) { Network vswitchNw = null; diff --git a/server/src/com/cloud/network/ovs/OvsListener.java b/server/src/com/cloud/network/ovs/OvsListener.java index df31e6253b0..213edaf7b5f 100644 --- a/server/src/com/cloud/network/ovs/OvsListener.java +++ b/server/src/com/cloud/network/ovs/OvsListener.java @@ -1,8 +1,11 @@ package com.cloud.network.ovs; import java.util.HashSet; +import java.util.List; import java.util.Set; +import javax.persistence.EntityExistsException; + import org.apache.log4j.Logger; import com.cloud.agent.Listener; @@ -13,23 +16,33 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.PingRoutingWithOvsCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.exception.ConnectionException; +import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; import com.cloud.network.ovs.dao.GreTunnelDao; import com.cloud.network.ovs.dao.GreTunnelVO; import com.cloud.network.ovs.dao.OvsWorkDao; import com.cloud.network.ovs.dao.OvsWorkVO.Step; +import com.cloud.network.ovs.dao.VlanMappingDao; +import com.cloud.network.ovs.dao.VlanMappingVO; +import com.cloud.utils.component.Inject; public class OvsListener implements Listener { public static final Logger s_logger = Logger.getLogger(OvsListener.class.getName()); OvsNetworkManager _ovsNetworkMgr; OvsWorkDao _workDao; GreTunnelDao _tunnelDao; + VlanMappingDao _mappingDao; + HostDao _hostDao; - public OvsListener(OvsNetworkManager ovsMgr, OvsWorkDao workDao, GreTunnelDao tunnelDao) { + public OvsListener(OvsNetworkManager ovsMgr, OvsWorkDao workDao, GreTunnelDao tunnelDao, + VlanMappingDao mappingDao, HostDao hostDao) { this._ovsNetworkMgr = ovsMgr; this._workDao = workDao; this._tunnelDao = tunnelDao; + this._mappingDao = mappingDao; + this._hostDao = hostDao; } @Override @@ -111,6 +124,48 @@ public class OvsListener implements Listener { @Override public void processConnect(HostVO host, StartupCommand cmd) throws ConnectionException { + if (host.getType() != Host.Type.Routing) { + return; + } + + List maps = _mappingDao.listByHostId(host.getId()); + if (maps.size() == 0) { + for (int i=0; i<512; i++) { + VlanMappingVO vo = new VlanMappingVO(0, host.getId(), i); + _mappingDao.persist(vo); + } + } + + try { + List hosts = _hostDao.listByType(Host.Type.Routing); + for (HostVO h : hosts) { + if (h.getId() == host.getId()) { + continue; + } + + GreTunnelVO t = _tunnelDao.getByFromAndTo(host.getId(), h.getId()); + if (t == null) { + t = new GreTunnelVO(host.getId(), h.getId()); + try { + _tunnelDao.persist(t); + } catch (EntityExistsException e) { + s_logger.debug(String.format("Already has (from=%1$s, to=%2$s)", host.getId(), h.getId())); + } + } + + t = _tunnelDao.getByFromAndTo(h.getId(), host.getId()); + if (t == null) { + t = new GreTunnelVO(h.getId(), host.getId()); + try { + _tunnelDao.persist(t); + } catch (EntityExistsException e) { + s_logger.debug(String.format("Already has (from=%1$s, to=%2$s)", h.getId(), host.getId())); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } } @Override diff --git a/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java b/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java index 88466315989..da5dd3a6641 100644 --- a/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java +++ b/server/src/com/cloud/network/ovs/OvsNetworkManagerImpl.java @@ -12,6 +12,7 @@ import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; +import javax.persistence.EntityExistsException; import org.apache.log4j.Logger; @@ -112,16 +113,9 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { _serverId = ((ManagementServer)ComponentLocator.getComponent(ManagementServer.Name)).getId(); _executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS")); _cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup")); - _ovsListener = new OvsListener(this, _workDao, _tunnelDao); + _ovsListener = new OvsListener(this, _workDao, _tunnelDao, _vlanMappingDao, _hostDao); _agentMgr.registerForHostEvents(_ovsListener, true, true, true); - //FIXME: - GreTunnelVO t = _tunnelDao.lockRow(new Long(1), true); - if (t == null) { - t = new GreTunnelVO(0, 0); - _tunnelDao.persist(t); - } - return true; } @@ -249,14 +243,13 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { } - //TODO: think about lock @DB - protected long askVlanId(long accountId, long hostId) { + protected long askVlanId(long accountId, long hostId) throws OvsVlanExhaustedException { assert _isEnabled : "Who call me ??? while OvsNetwokr is not enabled!!!"; final Transaction txn = Transaction.currentTxn(); txn.start(); - VlanMappingVO currVlan = _vlanMappingDao.findByAccountIdAndHostId(accountId, hostId); + VlanMappingVO currVlan = _vlanMappingDao.lockByAccountIdAndHostId(accountId, hostId); long vlan = 0; if (currVlan != null) { @@ -270,31 +263,35 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { } Listmappings = _vlanMappingDao.listByHostId(hostId); - if (mappings.size() > 0) { - ArrayList vlans = new ArrayList(); - for (VlanMappingVO vo : mappings) { - vlans.add(new Long(vo.getVlan())); - } - - // Find first available vlan - int i; - for (i=0; i<4096; i++) { - if (!vlans.contains(new Long(i))) { - vlan = i; + assert mappings.size() > 0: "where is my data!? it should be added when host connected!"; + + VlanMappingVO target = null; + for (VlanMappingVO vo : mappings) { + if (vo.getAccountId() == 0) { + target = _vlanMappingDao.lockRow(vo.getId(), true); + if (target == null || target.getAccountId() != 0) { + s_logger.debug("Someone took vlan mapping host = " + + vo.getHostId() + " vlan = " + vo.getVlan()); + continue; + } else { break; } } - assert i!=4096 : "Terrible, vlan exhausted on this server!!!"; } - VlanMappingVO newVlan = new VlanMappingVO(accountId, hostId, vlan); - _vlanMappingDao.persist(newVlan); + if (target == null) { + throw new OvsVlanExhaustedException("vlan exhausted on host " + hostId); + } + + target.setAccountId(accountId); + target.ref(); + _vlanMappingDao.update(target.getId(), target); _vlanMappingDirtyDao.markDirty(accountId); String s = String.format("allocate a new vlan %1$s(account:%2$s, hostId:%3$s), mark dirty", vlan, accountId, hostId); s_logger.debug("OVSDIRTY:" + s); txn.commit(); - return vlan; + return target.getVlan(); } @DB @@ -309,49 +306,51 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { return; } + final Transaction txn = Transaction.currentTxn(); long hostId = dest.getHost().getId(); long accountId = instance.getAccountId(); - - final Transaction txn = Transaction.currentTxn(); - txn.start(); //TODO: considerate router? Listvms = _userVmDao.listByAccountId(accountId); + DomainRouterVO router = _routerDao.findBy(accountId, instance.getDataCenterId()); + Listins = new ArrayList(); + ins.addAll(vms); + ins.add(router); ListtoHostIds = new ArrayList(); ListfromHostIds = new ArrayList(); - GreTunnelVO tvo = _tunnelDao.acquireInLockTable(new Long(1)); - if (tvo == null) { - throw new GreTunnelException("can't lock gre tunnel table for: from=" + hostId); - } - for (UserVmVO v : vms) { + for (VMInstanceVO v : ins) { Long rh = v.getHostId(); if (rh == null || rh.longValue() == hostId) { continue; } - GreTunnelVO tunnel = _tunnelDao.getByFromAndTo(hostId, rh.longValue()); + txn.start(); + GreTunnelVO tunnel = _tunnelDao.lockByFromAndTo(hostId, + rh.longValue()); + txn.commit(); if (tunnel == null) { - tunnel = new GreTunnelVO(hostId, rh.longValue()); - _tunnelDao.persist(tunnel); - - if (!toHostIds.contains(rh)) { - toHostIds.add(rh); - } + throw new GreTunnelException(String.format( + "No entity(from=%1$s, to=%2$s) of failed to lock", + hostId, rh.longValue())); + } + + if (tunnel.getInPort() == 0 && !toHostIds.contains(rh)) { + toHostIds.add(rh); } - tunnel = _tunnelDao.getByFromAndTo(rh.longValue(), hostId); + txn.start(); + tunnel = _tunnelDao.lockByFromAndTo(rh.longValue(), hostId); + txn.commit(); if (tunnel == null) { - tunnel = new GreTunnelVO(rh.longValue(), hostId); - _tunnelDao.persist(tunnel); - - if (!fromHostIds.contains(rh)) { - fromHostIds.add(rh); - } - } - + throw new GreTunnelException(String.format( + "No entity(from=%1$s, to=%2$s) of failed to lock", + rh.longValue(), hostId)); + } + + if (tunnel.getInPort() == 0 && !fromHostIds.contains(rh)) { + fromHostIds.add(rh); + } } - _tunnelDao.releaseFromLockTable(new Long(1)); - txn.commit(); try { String myIp = dest.getHost().getPrivateIpAddress(); @@ -381,11 +380,9 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { if (tunnels.size() == 0) { return "[]"; } else { - Transaction txn = Transaction.currentTxn(); - txn.start(); List maps = new ArrayList(); for (GreTunnelVO t : tunnels) { - VlanMappingVO m = _vlanMappingDao.lockByAccountIdAndHostId(accountId, t.getTo()); + VlanMappingVO m = _vlanMappingDao.findByAccountIdAndHostId(accountId, t.getTo()); if (m == null) { s_logger.debug("Host " + t.getTo() + " has no VM for account " + accountId + ", skip it"); continue; @@ -393,7 +390,7 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { String s = String.format("%1$s:%2$s", m.getVlan(), t.getInPort()); maps.add(s); } - txn.commit(); + return maps.toString(); } } @@ -409,17 +406,21 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { && vmType != VirtualMachine.Type.DomainRouter) { return; } - - long hostId = instance.getHostId(); - long accountId = instance.getAccountId(); - String tag = Long.toString(askVlanId(accountId, hostId)); - CheckAndUpdateDhcpFlow(instance); - String vlans = getVlanInPortMapping(accountId, hostId); - VmFlowLogVO log = _flowLogDao.findOrNewByVmId(instance.getId(), - instance.getName()); - cmds.addCommand(new OvsSetTagAndFlowCommand(instance.getName(), tag, - vlans, Long.toString(log.getLogsequence()), instance.getId())); + try { + long hostId = instance.getHostId(); + long accountId = instance.getAccountId(); + String tag = Long.toString(askVlanId(accountId, hostId)); + CheckAndUpdateDhcpFlow(instance); + String vlans = getVlanInPortMapping(accountId, hostId); + VmFlowLogVO log = _flowLogDao.findOrNewByVmId(instance.getId(), + instance.getName()); + cmds.addCommand(new OvsSetTagAndFlowCommand(instance.getName(), + tag, vlans, Long.toString(log.getLogsequence()), instance + .getId())); + } catch (OvsVlanExhaustedException e) { + e.printStackTrace(); + } } //FIXME: if router has record in database but not start, this will hang 10 secs due to host @@ -444,9 +445,8 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { } try { - String tag = Long.toString(_vlanMappingDao.findByAccountIdAndHostId(accountId, - router.getHostId()).getVlan()); long hostId = router.getHostId(); + String tag = Long.toString(_vlanMappingDao.findByAccountIdAndHostId(accountId, hostId).getVlan()); VmFlowLogVO log = _flowLogDao.findOrNewByVmId(instance.getId(), instance.getName()); String vlans = getVlanInPortMapping(accountId, hostId); s_logger.debug("ask router " + router.getName() + " on host " @@ -551,7 +551,6 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { s_logger.debug("OVSDIRTY:Clean dirty for account " + instance.getAccountId()); } - //TODO: think about lock @DB protected void checkAndRemove(VMInstanceVO instance) { long accountId = instance.getAccountId(); @@ -559,20 +558,20 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager { final Transaction txn = Transaction.currentTxn(); txn.start(); - VlanMappingVO vo = _vlanMappingDao.findByAccountIdAndHostId(accountId, hostId); + VlanMappingVO vo = _vlanMappingDao.lockByAccountIdAndHostId(accountId, hostId); assert vo!=null: "Why there is no record for account " + accountId + " host " + hostId; if (vo.unref() == 0) { - _vlanMappingDao.remove(vo.getId()); + vo.setAccountId(0); _vlanMappingDirtyDao.markDirty(accountId); String s = String.format("%1$s is the last VM(host:%2$s, accountId:%3$s), remove vlan", instance.getName(), hostId, accountId); s_logger.debug("OVSDIRTY:" + s); } else { - _vlanMappingDao.update(vo.getId(), vo); s_logger.debug(instance.getName() + " reduces reference count of (account,host) = (" + accountId + "," + hostId + ") to " + vo.getRef()); } + _vlanMappingDao.update(vo.getId(), vo); _flowLogDao.deleteByVmId(instance.getId()); txn.commit(); diff --git a/server/src/com/cloud/network/ovs/OvsVlanExhaustedException.java b/server/src/com/cloud/network/ovs/OvsVlanExhaustedException.java new file mode 100644 index 00000000000..fcf9e6dad4d --- /dev/null +++ b/server/src/com/cloud/network/ovs/OvsVlanExhaustedException.java @@ -0,0 +1,7 @@ +package com.cloud.network.ovs; + +public class OvsVlanExhaustedException extends Exception { + public OvsVlanExhaustedException(String msg) { + super(msg); + } +} diff --git a/server/src/com/cloud/network/ovs/dao/GreTunnelDao.java b/server/src/com/cloud/network/ovs/dao/GreTunnelDao.java index 753c70fcac2..2faa31e6af6 100644 --- a/server/src/com/cloud/network/ovs/dao/GreTunnelDao.java +++ b/server/src/com/cloud/network/ovs/dao/GreTunnelDao.java @@ -7,4 +7,5 @@ import com.cloud.utils.db.GenericDao; public interface GreTunnelDao extends GenericDao { List getByFrom(long from); GreTunnelVO getByFromAndTo(long from, long To); + GreTunnelVO lockByFromAndTo(long from, long to); } diff --git a/server/src/com/cloud/network/ovs/dao/GreTunnelDaoImpl.java b/server/src/com/cloud/network/ovs/dao/GreTunnelDaoImpl.java index 6caa68674c5..05a6c4b94d7 100644 --- a/server/src/com/cloud/network/ovs/dao/GreTunnelDaoImpl.java +++ b/server/src/com/cloud/network/ovs/dao/GreTunnelDaoImpl.java @@ -40,5 +40,13 @@ public class GreTunnelDaoImpl extends GenericDaoBase sc.setParameters("to", to); return findOneBy(sc); } + + @Override + public GreTunnelVO lockByFromAndTo(long from, long to) { + SearchCriteria sc = fromToSearch.create(); + sc.setParameters("from", from); + sc.setParameters("to", to); + return lockOneRandomRow(sc, true); + } } diff --git a/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java b/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java index d74bd3e41a4..fe5a58d3707 100644 --- a/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java +++ b/server/src/com/cloud/network/ovs/dao/VlanMappingDao.java @@ -6,9 +6,14 @@ import com.cloud.utils.db.GenericDao; public interface VlanMappingDao extends GenericDao { List listByAccountIdAndHostId(long accountId, long hostId); + List listByHostId(long hostId); + List listByAccountId(long accountId); + List lockByAccountId(long accoutnId); + VlanMappingVO findByAccountIdAndHostId(long accountId, long hostId); + VlanMappingVO lockByAccountIdAndHostId(long accountId, long hostId); } diff --git a/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java b/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java index 3376c05e465..487bfb1153d 100644 --- a/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java +++ b/server/src/com/cloud/network/ovs/dao/VlanMappingVO.java @@ -31,7 +31,7 @@ public class VlanMappingVO { this.hostId = hostId; this.accountId = accountId; this.vlan = vlan; - this.ref = 1; + this.ref = 0; } public VlanMappingVO() { @@ -45,6 +45,10 @@ public class VlanMappingVO { public long getAccountId() { return accountId; } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } public long getVlan() { return vlan; diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 1c86fce46b8..7ca67a16387 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1354,7 +1354,7 @@ CREATE TABLE `cloud`.`ovs_tunnel_alloc`( `from` bigint unsigned COMMENT 'from host id', `to` bigint unsigned COMMENT 'to host id', `in_port` int unsigned COMMENT 'in port on open vswitch', - PRIMARY KEY(`id`) + PRIMARY KEY(`from`, `to`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`ovs_vlan_mapping_dirty`( From 5b4f410621a4b7fbea90f580372981fae95281fc Mon Sep 17 00:00:00 2001 From: will Date: Tue, 11 Jan 2011 19:08:08 -0800 Subject: [PATCH 39/41] bug 7803: Fixed vm wizard to accomodate the new defaulted networks. The only case that doesn't work yet is when the virtual network is "unavailable". Need to fix that still. --- ui/jsp/instance.jsp | 87 +++++++++++-------------------- ui/scripts/cloud.core.instance.js | 77 ++++++++++++++++++--------- 2 files changed, 83 insertions(+), 81 deletions(-) diff --git a/ui/jsp/instance.jsp b/ui/jsp/instance.jsp index 18060798963..2442cd37fce 100644 --- a/ui/jsp/instance.jsp +++ b/ui/jsp/instance.jsp @@ -354,9 +354,25 @@
- + + + +