From 1e8648c92de9df281aaabc5067d6d1d61be3a4a3 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Mon, 11 Feb 2013 11:49:52 -0800 Subject: [PATCH 01/42] CLOUDSTACK-1124 [EC2 Query API] Improve the logging. Having empty response objects is harmless to functionality This is observed in the awsapi.log for all Describe* EC2 commands when the response object returned by CS has no elements. But with EC2DescribeImages command it is always observed. This is because every time DescribeImage is fired, we call CS listTemplates command multiple times, each time setting a different value for the templateFilter parameter (e.g. featured, executable, community etc.). And for some of these CS calls made we obtain am empty response and hence the message in the logs. --- awsapi/src/com/cloud/stack/CloudStackClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awsapi/src/com/cloud/stack/CloudStackClient.java b/awsapi/src/com/cloud/stack/CloudStackClient.java index 5002aa593e7..5017bd423dd 100644 --- a/awsapi/src/com/cloud/stack/CloudStackClient.java +++ b/awsapi/src/com/cloud/stack/CloudStackClient.java @@ -151,7 +151,8 @@ public class CloudStackClient { return (new Gson()).fromJson(json.eval(responseName + "." + responseObjName), collectionType); } catch(Exception e) { // this happens because responseObjName won't exist if there are no objects in the list. - logger.debug("Unable to find responseObjName:[" + responseObjName + "]. Returning null! Exception: " + e.getMessage()); + logger.debug("CloudSatck API response doesn't contain responseObjName:" + responseObjName + + " because response is empty"); return null; } return (new Gson()).fromJson(json.eval(responseName), collectionType); From 41f6585754ed9d7e878f3b53ad4294e225cdb759 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Mon, 11 Feb 2013 12:04:55 -0800 Subject: [PATCH 02/42] CLOUDSTACK-1136: [EC2 Query API] AssociateAdress, DisassociateAddress and ReleaseAddress fail with NPE When invalid parameter is provided as input for any of these API's we get an NPE --- .../bridge/service/core/ec2/EC2Engine.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index 9573d5ba092..b729f778ed3 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -767,7 +767,10 @@ public class EC2Engine extends ManagerBase { */ public boolean releaseAddress(EC2ReleaseAddress request) { try { - CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); + List cloudIps = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null); + if (cloudIps == null) + throw new EC2ServiceException(ServerError.InternalError, "Specified ipAddress doesn't exist"); + CloudStackIpAddress cloudIp = cloudIps.get(0); CloudStackInfoResponse resp = getApi().disassociateIpAddress(cloudIp.getId()); if (resp != null) { return resp.getSuccess(); @@ -787,8 +790,17 @@ public class EC2Engine extends ManagerBase { */ public boolean associateAddress( EC2AssociateAddress request ) { try { - CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); - CloudStackUserVm cloudVm = getApi().listVirtualMachines(null, null, true, null, null, null, null, request.getInstanceId(), null, null, null, null, null, null, null, null, null).get(0); + List cloudIps = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null); + if (cloudIps == null) + throw new EC2ServiceException(ServerError.InternalError, "Specified ipAddress doesn't exist"); + CloudStackIpAddress cloudIp = cloudIps.get(0); + + List vmList = getApi().listVirtualMachines(null, null, true, null, null, null, null, + request.getInstanceId(), null, null, null, null, null, null, null, null, null); + if (vmList == null || vmList.size() == 0) { + throw new EC2ServiceException(ServerError.InternalError, "Specified instance-id doesn't exist"); + } + CloudStackUserVm cloudVm = vmList.get(0); CloudStackInfoResponse resp = getApi().enableStaticNat(cloudIp.getId(), cloudVm.getId()); if (resp != null) { @@ -809,7 +821,11 @@ public class EC2Engine extends ManagerBase { */ public boolean disassociateAddress( EC2DisassociateAddress request ) { try { - CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); + List cloudIps = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null); + if (cloudIps == null) + throw new EC2ServiceException(ServerError.InternalError, "Specified ipAddress doesn't exist"); + CloudStackIpAddress cloudIp = cloudIps.get(0); + CloudStackInfoResponse resp = getApi().disableStaticNat(cloudIp.getId()); if (resp != null) { return resp.getSuccess(); From 06fd21bd4498479c52bcf12921bf867190ace3a1 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Mon, 11 Feb 2013 14:20:35 -0800 Subject: [PATCH 03/42] CLOUDSTACK-1126: [EC2 Query API] RunInstances ignores the UserData input parameter When EC2RunInstances is called with UserData parameter, the parameter is not propogated to CS --- awsapi/src/com/cloud/bridge/service/EC2RestServlet.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java index 5788b352a82..5d151bac7e4 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java @@ -1169,6 +1169,11 @@ public class EC2RestServlet extends HttpServlet { EC2request.setKeyName(keyName[0]); } + String[] userData = request.getParameterValues("UserData"); + if ( userData != null) { + EC2request.setUserData( userData[0]); + } + Enumeration names = request.getParameterNames(); while( names.hasMoreElements()) { String key = (String)names.nextElement(); From 52b81f8bffb09fa9a4134a5f55ad2d676d24ef3f Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Mon, 11 Feb 2013 15:25:30 -0800 Subject: [PATCH 04/42] CLOUDSTACK-1127: [EC2 Query API] DescribeSecurityGroups, support for additional filters Add support for filters ip-permission.group-name and ip-permission.user-id --- .../service/core/ec2/EC2GroupFilterSet.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2GroupFilterSet.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2GroupFilterSet.java index 149d25f6151..dbc367c109d 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2GroupFilterSet.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2GroupFilterSet.java @@ -41,6 +41,8 @@ public class EC2GroupFilterSet { filterTypes.put( "ip-permission.from-port", "string" ); filterTypes.put( "ip-permission.to-port", "string" ); filterTypes.put( "ip-permission.protocol", "string" ); + filterTypes.put( "ip-permission.group-name","string" ); + filterTypes.put( "ip-permission.user-id", "string" ); filterTypes.put( "owner-id", "string" ); } @@ -126,7 +128,7 @@ public class EC2GroupFilterSet { EC2IpPermission[] permissionSet = sg.getIpPermissionSet(); for (EC2IpPermission perm : permissionSet) { - boolean matched = true; + boolean matched = false; for (EC2Filter filter : ipPermissionFilterSet) { String filterName = filter.getName(); String[] valueSet = filter.getValueSet(); @@ -144,6 +146,24 @@ public class EC2GroupFilterSet { matched = containsString( perm.getToPort().toString(), valueSet ); } else if (filterName.equalsIgnoreCase( "ip-permission.protocol" )) matched = containsString( perm.getProtocol(), valueSet ); + else if (filterName.equalsIgnoreCase( "ip-permission.group-name" )) { + EC2SecurityGroup[] userSet = perm.getUserSet(); + for (EC2SecurityGroup user : userSet) { + if (containsString(user.getName(), valueSet)) { + matched = true; + break; + } + } + } + else if (filterName.equalsIgnoreCase( "ip-permission.user-id" )){ + EC2SecurityGroup[] userSet = perm.getUserSet(); + for (EC2SecurityGroup user : userSet) { + if (containsString(user.getAccountName(), valueSet)) { + matched = true; + break; + } + } + } if (!matched) break; } if (matched) return true; From ebe738ee2007c910ca20ab134d0a0ed8be8259d7 Mon Sep 17 00:00:00 2001 From: Chip Childers Date: Mon, 11 Feb 2013 21:40:06 -0500 Subject: [PATCH 05/42] CLOUDSTACK-1233: Fixed veewee config file legal documentation The veewee configuration files used for the devcloud base-box build, as well as the newly introduced files used to create a system vm, were inappropriately identified as ASLv2 licensed and copyrighted by the ASF. The fixes: - The ASF headers stripped to match the source. - The files are now excluded from RAT checks in the root pom. - The tools/whisker/descriptor.xml file updated - The root LICENSE and NOTICE were re-generated Changes in the Apache Whisker templates for LICENSE and NOTICE files caused a good bit of whitespace oddness in this commit for those 2 files. Signed-off-by: Chip Childers --- LICENSE | 185 +++++++++++------- NOTICE | 49 +++-- pom.xml | 10 +- .../definitions/systemvmtemplate/LICENSE | 21 -- .../definitions/systemvmtemplate/base.sh | 18 -- .../definitions/systemvmtemplate/cleanup.sh | 17 -- .../systemvmtemplate/definition.rb | 17 -- .../definitions/systemvmtemplate/preseed.cfg | 17 -- .../definitions/systemvmtemplate/zerodisk.sh | 17 -- .../deps/boxes/basebox-build/definition.rb | 17 -- .../src/deps/boxes/basebox-build/preseed.cfg | 17 -- tools/whisker/descriptor.xml | 31 ++- 12 files changed, 177 insertions(+), 239 deletions(-) delete mode 100644 tools/appliance/definitions/systemvmtemplate/LICENSE diff --git a/LICENSE b/LICENSE index 73bc738391f..eda958726f8 100644 --- a/LICENSE +++ b/LICENSE @@ -180,7 +180,6 @@ Copyright (c) 2013 The Apache Software Foundation This distribution contains third party resources. - Within the . directory licensed under the BSD (3-clause) http://www.opensource.org/licenses/BSD-3-Clause (as follows) @@ -209,10 +208,9 @@ Within the . directory CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + from Thomas Nagy http://code.google.com/p/waf/ - waf - + waf Within the awsapi directory licensed under the BSD (3-clause) http://www.opensource.org/licenses/BSD-3-Clause (as follows) @@ -242,10 +240,9 @@ Within the awsapi directory CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + from Thomas Nagy http://code.google.com/p/waf/ - waf - + waf Within the console-proxy/js directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -270,10 +267,9 @@ Within the console-proxy/js directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from John Resig - jquery.js - + jquery.js Within the deps directory licensed under the BSD (2-clause) for XenServerJava http://www.opensource.org/licenses/BSD-2-Clause (as follows) @@ -304,28 +300,27 @@ Within the deps directory THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + from Citrix Systems, Inc http://www.citrix.com/ - XenServerJava http://community.citrix.com/cdn/xs/sdks/ - + XenServerJava from http://community.citrix.com/cdn/xs/sdks/ Within the patches/systemvm/debian/config/etc directory placed in the public domain + by Adiscon GmbH http://www.adiscon.com/ + rsyslog.conf by Simon Kelley - dnsmasq.conf - vpcdnsmasq.conf - + dnsmasq.conf + vpcdnsmasq.conf Within the patches/systemvm/debian/config/etc/apache2 directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2012 The Apache Software Foundation from The Apache Software Foundation http://www.apache.org/ - httpd.conf - ports.conf - sites-available/default - sites-available/default-ssl - vhostexample.conf - + httpd.conf + ports.conf + sites-available/default + sites-available/default-ssl + vhostexample.conf Within the patches/systemvm/debian/config/etc/ssh/ directory licensed under the BSD (2-clause) http://www.opensource.org/licenses/BSD-2-Clause (as follows) @@ -354,40 +349,95 @@ Within the patches/systemvm/debian/config/etc/ssh/ directory ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + from OpenSSH Project http://www.openssh.org/ - sshd_config - + sshd_config Within the patches/systemvm/debian/config/root/redundant_router directory placed in the public domain by The netfilter.org project http://www.netfilter.org/ - conntrackd.conf.templ - + conntrackd.conf.templ Within the scripts/storage/secondary directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2010-2011 OpenStack, LLC. from OpenStack, LLC http://www.openstack.org - swift - + swift Within the scripts/vm/hypervisor/xenserver directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2010-2011 OpenStack, LLC. from OpenStack, LLC http://www.openstack.org - swift + swift +Within the tools/appliance/definitions/systemvmtemplate directory + licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) + + Copyright (c) 2010-2012 Patrick Debois + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + from Patrick Debois http://www.jedi.be/blog/ + base.sh from https://github.com/jedi4ever/veewee + cleanup.sh from https://github.com/jedi4ever/veewee + definition.rb from https://github.com/jedi4ever/veewee + preseed.cfg from https://github.com/jedi4ever/veewee + zerodisk.sh from https://github.com/jedi4ever/veewee + +Within the tools/devcloud/src/deps/boxes/basebox-build directory + licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) + + Copyright (c) 2010-2012 Patrick Debois + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + from Patrick Debois http://www.jedi.be/blog/ + definition.rb from https://github.com/jedi4ever/veewee + preseed.cfg from https://github.com/jedi4ever/veewee Within the ui/lib directory placed in the public domain by Eric Meyer http://meyerweb.com/eric/ - reset.css + reset.css from http://meyerweb.com/eric/tools/css/reset/ licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2006 Google Inc. from Google Inc. http://google.com - excanvas.js http://code.google.com/p/explorercanvas/ + excanvas.js from http://code.google.com/p/explorercanvas/ licensed under the BSD (2-clause) http://www.opensource.org/licenses/BSD-2-Clause (as follows) @@ -417,9 +467,9 @@ Within the ui/lib directory ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + from George McGinley Smith - jquery.easing.js + jquery.easing.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -443,9 +493,9 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from John Resig - jquery.js + jquery.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -469,9 +519,9 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from Jorn Zaefferer - jquery.validate.js + jquery.validate.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -495,9 +545,9 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from Sebastian Tschan https://blueimp.net - jquery.md5.js + jquery.md5.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -521,10 +571,9 @@ Within the ui/lib directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from Klaus Hartl http://stilbuero.de - jquery.cookies.js - + jquery.cookies.js Within the ui/lib/flot directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -549,18 +598,18 @@ Within the ui/lib/flot directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from IOLA http://www.iola.dk/ - jquery.flot.crosshair.js - jquery.flot.fillbetween.js - jquery.flot.image.js - jquery.flot.js - jquery.flot.navigate.js - jquery.flot.resize.js - jquery.flot.selection.js - jquery.flot.stack.js - jquery.flot.symbol.js - jquery.flot.threshold.js + jquery.flot.crosshair.js + jquery.flot.fillbetween.js + jquery.flot.image.js + jquery.flot.js + jquery.flot.navigate.js + jquery.flot.resize.js + jquery.flot.selection.js + jquery.flot.stack.js + jquery.flot.symbol.js + jquery.flot.threshold.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -585,9 +634,9 @@ Within the ui/lib/flot directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from Brian Medendorp - jquery.pie.js + jquery.pie.js licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -610,10 +659,9 @@ Within the ui/lib/flot directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from Ole Laursen - jquery.colorhelpers.js - + jquery.colorhelpers.js Within the ui/lib/jquery-ui directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -637,12 +685,11 @@ Within the ui/lib/jquery-ui directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from jQuery UI Developers http://jqueryui.com/about - css/jquery-ui.css - index.html - js/jquery-ui.js - + css/jquery-ui.css + index.html + js/jquery-ui.js Within the ui/lib/qunit directory licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows) @@ -667,14 +714,14 @@ Within the ui/lib/qunit directory LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + from Jorn Zaefferer - qunit.css http://docs.jquery.com/QUnit - qunit.js http://docs.jquery.com/QUnit - + qunit.css from http://docs.jquery.com/QUnit + qunit.js from http://docs.jquery.com/QUnit Within the utils/src/com/cloud/utils/db directory licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above) Copyright (c) 2004 Clinton Begin from Clinton Begin http://code.google.com/p/mybatis/ - ScriptRunner.java http://code.google.com/p/mybatis/ + ScriptRunner.java from http://code.google.com/p/mybatis/ + diff --git a/NOTICE b/NOTICE index 5cdd9fcee19..d36048aabda 100644 --- a/NOTICE +++ b/NOTICE @@ -6,30 +6,7 @@ - Source code distribution if this software contains third party resources requiring - the following notices: - - - For - jquery.md5.js - - - jQuery MD5 Plugin 1.2.1 - https://github.com/blueimp/jQuery-MD5 - - Copyright 2010, Sebastian Tschan - https://blueimp.net - - Licensed under the MIT license: - http://creativecommons.org/licenses/MIT/ - - Based on - A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - Digest Algorithm, as defined in RFC 1321. - Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 - Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - Distributed under the BSD License - See http://pajhome.org.uk/crypt/md5 for more info. + This distribution contains third party resources requiring the following notices: For @@ -66,6 +43,28 @@ Date: Thu May 12 15:04:36 2011 -0400 + For + jquery.md5.js + + + jQuery MD5 Plugin 1.2.1 + https://github.com/blueimp/jQuery-MD5 + + Copyright 2010, Sebastian Tschan + https://blueimp.net + + Licensed under the MIT license: + http://creativecommons.org/licenses/MIT/ + + Based on + A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + Digest Algorithm, as defined in RFC 1321. + Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 + Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + Distributed under the BSD License + See http://pajhome.org.uk/crypt/md5 for more info. + + For jquery.colorhelpers.js @@ -76,4 +75,4 @@ Inspiration from jQuery color animation plugin by John Resig. - Released under the MIT license by Ole Laursen, October 2009. \ No newline at end of file + Released under the MIT license by Ole Laursen, October 2009. diff --git a/pom.xml b/pom.xml index 5a1f66eded5..9a077692d4f 100644 --- a/pom.xml +++ b/pom.xml @@ -372,6 +372,13 @@ dist/console-proxy/js/jquery.js scripts/vm/systemvm/id_rsa.cloud tools/devcloud/basebuild/puppet-devcloudinitial/files/network.conf + tools/appliance/definitions/systemvmtemplate/base.sh + tools/appliance/definitions/systemvmtemplate/cleanup.sh + tools/appliance/definitions/systemvmtemplate/definition.rb + tools/appliance/definitions/systemvmtemplate/preseed.cfg + tools/appliance/definitions/systemvmtemplate/zerodisk.sh + tools/devcloud/src/deps/boxes/basebox-build/definition.rb + tools/devcloud/src/deps/boxes/basebox-build/preseed.cfg ui/lib/flot/jquery.colorhelpers.js ui/lib/flot/jquery.flot.crosshair.js ui/lib/flot/jquery.flot.fillbetween.js @@ -406,16 +413,13 @@ patches/systemvm/debian/config/etc/dnsmasq.conf patches/systemvm/debian/config/etc/vpcdnsmasq.conf patches/systemvm/debian/config/etc/ssh/sshd_config - patches/systemvm/debian/config/etc/rsyslog.conf - patches/systemvm/debian/config/etc/logrotate.conf patches/systemvm/debian/config/etc/logrotate.d/* patches/systemvm/debian/config/etc/sysctl.conf patches/systemvm/debian/config/root/redundant_router/keepalived.conf.templ patches/systemvm/debian/config/root/redundant_router/arping_gateways.sh.templ patches/systemvm/debian/config/root/redundant_router/conntrackd.conf.templ - patches/systemvm/debian/vpn/etc/ipsec.conf patches/systemvm/debian/vpn/etc/ppp/options.xl2tpd patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf diff --git a/tools/appliance/definitions/systemvmtemplate/LICENSE b/tools/appliance/definitions/systemvmtemplate/LICENSE deleted file mode 100644 index c33c3bba2ae..00000000000 --- a/tools/appliance/definitions/systemvmtemplate/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2010-2012 Patrick Debois - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/tools/appliance/definitions/systemvmtemplate/base.sh b/tools/appliance/definitions/systemvmtemplate/base.sh index 5b35b56c9e3..a6b69e6f096 100644 --- a/tools/appliance/definitions/systemvmtemplate/base.sh +++ b/tools/appliance/definitions/systemvmtemplate/base.sh @@ -1,21 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - # Update the box apt-get -y update #below are needed for ruby perhaps diff --git a/tools/appliance/definitions/systemvmtemplate/cleanup.sh b/tools/appliance/definitions/systemvmtemplate/cleanup.sh index 42d0fd64769..c6a1e9068d6 100644 --- a/tools/appliance/definitions/systemvmtemplate/cleanup.sh +++ b/tools/appliance/definitions/systemvmtemplate/cleanup.sh @@ -1,20 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - # Clean up #apt-get -y remove linux-headers-$(uname -r) build-essential apt-get -y remove dictionaries-common busybox diff --git a/tools/appliance/definitions/systemvmtemplate/definition.rb b/tools/appliance/definitions/systemvmtemplate/definition.rb index ec591ba7f3d..86f555507e4 100644 --- a/tools/appliance/definitions/systemvmtemplate/definition.rb +++ b/tools/appliance/definitions/systemvmtemplate/definition.rb @@ -1,20 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - Veewee::Definition.declare({ :cpu_count => '1', :memory_size=> '256', diff --git a/tools/appliance/definitions/systemvmtemplate/preseed.cfg b/tools/appliance/definitions/systemvmtemplate/preseed.cfg index 0643ce3329b..204d573a9d0 100644 --- a/tools/appliance/definitions/systemvmtemplate/preseed.cfg +++ b/tools/appliance/definitions/systemvmtemplate/preseed.cfg @@ -1,20 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - #### Contents of the preconfiguration file (for squeeze) ### Localization # Locale sets language and country. diff --git a/tools/appliance/definitions/systemvmtemplate/zerodisk.sh b/tools/appliance/definitions/systemvmtemplate/zerodisk.sh index fef5b705572..43a86472ea1 100644 --- a/tools/appliance/definitions/systemvmtemplate/zerodisk.sh +++ b/tools/appliance/definitions/systemvmtemplate/zerodisk.sh @@ -1,20 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - #clean up stuff copied in by veewee rm -f /root/* diff --git a/tools/devcloud/src/deps/boxes/basebox-build/definition.rb b/tools/devcloud/src/deps/boxes/basebox-build/definition.rb index 24668421c5d..f7e3c114993 100644 --- a/tools/devcloud/src/deps/boxes/basebox-build/definition.rb +++ b/tools/devcloud/src/deps/boxes/basebox-build/definition.rb @@ -1,20 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - Veewee::Session.declare({ :cpu_count => '1', :memory_size=> '2048', diff --git a/tools/devcloud/src/deps/boxes/basebox-build/preseed.cfg b/tools/devcloud/src/deps/boxes/basebox-build/preseed.cfg index 00bae613647..dd6a24a3b65 100644 --- a/tools/devcloud/src/deps/boxes/basebox-build/preseed.cfg +++ b/tools/devcloud/src/deps/boxes/basebox-build/preseed.cfg @@ -1,20 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - ## Options to set on the command line d-i debian-installer/locale string en_US.utf8 d-i console-setup/ask_detect boolean false diff --git a/tools/whisker/descriptor.xml b/tools/whisker/descriptor.xml index 5f324a5c28f..8e39586501d 100644 --- a/tools/whisker/descriptor.xml +++ b/tools/whisker/descriptor.xml @@ -2423,6 +2423,10 @@ Innovation Centre, 2006 (http://www.it-innovation.soton.ac.uk). id='adiscon.com' name='Adiscon GmbH' url='http://www.adiscon.com/' /> + Copyright (c) 2013 The Apache Software Foundation @@ -2653,5 +2657,30 @@ Copyright (c) 2010-2011 OpenStack, LLC. - + + + + +Copyright (c) 2010-2012 Patrick Debois + + + + + + + + + + + + + +Copyright (c) 2010-2012 Patrick Debois + + + + + + + From 733ec50d46bf934fe0e83771029d2c5845cd9a97 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 12 Feb 2013 12:17:25 +0530 Subject: [PATCH 06/42] maven: don't use process-test-resources lifecycles, target are skipped if test are Signed-off-by: Rohit Yadav --- developer/pom.xml | 4 ++-- tools/devcloud-kvm/pom.xml | 2 +- tools/devcloud/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/developer/pom.xml b/developer/pom.xml index dba7b383d69..79b24665542 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -130,7 +130,7 @@ - process-test-resources + process-resources create-schema java @@ -259,7 +259,7 @@ - process-test-resources + process-resources create-schema java diff --git a/tools/devcloud-kvm/pom.xml b/tools/devcloud-kvm/pom.xml index f29e8375219..e90a257ccee 100644 --- a/tools/devcloud-kvm/pom.xml +++ b/tools/devcloud-kvm/pom.xml @@ -94,7 +94,7 @@ create-schema - process-test-resources + process-resources execute diff --git a/tools/devcloud/pom.xml b/tools/devcloud/pom.xml index 8a7a7ca1f8b..cbf93a4918f 100644 --- a/tools/devcloud/pom.xml +++ b/tools/devcloud/pom.xml @@ -94,7 +94,7 @@ create-schema - process-test-resources + process-resources execute From c809d057efd42bf01d585326e09e51e0c0ff1cee Mon Sep 17 00:00:00 2001 From: Murali Reddy Date: Tue, 12 Feb 2013 15:25:07 +0530 Subject: [PATCH 07/42] CLOUDSTACK-1241: Network apply rules logic is broken added logic to check if the network element is configured service provider for the network before applying rules --- .../network/firewall/FirewallManagerImpl.java | 20 +++++++++++++++++++ .../lb/LoadBalancingRulesManagerImpl.java | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index d3b4c0beabf..0d17ba720a9 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -536,12 +536,22 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, switch (purpose){ case Firewall: for (FirewallServiceProvider fwElement: _firewallElements) { + Network.Provider provider = fwElement.getProvider(); + boolean isFwProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Firewall, provider); + if (!isFwProvider) { + continue; + } handled = fwElement.applyFWRules(network, rules); if (handled) break; } case PortForwarding: for (PortForwardingServiceProvider element: _pfElements) { + Network.Provider provider = element.getProvider(); + boolean isPfProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.PortForwarding, provider); + if (!isPfProvider) { + continue; + } handled = element.applyPFRules(network, (List) rules); if (handled) break; @@ -549,6 +559,11 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, break; case StaticNat: for (StaticNatServiceProvider element: _staticNatElements) { + Network.Provider provider = element.getProvider(); + boolean isSnatProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.StaticNat, provider); + if (!isSnatProvider) { + continue; + } handled = element.applyStaticNats(network, (List) rules); if (handled) break; @@ -556,6 +571,11 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, break; case NetworkACL: for (NetworkACLServiceProvider element: _networkAclElements) { + Network.Provider provider = element.getProvider(); + boolean isAclProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.NetworkACL, provider); + if (!isAclProvider) { + continue; + } handled = element.applyNetworkACLs(network, rules); if (handled) break; diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 85e850c0b5a..531a42805b6 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -1158,6 +1158,11 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements assert(purpose == Purpose.LoadBalancing): "LB Manager asked to handle non-LB rules"; boolean handled = false; for (LoadBalancingServiceProvider lbElement: _lbProviders) { + Provider provider = lbElement.getProvider(); + boolean isLbProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Lb, provider); + if (!isLbProvider) { + continue; + } handled = lbElement.applyLBRules(network, (List) rules); if (handled) break; From bd034e0b9f471ca1d0c3a18e135926380dc76e1c Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 12 Feb 2013 17:54:54 +0530 Subject: [PATCH 08/42] CLOUDSTACK-1243: Add @Inject to fix NPE in AccountManagerImpl Signed-off-by: Rohit Yadav --- server/src/com/cloud/user/AccountManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 54447a2f176..a3f9505c3df 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -218,7 +218,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M private IPAddressDao _ipAddressDao; @Inject private RegionManager _regionMgr; - + @Inject private VpcManager _vpcMgr; @Inject private DomainRouterDao _routerDao; From 4b268c4ae68413b3a53173c3f1035b9539f682af Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 12 Feb 2013 17:56:30 +0530 Subject: [PATCH 09/42] CLOUDSTACK-1066: Add random password for cloud, fix trailing whitespaces, cleanup isos Signed-off-by: Rohit Yadav --- .../definitions/systemvmtemplate/cleanup.sh | 3 ++ .../systemvmtemplate/cloudstack-packages.sh | 46 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/tools/appliance/definitions/systemvmtemplate/cleanup.sh b/tools/appliance/definitions/systemvmtemplate/cleanup.sh index c6a1e9068d6..abecc56dcb5 100644 --- a/tools/appliance/definitions/systemvmtemplate/cleanup.sh +++ b/tools/appliance/definitions/systemvmtemplate/cleanup.sh @@ -18,3 +18,6 @@ rm /lib/udev/rules.d/75-persistent-net-generator.rules echo "Adding a 2 sec delay to the interface up, to make the dhclient happy" echo "pre-up sleep 2" >> /etc/network/interfaces +# Clean up any copied iso or scripts +rm -v /root/*.iso +rm -v /root/*.sh diff --git a/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh b/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh index 9a402535463..320a515d446 100644 --- a/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh +++ b/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh @@ -26,10 +26,10 @@ install_packages() { DEBIAN_PRIORITY=critical #basic stuff - apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables - apt-get --no-install-recommends -q -y --force-yes install openssh-server grub-legacy e2fsprogs dhcp3-client dnsmasq tcpdump socat wget - apt-get --no-install-recommends -q -y --force-yes install python bzip2 sed gawk diffutils grep gzip less tar telnet ftp rsync traceroute psmisc lsof procps monit inetutils-ping iputils-arping httping - apt-get --no-install-recommends -q -y --force-yes install dnsutils zip unzip ethtool uuid file iproute acpid virt-what sudo + apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables + apt-get --no-install-recommends -q -y --force-yes install openssh-server openssl grub-legacy e2fsprogs dhcp3-client dnsmasq tcpdump socat wget + apt-get --no-install-recommends -q -y --force-yes install python bzip2 sed gawk diffutils grep gzip less tar telnet ftp rsync traceroute psmisc lsof procps monit inetutils-ping iputils-arping httping + apt-get --no-install-recommends -q -y --force-yes install dnsutils zip unzip ethtool uuid file iproute acpid virt-what sudo #sysstat echo 'sysstat sysstat/enable boolean true' | debconf-set-selections @@ -65,21 +65,20 @@ install_packages() { apt-get --no-install-recommends -q -y --force-yes install iptables-persistent } -accounts() { +setup_accounts() { # Setup sudo to allow no-password sudo for "admin" groupadd -r admin #create a 'cloud' user useradd -G admin cloud - echo "root:$PASSWORD" | chpasswd - #FIXME: create random password for cloud - #FIXME: disable password auth in sshd (final step, after veewee is done) + echo "root:$ROOTPW" | chpasswd + echo "cloud:`openssl rand -base64 32`" | chpasswd + #FIXME: disable password auth in sshd (final step, after veewee is done) #echo "cloud:password" | chpasswd sed -i -e '/Defaults\s\+env_reset/a Defaults\texempt_group=admin' /etc/sudoers sed -i -e 's/%admin ALL=(ALL) ALL/%admin ALL=NOPASSWD:ALL/g' /etc/sudoers - + mkdir -p /home/cloud/.ssh chmod 700 /home/cloud/.ssh - } fix_nameserver() { @@ -98,7 +97,7 @@ do_fixes() { echo "$HOSTNAME" > /etc/hostname hostname $HOSTNAME #delete entry in /etc/hosts derived from dhcp - sed -i '/127.0.1.1/d' /etc/hosts + sed -i '/127.0.1.1/d' /etc/hosts #fix_nameserver FIXME needed after veewee finishes } @@ -112,7 +111,7 @@ configure_apache2() { cp /etc/apache2/sites-available/default-ssl /etc/apache2/sites-available/default-ssl.orig } -services() { +configure_services() { mkdir -p /var/www/html mkdir -p /opt/cloud/bin mkdir -p /var/cache/cloud @@ -121,12 +120,12 @@ services() { mkdir -p /root/.ssh #Fix haproxy directory issue mkdir -p /var/lib/haproxy - - wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-early-config;hb=HEAD' -O /etc/init.d/cloud-early-config + + wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-early-config;hb=HEAD' -O /etc/init.d/cloud-early-config chkconfig --add cloud-early-config chkconfig cloud-early-config on wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-passwd-srvr;hb=HEAD' -O /etc/init.d/cloud-passwd-srvr - chkconfig --add cloud-passwd-srvr + chkconfig --add cloud-passwd-srvr chkconfig cloud-passwd-srvr off wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud;hb=HEAD' -O /etc/init.d/cloud chkconfig --add cloud @@ -135,18 +134,25 @@ services() { chkconfig xl2tpd off } -signature() { +do_signature() { mkdir -p /var/cache/cloud/ touch /var/cache/cloud/cloud-scripts-signature #FIXME: signature should be generated from scripts package that can get updated echo "Cloudstack Release $CLOUDSTACK_RELEASE $(date)" > /etc/cloudstack-release } +begin=$(date +%s) + echo "*************INSTALLING PACKAGES********************" install_packages echo "*************DONE INSTALLING PACKAGES********************" -accounts -do_fixes +setup_accounts configure_apache2 -services -signature +configure_services +do_fixes +do_signature + +fin=$(date +%s) +t=$((fin-begin)) + +echo "Finished building systemvm appliance in $t seconds" From 98c0a4fb9d242e2ffd5f3b6554cea034946a55cf Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 12 Feb 2013 18:28:55 +0530 Subject: [PATCH 10/42] CLOUDSTACK-1066: Fix FIXMEs, rename script to postinstall.sh Signed-off-by: Rohit Yadav --- .../systemvmtemplate/cloudstack-packages.sh | 158 ----------------- .../systemvmtemplate/definition.rb | 2 +- .../systemvmtemplate/postinstall.sh | 163 +++++++++--------- .../definitions/systemvmtemplate/zerodisk.sh | 3 + 4 files changed, 90 insertions(+), 236 deletions(-) delete mode 100644 tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh diff --git a/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh b/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh deleted file mode 100644 index 320a515d446..00000000000 --- a/tools/appliance/definitions/systemvmtemplate/cloudstack-packages.sh +++ /dev/null @@ -1,158 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -ROOTPW=password -HOSTNAME=systemvm -CLOUDSTACK_RELEASE=4.2.0 - - -install_packages() { - DEBIAN_FRONTEND=noninteractive - DEBIAN_PRIORITY=critical - - #basic stuff - apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables - apt-get --no-install-recommends -q -y --force-yes install openssh-server openssl grub-legacy e2fsprogs dhcp3-client dnsmasq tcpdump socat wget - apt-get --no-install-recommends -q -y --force-yes install python bzip2 sed gawk diffutils grep gzip less tar telnet ftp rsync traceroute psmisc lsof procps monit inetutils-ping iputils-arping httping - apt-get --no-install-recommends -q -y --force-yes install dnsutils zip unzip ethtool uuid file iproute acpid virt-what sudo - - #sysstat - echo 'sysstat sysstat/enable boolean true' | debconf-set-selections - apt-get --no-install-recommends -q -y --force-yes install sysstat - #apache - apt-get --no-install-recommends -q -y --force-yes install apache2 ssl-cert - #haproxy - apt-get --no-install-recommends -q -y --force-yes install haproxy - #dnsmasq - apt-get --no-install-recommends -q -y --force-yes install dnsmasq - #nfs client - apt-get --no-install-recommends -q -y --force-yes install nfs-common - - #vpn stuff - apt-get --no-install-recommends -q -y --force-yes install xl2tpd bcrelay ppp ipsec-tools tdb-tools - echo "openswan openswan/install_x509_certificate boolean false" | debconf-set-selections - echo "openswan openswan/install_x509_certificate seen true" | debconf-set-selections - apt-get --no-install-recommends -q -y --force-yes install openswan - - #vmware tools - apt-get --no-install-recommends -q -y --force-yes install open-vm-tools - #xenstore utils - apt-get --no-install-recommends -q -y --force-yes install xenstore-utils libxenstore3.0 - #keepalived and conntrackd for redundant router - apt-get --no-install-recommends -q -y --force-yes install keepalived conntrackd ipvsadm libnetfilter-conntrack3 libnl1 - #ipcalc - apt-get --no-install-recommends -q -y --force-yes install ipcalc - #java - apt-get --no-install-recommends -q -y --force-yes install default-jre-headless - - echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | debconf-set-selections - echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | debconf-set-selections - apt-get --no-install-recommends -q -y --force-yes install iptables-persistent -} - -setup_accounts() { - # Setup sudo to allow no-password sudo for "admin" - groupadd -r admin - #create a 'cloud' user - useradd -G admin cloud - echo "root:$ROOTPW" | chpasswd - echo "cloud:`openssl rand -base64 32`" | chpasswd - #FIXME: disable password auth in sshd (final step, after veewee is done) - #echo "cloud:password" | chpasswd - sed -i -e '/Defaults\s\+env_reset/a Defaults\texempt_group=admin' /etc/sudoers - sed -i -e 's/%admin ALL=(ALL) ALL/%admin ALL=NOPASSWD:ALL/g' /etc/sudoers - - mkdir -p /home/cloud/.ssh - chmod 700 /home/cloud/.ssh -} - -fix_nameserver() { - #replace /etc/resolv.conf also - cat > /etc/resolv.conf << EOF -nameserver 8.8.8.8 -nameserver 4.4.4.4 -EOF - -} - -do_fixes() { - #fix hostname in openssh-server generated keys - sed -i "s/root@\(.*\)$/root@$HOSTNAME/g" /etc/ssh/ssh_host_*.pub - #fix hostname to override one provided by dhcp during vm build - echo "$HOSTNAME" > /etc/hostname - hostname $HOSTNAME - #delete entry in /etc/hosts derived from dhcp - sed -i '/127.0.1.1/d' /etc/hosts - - #fix_nameserver FIXME needed after veewee finishes -} - -configure_apache2() { - #enable ssl, rewrite and auth - a2enmod ssl rewrite auth_basic auth_digest - a2ensite default-ssl - #backup stock apache configuration since we may modify it in Secondary Storage VM - cp /etc/apache2/sites-available/default /etc/apache2/sites-available/default.orig - cp /etc/apache2/sites-available/default-ssl /etc/apache2/sites-available/default-ssl.orig -} - -configure_services() { - mkdir -p /var/www/html - mkdir -p /opt/cloud/bin - mkdir -p /var/cache/cloud - mkdir -p /usr/share/cloud - mkdir -p /usr/local/cloud - mkdir -p /root/.ssh - #Fix haproxy directory issue - mkdir -p /var/lib/haproxy - - wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-early-config;hb=HEAD' -O /etc/init.d/cloud-early-config - chkconfig --add cloud-early-config - chkconfig cloud-early-config on - wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-passwd-srvr;hb=HEAD' -O /etc/init.d/cloud-passwd-srvr - chkconfig --add cloud-passwd-srvr - chkconfig cloud-passwd-srvr off - wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud;hb=HEAD' -O /etc/init.d/cloud - chkconfig --add cloud - chkconfig cloud off - chkconfig monit off - chkconfig xl2tpd off -} - -do_signature() { - mkdir -p /var/cache/cloud/ - touch /var/cache/cloud/cloud-scripts-signature - #FIXME: signature should be generated from scripts package that can get updated - echo "Cloudstack Release $CLOUDSTACK_RELEASE $(date)" > /etc/cloudstack-release -} - -begin=$(date +%s) - -echo "*************INSTALLING PACKAGES********************" -install_packages -echo "*************DONE INSTALLING PACKAGES********************" -setup_accounts -configure_apache2 -configure_services -do_fixes -do_signature - -fin=$(date +%s) -t=$((fin-begin)) - -echo "Finished building systemvm appliance in $t seconds" diff --git a/tools/appliance/definitions/systemvmtemplate/definition.rb b/tools/appliance/definitions/systemvmtemplate/definition.rb index 86f555507e4..a839182bbd9 100644 --- a/tools/appliance/definitions/systemvmtemplate/definition.rb +++ b/tools/appliance/definitions/systemvmtemplate/definition.rb @@ -37,7 +37,7 @@ Veewee::Definition.declare({ :shutdown_cmd => "halt -p", :postinstall_files => [ "base.sh", - "cloudstack-packages.sh", + "postinstall.sh", "cleanup.sh", "zerodisk.sh" ], diff --git a/tools/appliance/definitions/systemvmtemplate/postinstall.sh b/tools/appliance/definitions/systemvmtemplate/postinstall.sh index 40064325a2b..97de81abbb7 100644 --- a/tools/appliance/definitions/systemvmtemplate/postinstall.sh +++ b/tools/appliance/definitions/systemvmtemplate/postinstall.sh @@ -15,55 +15,21 @@ # specific language governing permissions and limitations # under the License. -set -e set -x -IMAGENAME=systemvm -LOCATION=/var/lib/images/systemvm -PASSWORD=password +ROOTPW=password HOSTNAME=systemvm -SIZE=2048 -DEBIAN_MIRROR=ftp.us.debian.org/debian -MINIMIZE=true -CLOUDSTACK_RELEASE=4.1.0 - -init() { - # Update the box - apt-get -y update - apt-get -y install linux-headers-$(uname -r) build-essential - apt-get -y install zlib1g-dev libssl-dev libreadline-gplv2-dev - apt-get -y install curl unzip - apt-get clean - - # Set up sudo - echo 'vagrant ALL=NOPASSWD:ALL' > /etc/sudoers.d/vagrant - - # Tweak sshd to prevent DNS resolution (speed up logins) - echo 'UseDNS no' >> /etc/ssh/sshd_config - - # Remove 5s grub timeout to speed up booting - echo < /etc/default/grub -# If you change this file, run 'update-grub' afterwards to update -# /boot/grub/grub.cfg. - -GRUB_DEFAULT=0 -GRUB_TIMEOUT=0 -GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` -GRUB_CMDLINE_LINUX_DEFAULT="quiet" -GRUB_CMDLINE_LINUX="debian-installer=en_US" -EOF - - update-grub -} +CLOUDSTACK_RELEASE=4.2.0 install_packages() { DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical #basic stuff - apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables openssh-server grub-legacy e2fsprogs dhcp3-client dnsmasq tcpdump socat wget python bzip2 sed gawk diff grep gzip less tar telnet ftp rsync traceroute psmisc lsof procps monit inetutils-ping iputils-arping httping dnsutils zip unzip ethtool uuid file iproute acpid iptables-persistent virt-what sudo - #fix hostname in openssh-server generated keys - sed -i "s/root@\(.*\)$/root@systemvm/g" etc/ssh/ssh_host_*.pub + apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables + apt-get --no-install-recommends -q -y --force-yes install openssh-server openssl grub-legacy e2fsprogs dhcp3-client dnsmasq tcpdump socat wget + apt-get --no-install-recommends -q -y --force-yes install python bzip2 sed gawk diffutils grep gzip less tar telnet ftp rsync traceroute psmisc lsof procps monit inetutils-ping iputils-arping httping + apt-get --no-install-recommends -q -y --force-yes install dnsutils zip unzip ethtool uuid file iproute acpid virt-what sudo #sysstat echo 'sysstat sysstat/enable boolean true' | debconf-set-selections @@ -76,72 +42,115 @@ install_packages() { apt-get --no-install-recommends -q -y --force-yes install dnsmasq #nfs client apt-get --no-install-recommends -q -y --force-yes install nfs-common + #vpn stuff - apt-get --no-install-recommends -q -y --force-yes install xl2tpd openswan bcrelay ppp ipsec-tools tdb-tools + apt-get --no-install-recommends -q -y --force-yes install xl2tpd bcrelay ppp ipsec-tools tdb-tools + echo "openswan openswan/install_x509_certificate boolean false" | debconf-set-selections + echo "openswan openswan/install_x509_certificate seen true" | debconf-set-selections + apt-get --no-install-recommends -q -y --force-yes install openswan + #vmware tools apt-get --no-install-recommends -q -y --force-yes install open-vm-tools #xenstore utils apt-get --no-install-recommends -q -y --force-yes install xenstore-utils libxenstore3.0 - #keepalived and conntrackd + #keepalived and conntrackd for redundant router apt-get --no-install-recommends -q -y --force-yes install keepalived conntrackd ipvsadm libnetfilter-conntrack3 libnl1 #ipcalc apt-get --no-install-recommends -q -y --force-yes install ipcalc #java apt-get --no-install-recommends -q -y --force-yes install default-jre-headless + echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | debconf-set-selections + echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | debconf-set-selections + apt-get --no-install-recommends -q -y --force-yes install iptables-persistent +} + +setup_accounts() { # Setup sudo to allow no-password sudo for "admin" groupadd -r admin - usermod -a -G admin cloud - echo "root:password" | chpasswd + #create a 'cloud' user + useradd -G admin cloud + echo "root:$ROOTPW" | chpasswd + echo "cloud:`openssl rand -base64 32`" | chpasswd sed -i -e '/Defaults\s\+env_reset/a Defaults\texempt_group=admin' /etc/sudoers sed -i -e 's/%admin ALL=(ALL) ALL/%admin ALL=NOPASSWD:ALL/g' /etc/sudoers - - mkdir /home/cloud/.ssh + # Disable password based authentication via ssh, this will take effect on next reboot + sed -i -e 's/^.*PasswordAuthentication .*$/PasswordAuthentication no/g' /etc/ssh/sshd_config + # Secure ~/.ssh + mkdir -p /home/cloud/.ssh chmod 700 /home/cloud/.ssh +} + +fix_nameserver() { + #replace /etc/resolv.conf also + cat > /etc/resolv.conf << EOF +nameserver 8.8.8.8 +nameserver 4.4.4.4 +EOF } -cleanup() { - # Clean up - apt-get -y remove linux-headers-$(uname -r) build-essential - apt-get -y autoremove +do_fixes() { + #fix hostname in openssh-server generated keys + sed -i "s/root@\(.*\)$/root@$HOSTNAME/g" /etc/ssh/ssh_host_*.pub + #fix hostname to override one provided by dhcp during vm build + echo "$HOSTNAME" > /etc/hostname + hostname $HOSTNAME + #delete entry in /etc/hosts derived from dhcp + sed -i '/127.0.1.1/d' /etc/hosts - # Removing leftover leases and persistent rules - echo "cleaning up dhcp leases" - rm /var/lib/dhcp/* - - # Make sure Udev doesn't block our network - echo "cleaning up udev rules" - rm /etc/udev/rules.d/70-persistent-net.rules - mkdir /etc/udev/rules.d/70-persistent-net.rules - rm -rf /dev/.udev/ - rm /lib/udev/rules.d/75-persistent-net-generator.rules - - echo "Adding a 2 sec delay to the interface up, to make the dhclient happy" - echo "pre-up sleep 2" >> /etc/network/interfaces + fix_nameserver } -finalize() { - # Zero out the free space to save space in the final image: - dd if=/dev/zero of=/EMPTY bs=1M - rm -f /EMPTY +configure_apache2() { + #enable ssl, rewrite and auth + a2enmod ssl rewrite auth_basic auth_digest + a2ensite default-ssl + #backup stock apache configuration since we may modify it in Secondary Storage VM + cp /etc/apache2/sites-available/default /etc/apache2/sites-available/default.orig + cp /etc/apache2/sites-available/default-ssl /etc/apache2/sites-available/default-ssl.orig } +configure_services() { + mkdir -p /var/www/html + mkdir -p /opt/cloud/bin + mkdir -p /var/cache/cloud + mkdir -p /usr/share/cloud + mkdir -p /usr/local/cloud + mkdir -p /root/.ssh + #Fix haproxy directory issue + mkdir -p /var/lib/haproxy + + wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-early-config;hb=HEAD' -O /etc/init.d/cloud-early-config + chkconfig --add cloud-early-config + chkconfig cloud-early-config on + wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud-passwd-srvr;hb=HEAD' -O /etc/init.d/cloud-passwd-srvr + chkconfig --add cloud-passwd-srvr + chkconfig cloud-passwd-srvr off + wget 'https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=patches/systemvm/debian/config/etc/init.d/cloud;hb=HEAD' -O /etc/init.d/cloud + chkconfig --add cloud + chkconfig cloud off + chkconfig monit off + chkconfig xl2tpd off +} + +do_signature() { + mkdir -p /var/cache/cloud/ + touch /var/cache/cloud/cloud-scripts-signature + #FIXME: signature should be generated from scripts package that can get updated + echo "Cloudstack Release $CLOUDSTACK_RELEASE $(date)" > /etc/cloudstack-release +} -echo "*************STARTING POSTINST SCRIPT********************" begin=$(date +%s) -echo "*************INITIALIZING BASE SYSTEM********************" -init - echo "*************INSTALLING PACKAGES********************" install_packages - -echo "*************CLEANING UP********************" -cleanup - -echo "*************FINALIZING IMAGE********************" -finalize +echo "*************DONE INSTALLING PACKAGES********************" +setup_accounts +configure_apache2 +configure_services +do_fixes +do_signature fin=$(date +%s) t=$((fin-begin)) diff --git a/tools/appliance/definitions/systemvmtemplate/zerodisk.sh b/tools/appliance/definitions/systemvmtemplate/zerodisk.sh index 43a86472ea1..6ad4205033e 100644 --- a/tools/appliance/definitions/systemvmtemplate/zerodisk.sh +++ b/tools/appliance/definitions/systemvmtemplate/zerodisk.sh @@ -4,3 +4,6 @@ rm -f /root/* # Zero out the free space to save space in the final image: dd if=/dev/zero of=/EMPTY bs=1M rm -f /EMPTY + +# Shutdown the appliance, now export it to required image format +shutdown -h now From 28622b5e6f2eee48e237cd949ddf431472399d3a Mon Sep 17 00:00:00 2001 From: Hugo Trippaers Date: Tue, 12 Feb 2013 16:16:36 +0100 Subject: [PATCH 11/42] Don't forget the environment.properties when packaging (cherry picked from commit 5594b1d622adb07203d4549aee426268ecee092d) Signed-off-by: Hugo Trippaers --- packaging/centos63/cloud.spec | 3 ++- packaging/centos63/replace.properties | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index d8143214b6b..2577cd0e58b 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -222,7 +222,7 @@ rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/cl rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/vms for name in db.properties log4j-cloud.xml tomcat6-nonssl.conf tomcat6-ssl.conf server-ssl.xml server-nonssl.xml \ - catalina.policy catalina.properties db-enc.properties classpath.conf tomcat-users.xml web.xml ; do + catalina.policy catalina.properties db-enc.properties classpath.conf tomcat-users.xml web.xml environment.properties ; do mv ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/$name \ ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/$name done @@ -336,6 +336,7 @@ fi %config(noreplace) %{_sysconfdir}/%{name}/management/server-ssl.xml %config(noreplace) %{_sysconfdir}/%{name}/management/tomcat-users.xml %config(noreplace) %{_sysconfdir}/%{name}/management/web.xml +%config(noreplace) %{_sysconfdir}/%{name}/management/environment.properties %attr(0755,root,root) %{_initrddir}/%{name}-management %attr(0755,root,root) %{_bindir}/%{name}-setup-management %attr(0755,root,root) %{_bindir}/%{name}-update-xenserver-licenses diff --git a/packaging/centos63/replace.properties b/packaging/centos63/replace.properties index bcc4d4a0087..211cc95449f 100644 --- a/packaging/centos63/replace.properties +++ b/packaging/centos63/replace.properties @@ -21,7 +21,6 @@ DBROOTPW= MSLOG=vmops.log APISERVERLOG=api.log DBHOST=localhost -MSMNTDIR=/mnt COMPONENTS-SPEC=components-premium.xml AWSAPILOG=awsapi.log REMOTEHOST=localhost @@ -45,7 +44,7 @@ MSCONF=/etc/cloudstack/management MSENVIRON=/usr/share/cloudstack-management MSLOG=/var/log/cloudstack/management/management-server.log MSLOGDIR=/var/log/cloudstack/management/ -MSMNTDIR=/var/lib/cloud/mnt +MSMNTDIR=/var/cloudstack/mnt MSUSER=cloud PIDDIR=/var/run PLUGINJAVADIR=/usr/share/cloudstack-management/plugin From 03b8181dcb8f7433204caea5ae69ce1c0a37a9de Mon Sep 17 00:00:00 2001 From: Logan McNaughton Date: Mon, 4 Feb 2013 15:10:36 -0500 Subject: [PATCH 12/42] CLOUDSTACK-1106: Fix documentation for cloud-setup-databases Signed-off-by: Rohit Yadav --- docs/en-US/management-server-install-db-external.xml | 7 ++++++- docs/en-US/management-server-install-db-local.xml | 9 +++++++-- docs/en-US/management-server-install-multi-node.xml | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/en-US/management-server-install-db-external.xml b/docs/en-US/management-server-install-db-external.xml index 3bba45f3ee1..a28dee56934 100644 --- a/docs/en-US/management-server-install-db-external.xml +++ b/docs/en-US/management-server-install-db-external.xml @@ -127,12 +127,17 @@ bind-address = 0.0.0.0 recommended that you replace this with a more secure value. See . + + (Optional) For management_server_ip, you may explicitly specify cluster management + server node IP. If not specified, the local IP address will be used. + cloud-setup-databases cloud:<dbpassword>@<ip address mysql server> \ --deploy-as=root:<password> \ -e <encryption_type> \ -m <management_server_key> \ --k <database_key> +-k <database_key> \ +-i <management_server_ip> When this script is finished, you should see a message like “Successfully initialized the database.” diff --git a/docs/en-US/management-server-install-db-local.xml b/docs/en-US/management-server-install-db-local.xml index 3e09c554df0..242249040b1 100644 --- a/docs/en-US/management-server-install-db-local.xml +++ b/docs/en-US/management-server-install-db-local.xml @@ -98,12 +98,17 @@ binlog-format = 'ROW' recommended that you replace this with a more secure value. See . + + (Optional) For management_server_ip, you may explicitly specify cluster management + server node IP. If not specified, the local IP address will be used. + cloud-setup-databases cloud:<dbpassword>@localhost \ --deploy-as=root:<password> \ -e <encryption_type> \ -m <management_server_key> \ --k <database_key> +-k <database_key> \ +-i <management_server_ip> When this script is finished, you should see a message like “Successfully initialized the database.” @@ -118,7 +123,7 @@ binlog-format = 'ROW' Now that the database is set up, you can finish configuring the OS for the Management Server. This command will set up iptables, sudoers, and start the Management Server. - # cloud-setup-management + # cloud-setup-management You should see the message “&PRODUCT; Management Server setup is done.” diff --git a/docs/en-US/management-server-install-multi-node.xml b/docs/en-US/management-server-install-multi-node.xml index e61f6230ff0..3f011b83b87 100644 --- a/docs/en-US/management-server-install-multi-node.xml +++ b/docs/en-US/management-server-install-multi-node.xml @@ -53,7 +53,7 @@ linkend="sect-source-buildrpm"/> or as Configure the database client. Note the absence of the --deploy-as argument in this case. (For more details about the arguments to this command, see .) - # cloud-setup-databases cloud:dbpassword@dbhost -e encryption_type -m management_server_key -k database_key + # cloud-setup-databases cloud:dbpassword@dbhost -e encryption_type -m management_server_key -k database_key -i management_server_ip @@ -69,4 +69,4 @@ linkend="sect-source-buildrpm"/> or as Load Balancing. - \ No newline at end of file + From 1e24892dfe6dfcd60372d901bea71831ebea44ea Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 12 Feb 2013 09:22:04 -0800 Subject: [PATCH 13/42] If filesystem in virtual router is in read-only state, reports error to commands send to virtual router, instead of keeping silence. Test: Before change: (1) Acquire IP. always in "Allocating" state. (2) EnableStaticNat, the result is success(it is incorrect). (3) DisableStaticNat, will get error message.. This is correct. (4) Add Firewalls. always in "Adding" state. (5) The AgentManager report statistics every 60 minutes(normally it should be router.stats.interval=5 minutes). After change: (1) Acquire IP, will get error message. (2) EnableStaticNat, will get error message. (3) DisableStaticNat, will get error message. (4) Add Firewalls, will get error message. But the firewall rules are saved in database. (5) The AgentManager report statistics every 5 minutes, except the network with read-only FS virtual router. --- patches/systemvm/debian/config/root/func.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/patches/systemvm/debian/config/root/func.sh b/patches/systemvm/debian/config/root/func.sh index 4047a4047a6..86317a06843 100644 --- a/patches/systemvm/debian/config/root/func.sh +++ b/patches/systemvm/debian/config/root/func.sh @@ -42,7 +42,11 @@ getLockFile() { psline=`ps u $$` echo $psline > $__LOCKFILE - + if [ ! -e $__LOCKFILE ] + then + return + fi + for i in `seq 1 $(($__TIMEOUT * 10))` do currlock=`ls -tr /tmp/$1-*.lock | head -n1` From 5828e526b3189f53cc13d56b203fcbc822113cb2 Mon Sep 17 00:00:00 2001 From: Sanjay Tripathi Date: Fri, 18 Jan 2013 16:04:13 +0530 Subject: [PATCH 14/42] CLOUDSTACK-713: Limit Resources(CPU and Memory) to domain/accounts Addition of two new resource types i.e. CPU and Memory in the existing pool of resource types. Added some methods to set the limits on these resources using updateResourceLimit API command and to get a count using updateResourceCount. Also added calls in the Virtual machine life cycle to check these limits and to increment/decrement the new resource types Resource Name :: Resource type number CPU 8 Memory 9 Also added Unit Tests for the same. --- api/src/com/cloud/configuration/Resource.java | 4 +- .../user/resource/UpdateResourceCountCmd.java | 9 +- .../user/resource/UpdateResourceLimitCmd.java | 8 +- .../api/response/AccountResponse.java | 42 ++++++++++ .../api/response/ResourceCountResponse.java | 2 +- .../api/response/ResourceLimitResponse.java | 2 +- .../api/query/dao/AccountJoinDaoImpl.java | 18 ++++ .../com/cloud/api/query/vo/AccountJoinVO.java | 54 +++++++++++- .../baremetal/BareMetalVmManagerImpl.java | 10 +-- .../src/com/cloud/configuration/Config.java | 4 + .../ResourceLimitManagerImpl.java | 69 +++++++++++++++- .../src/com/cloud/vm/UserVmManagerImpl.java | 82 +++++++++++++------ .../ResourceLimitManagerImplTest.java | 76 +++++++++++++++++ .../vpc/MockResourceLimitManagerImpl.java | 17 +++- setup/db/create-schema-view.sql | 16 ++++ setup/db/db/schema-40to410.sql | 24 ++++++ 16 files changed, 395 insertions(+), 42 deletions(-) create mode 100644 server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java diff --git a/api/src/com/cloud/configuration/Resource.java b/api/src/com/cloud/configuration/Resource.java index 7f551d6b52c..7614c8a4b43 100644 --- a/api/src/com/cloud/configuration/Resource.java +++ b/api/src/com/cloud/configuration/Resource.java @@ -28,7 +28,9 @@ public interface Resource { template("template", 4, ResourceOwnerType.Account, ResourceOwnerType.Domain), project("project", 5, ResourceOwnerType.Account, ResourceOwnerType.Domain), network("network", 6, ResourceOwnerType.Account, ResourceOwnerType.Domain), - vpc("vpc", 7, ResourceOwnerType.Account, ResourceOwnerType.Domain); + vpc("vpc", 7, ResourceOwnerType.Account, ResourceOwnerType.Domain), + cpu("cpu", 8, ResourceOwnerType.Account, ResourceOwnerType.Domain), + memory("memory", 9, ResourceOwnerType.Account, ResourceOwnerType.Domain); private String name; private ResourceOwnerType[] supportedOwners; diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java index 91728ee9cf6..f6d3a98a05d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java @@ -53,12 +53,17 @@ public class UpdateResourceCountCmd extends BaseCmd { required=true, description="If account parameter specified then updates resource counts for a specified account in this domain else update resource counts for all accounts & child domains in specified domain.") private Long domainId; - @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, description= "Type of resource to update. If specifies valid values are 0, 1, 2, 3, and 4. If not specified will update all resource counts" + + @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, description= "Type of resource to update. If specifies valid values are 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9. If not specified will update all resource counts" + "0 - Instance. Number of instances a user can create. " + "1 - IP. Number of public IP addresses a user can own. " + "2 - Volume. Number of disk volumes a user can create." + "3 - Snapshot. Number of snapshots a user can create." + - "4 - Template. Number of templates that a user can register/create.") + "4 - Template. Number of templates that a user can register/create." + + "5 - Project. Number of projects that a user can create." + + "6 - Network. Number of guest network a user can create." + + "7 - VPC. Number of VPC a user can create." + + "8 - CPU. Total number of CPU cores a user can use." + + "9 - Memory. Total Memory (in MB) a user can use." ) private Integer resourceType; @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java index 33f2574d3e1..0039f6293f7 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java @@ -54,11 +54,15 @@ public class UpdateResourceLimitCmd extends BaseCmd { @Parameter(name=ApiConstants.MAX, type=CommandType.LONG, description=" Maximum resource limit.") private Long max; - @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, required=true, description="Type of resource to update. Values are 0, 1, 2, 3, and 4. 0 - Instance. Number of instances a user can create. " + + @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, required=true, description="Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8 and 9. 0 - Instance. Number of instances a user can create. " + "1 - IP. Number of public IP addresses a user can own. " + "2 - Volume. Number of disk volumes a user can create." + "3 - Snapshot. Number of snapshots a user can create." + - "4 - Template. Number of templates that a user can register/create.") + "4 - Template. Number of templates that a user can register/create." + + "6 - Network. Number of guest network a user can create." + + "7 - VPC. Number of VPC a user can create." + + "8 - CPU. Total number of CPU cores a user can use." + + "9 - Memory. Total Memory (in MB) a user can use." ) private Integer resourceType; ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/response/AccountResponse.java b/api/src/org/apache/cloudstack/api/response/AccountResponse.java index 0277d5b6fe9..9a98a356492 100644 --- a/api/src/org/apache/cloudstack/api/response/AccountResponse.java +++ b/api/src/org/apache/cloudstack/api/response/AccountResponse.java @@ -132,6 +132,24 @@ public class AccountResponse extends BaseResponse { @SerializedName("vpcavailable") @Param(description="the total number of vpcs available to be created for this account", since="4.0.0") private String vpcAvailable; + @SerializedName("cpulimit") @Param(description="the total number of cpu cores the account can own", since="4.1.0") + private String cpuLimit; + + @SerializedName("cputotal") @Param(description="the total number of cpu cores owned by account", since="4.1.0") + private Long cpuTotal; + + @SerializedName("cpuavailable") @Param(description="the total number of cpu cores available to be created for this account", since="4.1.0") + private String cpuAvailable; + + @SerializedName("memorylimit") @Param(description="the total memory (in MB) the account can own", since="4.1.0") + private String memoryLimit; + + @SerializedName("memorytotal") @Param(description="the total memory (in MB) owned by account", since="4.1.0") + private Long memoryTotal; + + @SerializedName("memoryavailable") @Param(description="the total memory (in MB) available to be created for this account", since="4.1.0") + private String memoryAvailable; + @SerializedName(ApiConstants.STATE) @Param(description="the state of the account") private String state; @@ -294,6 +312,30 @@ public class AccountResponse extends BaseResponse { this.networkAvailable = networkAvailable; } + public void setCpuLimit(String cpuLimit) { + this.cpuLimit = cpuLimit; + } + + public void setCpuTotal(Long cpuTotal) { + this.cpuTotal = cpuTotal; + } + + public void setCpuAvailable(String cpuAvailable) { + this.cpuAvailable = cpuAvailable; + } + + public void setMemoryLimit(String memoryLimit) { + this.memoryLimit = memoryLimit; + } + + public void setMemoryTotal(Long memoryTotal) { + this.memoryTotal = memoryTotal; + } + + public void setMemoryAvailable(String memoryAvailable) { + this.memoryAvailable = memoryAvailable; + } + public void setDefaultZone(String defaultZoneId) { this.defaultZoneId = defaultZoneId; } diff --git a/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java b/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java index 9d4f6c5aad2..a7fbbf2630b 100644 --- a/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java @@ -40,7 +40,7 @@ public class ResourceCountResponse extends BaseResponse implements ControlledEnt @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name for which resource count's are updated") private String domainName; - @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4. See the resourceType parameter for more information on these values.") + @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName("resourcecount") @Param(description="resource count") diff --git a/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java b/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java index beead247b23..b444e7a68a7 100644 --- a/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java @@ -36,7 +36,7 @@ public class ResourceLimitResponse extends BaseResponse implements ControlledEnt @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the resource limit") private String domainName; - @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4. See the resourceType parameter for more information on these values.") + @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName("max") @Param(description="the maximum number of the resource. A -1 means the resource currently has no limit.") diff --git a/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java index 22b807c9d40..898bafc47cd 100644 --- a/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java @@ -157,6 +157,24 @@ public class AccountJoinDaoImpl extends GenericDaoBase impl accountResponse.setNetworkTotal(vpcTotal); accountResponse.setNetworkAvailable(vpcAvail); + //get resource limits for cpu cores + long cpuLimit = ApiDBUtils.findCorrectResourceLimit(account.getCpuLimit(), account.getType(), ResourceType.cpu); + String cpuLimitDisplay = (accountIsAdmin || cpuLimit == -1) ? "Unlimited" : String.valueOf(cpuLimit); + long cpuTotal = (account.getCpuTotal() == null) ? 0 : account.getCpuTotal(); + String cpuAvail = (accountIsAdmin || cpuLimit == -1) ? "Unlimited" : String.valueOf(cpuLimit - cpuTotal); + accountResponse.setCpuLimit(cpuLimitDisplay); + accountResponse.setCpuTotal(cpuTotal); + accountResponse.setCpuAvailable(cpuAvail); + + //get resource limits for memory + long memoryLimit = ApiDBUtils.findCorrectResourceLimit(account.getMemoryLimit(), account.getType(), ResourceType.memory); + String memoryLimitDisplay = (accountIsAdmin || memoryLimit == -1) ? "Unlimited" : String.valueOf(memoryLimit); + long memoryTotal = (account.getMemoryTotal() == null) ? 0 : account.getMemoryTotal(); + String memoryAvail = (accountIsAdmin || memoryLimit == -1) ? "Unlimited" : String.valueOf(memoryLimit - memoryTotal); + accountResponse.setMemoryLimit(memoryLimitDisplay); + accountResponse.setMemoryTotal(memoryTotal); + accountResponse.setMemoryAvailable(memoryAvail); + // adding all the users for an account as part of the response obj List usersForAccount = ApiDBUtils.findUserViewByAccountId(account.getId()); List userResponses = ViewResponseHelper.createUserResponse(usersForAccount.toArray(new UserAccountJoinVO[usersForAccount.size()])); diff --git a/server/src/com/cloud/api/query/vo/AccountJoinVO.java b/server/src/com/cloud/api/query/vo/AccountJoinVO.java index 6d37f4de00e..cd7231cc8ea 100644 --- a/server/src/com/cloud/api/query/vo/AccountJoinVO.java +++ b/server/src/com/cloud/api/query/vo/AccountJoinVO.java @@ -148,6 +148,20 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident @Column(name="vpcTotal") private Long vpcTotal; + + @Column(name="cpuLimit") + private Long cpuLimit; + + @Column(name="cpuTotal") + private Long cpuTotal; + + + @Column(name="memoryLimit") + private Long memoryLimit; + + @Column(name="memoryTotal") + private Long memoryTotal; + @Column(name="job_id") private long jobId; @@ -445,7 +459,6 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident } - public Long getVpcTotal() { return vpcTotal; } @@ -456,6 +469,25 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident } + public Long getCpuTotal() { + return cpuTotal; + } + + + public void setCpuTotal(Long cpuTotal) { + this.cpuTotal = cpuTotal; + } + + public Long getMemoryTotal() { + return memoryTotal; + } + + + public void setMemoryTotal(Long memoryTotal) { + this.memoryTotal = memoryTotal; + } + + public Long getVmLimit() { return vmLimit; } @@ -536,6 +568,26 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident } + public Long getCpuLimit() { + return cpuLimit; + } + + + public void setCpuLimit(Long cpuLimit) { + this.cpuLimit = cpuLimit; + } + + + public Long getMemoryLimit() { + return memoryLimit; + } + + + public void setMemoryLimit(Long memoryLimit) { + this.memoryLimit = memoryLimit; + } + + public long getJobId() { return jobId; } diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java index 8e447bc9aa6..5de5ccdd059 100755 --- a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java +++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java @@ -239,14 +239,14 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet _configMgr.checkZoneAccess(owner, dc); } - // check if account/domain is with in resource limits to create a new vm - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); - ServiceOfferingVO offering = _serviceOfferingDao.findById(cmd.getServiceOfferingId()); if (offering == null || offering.getRemoved() != null) { throw new InvalidParameterValueException("Unable to find service offering: " + cmd.getServiceOfferingId()); } - + + // check if account/domain is with in resource limits to create a new vm + resourceLimitCheck(owner, new Long(offering.getCpu()), new Long(offering.getRamSize())); + VMTemplateVO template = _templateDao.findById(cmd.getTemplateId()); // Make sure a valid template ID was specified if (template == null || template.getRemoved() != null) { @@ -362,7 +362,7 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet vm.getHostName(), offering.getId(), template.getId(), HypervisorType.BareMetal.toString(), VirtualMachine.class.getName(), vm.getUuid()); - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm); + resourceCountIncrement(accountId, new Long(offering.getCpu()), new Long(offering.getRamSize())); // Assign instance to the group try { diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index cbd5b013699..b1a13083399 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -308,6 +308,8 @@ public enum Config { DefaultMaxAccountVolumes("Account Defaults", ManagementServer.class, Long.class, "max.account.volumes", "20", "The default maximum number of volumes that can be created for an account", null), DefaultMaxAccountNetworks("Account Defaults", ManagementServer.class, Long.class, "max.account.networks", "20", "The default maximum number of networks that can be created for an account", null), DefaultMaxAccountVpcs("Account Defaults", ManagementServer.class, Long.class, "max.account.vpcs", "20", "The default maximum number of vpcs that can be created for an account", null), + DefaultMaxAccountCpus("Account Defaults", ManagementServer.class, Long.class, "max.account.cpus", "40", "The default maximum number of cpu cores that can be used for an account", null), + DefaultMaxAccountMemory("Account Defaults", ManagementServer.class, Long.class, "max.account.memory", "40960", "The default maximum memory (in MB) that can be used for an account", null), ResourceCountCheckInterval("Advanced", ManagementServer.class, Long.class, "resourcecount.check.interval", "0", "Time (in seconds) to wait before retrying resource count check task. Default is 0 which is to never run the task", "Seconds"), @@ -332,6 +334,8 @@ public enum Config { DefaultMaxProjectVolumes("Project Defaults", ManagementServer.class, Long.class, "max.project.volumes", "20", "The default maximum number of volumes that can be created for a project", null), DefaultMaxProjectNetworks("Project Defaults", ManagementServer.class, Long.class, "max.project.networks", "20", "The default maximum number of networks that can be created for a project", null), DefaultMaxProjectVpcs("Project Defaults", ManagementServer.class, Long.class, "max.project.vpcs", "20", "The default maximum number of vpcs that can be created for a project", null), + DefaultMaxProjectCpus("Project Defaults", ManagementServer.class, Long.class, "max.project.cpus", "40", "The default maximum number of cpu cores that can be used for a project", null), + DefaultMaxProjectMemory("Project Defaults", ManagementServer.class, Long.class, "max.project.memory", "40960", "The default maximum memory (in MB) that can be used for a project", null), ProjectInviteRequired("Project Defaults", ManagementServer.class, Boolean.class, "project.invite.required", "false", "If invitation confirmation is required when add account to project. Default value is false", null), ProjectInvitationExpirationTime("Project Defaults", ManagementServer.class, Long.class, "project.invite.timeout", "86400", "Invitation expiration time (in seconds). Default is 1 day - 86400 seconds", null), diff --git a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 7419690244f..7ff06af9409 100755 --- a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -29,10 +29,10 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import org.apache.cloudstack.acl.SecurityChecker.AccessType; import com.cloud.alert.AlertManager; import com.cloud.configuration.Config; import com.cloud.configuration.Resource; @@ -59,9 +59,12 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectAccount.Role; import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDaoImpl.SumCount; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; @@ -69,15 +72,21 @@ import com.cloud.user.ResourceLimitService; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Func; +import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @@ -124,6 +133,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim private NetworkDao _networkDao; @Inject private VpcDao _vpcDao; + @Inject + private ServiceOfferingDao _serviceOfferingDao; protected SearchBuilder ResourceCountSearch; ScheduledExecutorService _rcExecutor; @@ -165,6 +176,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim projectResourceLimitMap.put(Resource.ResourceType.volume, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectVolumes.key()))); projectResourceLimitMap.put(Resource.ResourceType.network, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectNetworks.key()))); projectResourceLimitMap.put(Resource.ResourceType.vpc, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectVpcs.key()))); + projectResourceLimitMap.put(Resource.ResourceType.cpu, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectCpus.key()))); + projectResourceLimitMap.put(Resource.ResourceType.memory, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectMemory.key()))); accountResourceLimitMap.put(Resource.ResourceType.public_ip, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountPublicIPs.key()))); accountResourceLimitMap.put(Resource.ResourceType.snapshot, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountSnapshots.key()))); @@ -173,6 +186,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim accountResourceLimitMap.put(Resource.ResourceType.volume, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountVolumes.key()))); accountResourceLimitMap.put(Resource.ResourceType.network, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountNetworks.key()))); accountResourceLimitMap.put(Resource.ResourceType.vpc, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountVpcs.key()))); + accountResourceLimitMap.put(Resource.ResourceType.cpu, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountCpus.key()))); + accountResourceLimitMap.put(Resource.ResourceType.memory, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountMemory.key()))); return true; } @@ -769,7 +784,11 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } else if (type == Resource.ResourceType.network) { newCount = _networkDao.countNetworksUserCanCreate(accountId); } else if (type == Resource.ResourceType.vpc) { - newCount = _vpcDao.countByAccountId(accountId); + newCount = _vpcDao.countByAccountId(accountId); + } else if (type == Resource.ResourceType.cpu) { + newCount = countCpusForAccount(accountId); + } else if (type == Resource.ResourceType.memory) { + newCount = calculateMemoryForAccount(accountId); } else { throw new InvalidParameterValueException("Unsupported resource type " + type); } @@ -784,6 +803,50 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim return (newCount == null) ? 0 : newCount.longValue(); } + public long countCpusForAccount(long accountId) { + GenericSearchBuilder cpuSearch = _serviceOfferingDao.createSearchBuilder(SumCount.class); + cpuSearch.select("sum", Func.SUM, cpuSearch.entity().getCpu()); + SearchBuilder join1 = _userVmDao.createSearchBuilder(); + join1.and("accountId", join1.entity().getAccountId(), Op.EQ); + join1.and("type", join1.entity().getType(), Op.EQ); + join1.and("state", join1.entity().getState(), SearchCriteria.Op.NIN); + cpuSearch.join("offerings", join1, cpuSearch.entity().getId(), join1.entity().getServiceOfferingId(), JoinBuilder.JoinType.INNER); + cpuSearch.done(); + + SearchCriteria sc = cpuSearch.create(); + sc.setJoinParameters("offerings", "accountId", accountId); + sc.setJoinParameters("offerings", "type", VirtualMachine.Type.User); + sc.setJoinParameters("offerings", "state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + List cpus = _serviceOfferingDao.customSearch(sc, null); + if (cpus != null) { + return cpus.get(0).sum; + } else { + return 0; + } + } + + public long calculateMemoryForAccount(long accountId) { + GenericSearchBuilder memorySearch = _serviceOfferingDao.createSearchBuilder(SumCount.class); + memorySearch.select("sum", Func.SUM, memorySearch.entity().getRamSize()); + SearchBuilder join1 = _userVmDao.createSearchBuilder(); + join1.and("accountId", join1.entity().getAccountId(), Op.EQ); + join1.and("type", join1.entity().getType(), Op.EQ); + join1.and("state", join1.entity().getState(), SearchCriteria.Op.NIN); + memorySearch.join("offerings", join1, memorySearch.entity().getId(), join1.entity().getServiceOfferingId(), JoinBuilder.JoinType.INNER); + memorySearch.done(); + + SearchCriteria sc = memorySearch.create(); + sc.setJoinParameters("offerings", "accountId", accountId); + sc.setJoinParameters("offerings", "type", VirtualMachine.Type.User); + sc.setJoinParameters("offerings", "state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + List memory = _serviceOfferingDao.customSearch(sc, null); + if (memory != null) { + return memory.get(0).sum; + } else { + return 0; + } + } + @Override public long getResourceCount(Account account, ResourceType type) { return _resourceCountDao.getResourceCount(account.getId(), ResourceOwnerType.Account, type); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 33a53d9e5f4..d5c66ded91e 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -418,6 +418,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use return _vmDao.listByHostId(hostId); } + protected void resourceLimitCheck (Account owner, Long cpu, Long memory) throws ResourceAllocationException { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, cpu); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, memory); + } + + protected void resourceCountIncrement (long accountId, Long cpu, Long memory) { + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm); + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.cpu, cpu); + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.memory, memory); + } + + protected void resourceCountDecrement (long accountId, Long cpu, Long memory) { + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.user_vm); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.cpu, cpu); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.memory, memory); + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD, eventDescription = "resetting Vm password", async = true) public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) @@ -1629,9 +1647,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use "Unable to recover VM as the account is deleted"); } - // First check that the maximum number of UserVMs for the given + // Get serviceOffering for Virtual Machine + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + + // First check that the maximum number of UserVMs, CPU and Memory limit for the given // accountId will not be exceeded - _resourceLimitMgr.checkResourceLimit(account, ResourceType.user_vm); + resourceLimitCheck(account, new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize())); _haMgr.cancelDestroy(vm, vm.getHostId()); @@ -1672,12 +1693,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } + //Update Resource Count for the given account _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.volume, new Long(volumes.size())); - - _resourceLimitMgr.incrementResourceCount(account.getId(), - ResourceType.user_vm); - + resourceCountIncrement(account.getId(), new Long(serviceOffering.getCpu()), + new Long(serviceOffering.getRamSize())); txn.commit(); return _vmDao.findById(vmId); @@ -2384,8 +2404,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use String msg = "Failed to deploy Vm with Id: " + vmId + ", on Host with Id: " + hostId; _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg); - _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), - ResourceType.user_vm); + // Get serviceOffering for Virtual Machine + ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + + // Update Resource Count for the given account + resourceCountDecrement(vm.getAccountId(), new Long(offering.getCpu()), + new Long(offering.getRamSize())); } } } @@ -3120,9 +3144,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use _configMgr.checkZoneAccess(owner, zone); } + ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOffering.getId()); + // check if account/domain is with in resource limits to create a new vm boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); + resourceLimitCheck(owner, new Long(offering.getCpu()), new Long(offering.getRamSize())); _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2)); @@ -3150,9 +3176,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use -1); } - ServiceOfferingVO offering = _serviceOfferingDao - .findById(serviceOffering.getId()); - if (template.getTemplateType().equals(TemplateType.SYSTEM)) { throw new InvalidParameterValueException( "Unable to use system template " + template.getId() @@ -3380,8 +3403,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid()); - _resourceLimitMgr.incrementResourceCount(accountId, - ResourceType.user_vm); + //Update Resource Count for the given account + resourceCountIncrement(accountId, new Long(offering.getCpu()), + new Long(offering.getRamSize())); txn.commit(); @@ -3915,10 +3939,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } if (vmState != State.Error) { - _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), - ResourceType.user_vm); - } + // Get serviceOffering for Virtual Machine + ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + //Update Resource Count for the given account + resourceCountDecrement(vm.getAccountId(), new Long(offering.getCpu()), + new Long(offering.getRamSize())); + } return _vmDao.findById(vmId); } else { CloudRuntimeException ex = new CloudRuntimeException( @@ -4414,12 +4441,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use DataCenterVO zone = _dcDao.findById(vm.getDataCenterId()); - // Remove vm from instance group + // Get serviceOffering for Virtual Machine + ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + + //Remove vm from instance group removeInstanceFromInstanceGroup(cmd.getVmId()); - // VV 2: check if account/domain is with in resource limits to create a - // new vm - _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.user_vm); + // VV 2: check if account/domain is with in resource limits to create a new vm + resourceLimitCheck(newAccount, new Long(offering.getCpu()), new Long(offering.getRamSize())); // VV 3: check if volumes are with in resource limits _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.volume, @@ -4444,9 +4473,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid()); - // update resource counts - _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), - ResourceType.user_vm); + + // update resource counts for old account + resourceCountDecrement(oldAccount.getAccountId(), new Long(offering.getCpu()), + new Long(offering.getRamSize())); // OWNERSHIP STEP 1: update the vm owner vm.setAccountId(newAccount.getAccountId()); @@ -4473,7 +4503,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } - _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.user_vm); + //update resource count of new account + resourceCountIncrement(newAccount.getAccountId(), new Long(offering.getCpu()), new Long(offering.getRamSize())); + //generate usage events to account for this change UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString(), diff --git a/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java new file mode 100644 index 00000000000..0cf0459f532 --- /dev/null +++ b/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -0,0 +1,76 @@ +package com.cloud.resourcelimit; + +import javax.inject.Inject; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.cloud.configuration.ResourceLimit; +import com.cloud.vpc.MockResourceLimitManagerImpl; + +public class ResourceLimitManagerImplTest extends TestCase{ + private static final Logger s_logger = Logger.getLogger(ResourceLimitManagerImplTest.class); + + MockResourceLimitManagerImpl _resourceLimitService = new MockResourceLimitManagerImpl(); + + @Override + @Before + public void setUp() { + + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testInjected() throws Exception { + s_logger.info("Starting test for Resource Limit manager"); + updateResourceCount(); + updateResourceLimit(); + //listResourceLimits(); + s_logger.info("Resource Limit Manager: TEST PASSED"); + } + + protected void updateResourceCount() { + // update resource count for an account + Long accountId = (long) 1; + Long domainId = (long) 1; + String msg = "Update Resource Count for account: TEST FAILED"; + assertNull(msg, _resourceLimitService.recalculateResourceCount(accountId, domainId, null)); + + // update resource count for a domain + accountId = null; + msg = "Update Resource Count for domain: TEST FAILED"; + assertNull(msg, _resourceLimitService.recalculateResourceCount(accountId, domainId, null)); + } + + protected void updateResourceLimit() { + // update resource Limit for an account for resource_type = 8 (CPU) + resourceLimitServiceCall((long) 1, (long) 1, 8, (long) 20); + + // update resource Limit for a domain for resource_type = 8 (CPU) + resourceLimitServiceCall(null, (long) 1, 8, (long) 40); + + // update resource Limit for an account for resource_type = 9 (Memory) + resourceLimitServiceCall((long) 1, (long) 1, 9, (long) 4096); + + // update resource Limit for a domain for resource_type = 9 (Memory) + resourceLimitServiceCall(null, (long) 1, 9, (long) 10240); + } + + private void resourceLimitServiceCall(Long accountId, Long domainId, Integer resourceType, Long max) { + String msg = "Update Resource Limit: TEST FAILED"; + ResourceLimit result = null; + try { + result = _resourceLimitService.updateResourceLimit(accountId, domainId, resourceType, max); + assertFalse(msg, (result != null || (result == null && max != null && max.longValue() == -1L))); + } catch (Exception ex) { + fail(msg); + } + } +} \ No newline at end of file diff --git a/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java index 690aed65677..b9fc8617059 100644 --- a/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -31,7 +31,6 @@ import com.cloud.domain.Domain; import com.cloud.exception.ResourceAllocationException; import com.cloud.user.Account; import com.cloud.user.ResourceLimitService; -import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; @Component @@ -117,6 +116,22 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } + /* (non-Javadoc) + * @see com.cloud.user.ResourceLimitService#countCpusForAccount(long) + */ + public long countCpusForAccount(long accountId) { + // TODO Auto-generated method stub + return 0; + } + + /* (non-Javadoc) + * @see com.cloud.user.ResourceLimitService#calculateRAMForAccount(long) + */ + public long calculateMemoryForAccount(long accountId) { + // TODO Auto-generated method stub + return 0; + } + /* (non-Javadoc) * @see com.cloud.user.ResourceLimitService#getResourceCount(com.cloud.user.Account, com.cloud.configuration.Resource.ResourceType) */ diff --git a/setup/db/create-schema-view.sql b/setup/db/create-schema-view.sql index f68a6cadb71..265779dccdc 100644 --- a/setup/db/create-schema-view.sql +++ b/setup/db/create-schema-view.sql @@ -817,6 +817,10 @@ CREATE VIEW `cloud`.`account_view` AS projectcount.count projectTotal, networklimit.max networkLimit, networkcount.count networkTotal, + cpulimit.max cpuLimit, + cpucount.count cpuTotal, + memorylimit.max memoryLimit, + memorycount.count memoryTotal, async_job.id job_id, async_job.uuid job_uuid, async_job.job_status job_status, @@ -885,6 +889,18 @@ CREATE VIEW `cloud`.`account_view` AS `cloud`.`resource_count` networkcount ON account.id = networkcount.account_id and networkcount.type = 'network' left join + `cloud`.`resource_limit` cpulimit ON account.id = cpulimit.account_id + and cpulimit.type = 'cpu' + left join + `cloud`.`resource_count` cpucount ON account.id = cpucount.account_id + and cpucount.type = 'cpu' + left join + `cloud`.`resource_limit` memorylimit ON account.id = memorylimit.account_id + and memorylimit.type = 'memory' + left join + `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id + and memorycount.type = 'memory' + left join `cloud`.`async_job` ON async_job.instance_id = account.id and async_job.instance_type = 'Account' and async_job.job_status = 0; diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index bb9c815af05..7f0044127f6 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -953,6 +953,10 @@ CREATE VIEW `cloud`.`account_view` AS projectcount.count projectTotal, networklimit.max networkLimit, networkcount.count networkTotal, + cpulimit.max cpuLimit, + cpucount.count cpuTotal, + memorylimit.max memoryLimit, + memorycount.count memoryTotal, async_job.id job_id, async_job.uuid job_uuid, async_job.job_status job_status, @@ -1021,6 +1025,18 @@ CREATE VIEW `cloud`.`account_view` AS `cloud`.`resource_count` networkcount ON account.id = networkcount.account_id and networkcount.type = 'network' left join + `cloud`.`resource_limit` cpulimit ON account.id = cpulimit.account_id + and cpulimit.type = 'cpu' + left join + `cloud`.`resource_count` cpucount ON account.id = cpucount.account_id + and cpucount.type = 'cpu' + left join + `cloud`.`resource_limit` memorylimit ON account.id = memorylimit.account_id + and memorylimit.type = 'memory' + left join + `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id + and memorycount.type = 'memory' + left join `cloud`.`async_job` ON async_job.instance_id = account.id and async_job.instance_type = 'Account' and async_job.job_status = 0; @@ -1299,3 +1315,11 @@ INSERT INTO `cloud`.`region` values ('1','Local','http://localhost:8080/client/a ALTER TABLE `cloud`.`account` ADD COLUMN `region_id` int unsigned NOT NULL DEFAULT '1'; ALTER TABLE `cloud`.`user` ADD COLUMN `region_id` int unsigned NOT NULL DEFAULT '1'; ALTER TABLE `cloud`.`domain` ADD COLUMN `region_id` int unsigned NOT NULL DEFAULT '1'; + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.cpus', '40', 'The default maximum number of cpu cores that can be used for an account'); + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.memory', '40960', 'The default maximum memory (in MB) that can be used for an account'); + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.cpus', '40', 'The default maximum number of cpu cores that can be used for a project'); + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.memory', '40960', 'The default maximum memory (in MB) that can be used for a project'); From cd039206d354ce0e8ae5ef997f0b701b6cb8fdd7 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Tue, 12 Feb 2013 13:34:49 -0800 Subject: [PATCH 15/42] CLOUDSTACK-1128: [EC2 Query API] DescribeAvailabilityZones, support for message filter Add message in the response element of EC2DesrcibeAvailibilityZones. Add support for filter 'message'. The value of 'message' should be set to the allocation_state of the zone Code cleanup --- .../bridge/service/EC2SoapServiceImpl.java | 15 +++-- .../service/core/ec2/EC2AvailabilityZone.java | 55 +++++++++++++++++++ .../ec2/EC2AvailabilityZonesFilterSet.java | 21 ++++--- .../EC2DescribeAvailabilityZonesResponse.java | 36 ++++-------- .../bridge/service/core/ec2/EC2Engine.java | 43 ++++++++------- 5 files changed, 113 insertions(+), 57 deletions(-) create mode 100644 awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZone.java diff --git a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java index bf3c13eda9c..442b4606a1f 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java +++ b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java @@ -41,6 +41,7 @@ import com.cloud.bridge.service.core.ec2.EC2DescribeAvailabilityZones; import com.cloud.bridge.service.core.ec2.EC2DescribeAvailabilityZonesResponse; import com.cloud.bridge.service.core.ec2.EC2DescribeImageAttribute; +import com.cloud.bridge.service.core.ec2.EC2AvailabilityZone; import com.cloud.bridge.service.core.ec2.EC2DescribeImages; import com.cloud.bridge.service.core.ec2.EC2DescribeImagesResponse; import com.cloud.bridge.service.core.ec2.EC2DescribeInstances; @@ -1775,14 +1776,18 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { DescribeAvailabilityZonesResponse response = new DescribeAvailabilityZonesResponse(); DescribeAvailabilityZonesResponseType param1 = new DescribeAvailabilityZonesResponseType(); AvailabilityZoneSetType param2 = new AvailabilityZoneSetType(); - - String[] zones = engineResponse.getZoneSet(); - for (String zone : zones) { + + EC2AvailabilityZone[] zones = engineResponse.getAvailabilityZoneSet(); + for (EC2AvailabilityZone zone : zones) { AvailabilityZoneItemType param3 = new AvailabilityZoneItemType(); - AvailabilityZoneMessageSetType param4 = new AvailabilityZoneMessageSetType(); - param3.setZoneName( zone ); + param3.setZoneName( zone.getName() ); param3.setZoneState( "available" ); param3.setRegionName( "" ); + + AvailabilityZoneMessageSetType param4 = new AvailabilityZoneMessageSetType(); + AvailabilityZoneMessageType param5 = new AvailabilityZoneMessageType(); + param5.setMessage(zone.getMessage()); + param4.addItem(param5); param3.setMessageSet( param4 ); param2.addItem( param3 ); } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZone.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZone.java new file mode 100644 index 00000000000..457c6efd069 --- /dev/null +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZone.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.bridge.service.core.ec2; + +public class EC2AvailabilityZone { + + private String id; + private String name; + private String message; + + public EC2AvailabilityZone() { + id = null; + name = null; + message = null; + } + + public void setId( String id ) { + this.id = id; + } + + public String getId() { + return this.id; + } + + public void setName( String name ) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public void setMessage( String message ) { + this.message = message; + } + + public String getMessage() { + return this.message; + } + +} diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZonesFilterSet.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZonesFilterSet.java index 994b721203a..aa3897a4bf6 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZonesFilterSet.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2AvailabilityZonesFilterSet.java @@ -36,6 +36,7 @@ public class EC2AvailabilityZonesFilterSet { public EC2AvailabilityZonesFilterSet() { // -> use these values to check that the proper filter is passed to this type of filter set filterTypes.put( "zone-name", "String" ); + filterTypes.put( "message", "String"); } public void addFilter( EC2Filter param ) { @@ -55,13 +56,14 @@ public class EC2AvailabilityZonesFilterSet { return filterSet.toArray(new EC2Filter[0]); } - public List evaluate( EC2DescribeAvailabilityZonesResponse availabilityZones) throws ParseException { - List resultList = new ArrayList(); + public EC2DescribeAvailabilityZonesResponse evaluate( EC2DescribeAvailabilityZonesResponse availabilityZones) + throws ParseException { + EC2DescribeAvailabilityZonesResponse resultList = new EC2DescribeAvailabilityZonesResponse(); boolean matched; EC2Filter[] filterSet = getFilterSet(); - for ( String availableZone : availabilityZones.getZoneSet() ) { + for ( EC2AvailabilityZone availableZone : availabilityZones.getAvailabilityZoneSet() ) { matched = true; if (filterSet != null) { for (EC2Filter filter : filterSet) { @@ -71,19 +73,22 @@ public class EC2AvailabilityZonesFilterSet { } } } - if (matched == true) - resultList.add(availableZone); + if (matched) + resultList.addAvailabilityZone(availableZone); } return resultList; } - private boolean filterMatched( String availableZone, EC2Filter filter ) throws ParseException { + private boolean filterMatched( EC2AvailabilityZone availableZone, EC2Filter filter ) throws ParseException { String filterName = filter.getName(); String[] valueSet = filter.getValueSet(); if ( filterName.equalsIgnoreCase("zone-name")) { - return containsString(availableZone, valueSet); - } + return containsString(availableZone.getName(), valueSet); + } + else if (filterName.equalsIgnoreCase("message")) { + return containsString(availableZone.getMessage(), valueSet); + } return false; } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2DescribeAvailabilityZonesResponse.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2DescribeAvailabilityZonesResponse.java index ae0c233f83f..f9bc6b66d83 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2DescribeAvailabilityZonesResponse.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2DescribeAvailabilityZonesResponse.java @@ -20,31 +20,17 @@ import java.util.ArrayList; import java.util.List; public class EC2DescribeAvailabilityZonesResponse { + private List availabilityZoneSet = new ArrayList(); - private List zoneIds = new ArrayList(); - private List zoneNames = new ArrayList(); + public EC2DescribeAvailabilityZonesResponse() { + } + + public void addAvailabilityZone( EC2AvailabilityZone param ) { + availabilityZoneSet.add( param ); + } + + public EC2AvailabilityZone[] getAvailabilityZoneSet() { + return availabilityZoneSet.toArray(new EC2AvailabilityZone[0]); + } - public EC2DescribeAvailabilityZonesResponse() { - } - - public void addZone(String id, String name) { - zoneIds.add(id); - zoneNames.add(name); - } - - /** - * The Amazon API only cares about the names of zones not their ID value. - * - * @return an array containing a set of zone names - */ - public String[] getZoneSet() { - return zoneNames.toArray(new String[0]); - } - - public String getZoneIdAt(int index) { - if (zoneIds.isEmpty() || index >= zoneIds.size()) { - return null; - } - return zoneIds.get(index); - } } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index b729f778ed3..1d278a245c8 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -1060,12 +1060,8 @@ public class EC2Engine extends ManagerBase { EC2AvailabilityZonesFilterSet azfs = request.getFilterSet(); if ( null == azfs ) return availableZones; - else { - List matchedAvailableZones = azfs.evaluate(availableZones); - if (matchedAvailableZones.isEmpty()) - return new EC2DescribeAvailabilityZonesResponse(); - return listZones(matchedAvailableZones.toArray(new String[0]), null); - } + else + return azfs.evaluate(availableZones); } catch( EC2ServiceException error ) { logger.error( "EC2 DescribeAvailabilityZones - ", error); throw error; @@ -1691,9 +1687,11 @@ public class EC2Engine extends ManagerBase { zones = listZones(interestedZones, domainId); - if (zones == null || zones.getZoneIdAt( 0 ) == null) + if (zones == null || zones.getAvailabilityZoneSet().length == 0) throw new EC2ServiceException(ClientError.InvalidParameterValue, "Unknown zoneName value - " + zoneName); - return zones.getZoneIdAt(0); + + EC2AvailabilityZone[] zoneSet = zones.getAvailabilityZoneSet(); + return zoneSet[0].getId(); } @@ -1768,24 +1766,31 @@ public class EC2Engine extends ManagerBase { * * @return EC2DescribeAvailabilityZonesResponse */ - private EC2DescribeAvailabilityZonesResponse listZones(String[] interestedZones, String domainId) throws Exception - { + private EC2DescribeAvailabilityZonesResponse listZones(String[] interestedZones, String domainId) + throws Exception { EC2DescribeAvailabilityZonesResponse zones = new EC2DescribeAvailabilityZonesResponse(); List cloudZones = getApi().listZones(true, domainId, null, null); - - if(cloudZones != null) { + if(cloudZones != null && cloudZones.size() > 0) { for(CloudStackZone cloudZone : cloudZones) { - if ( null != interestedZones && 0 < interestedZones.length ) { - for( int j=0; j < interestedZones.length; j++ ) { - if (interestedZones[j].equalsIgnoreCase( cloudZone.getName())) { - zones.addZone(cloudZone.getId().toString(), cloudZone.getName()); + boolean matched = false; + if (interestedZones.length > 0) { + for (String zoneName : interestedZones){ + if (zoneName.equalsIgnoreCase( cloudZone.getName())) { + matched = true; break; } } - } else { - zones.addZone(cloudZone.getId().toString(), cloudZone.getName()); + } else { + matched = true; } + if (!matched) continue; + EC2AvailabilityZone ec2Zone = new EC2AvailabilityZone(); + ec2Zone.setId(cloudZone.getId().toString()); + ec2Zone.setMessage(cloudZone.getAllocationState()); + ec2Zone.setName(cloudZone.getName()); + + zones.addAvailabilityZone(ec2Zone); } } return zones; @@ -1952,7 +1957,7 @@ public class EC2Engine extends ManagerBase { * @throws ParserConfigurationException * @throws ParseException */ - public EC2DescribeSecurityGroupsResponse listSecurityGroups( String[] interestedGroups ) throws Exception { + private EC2DescribeSecurityGroupsResponse listSecurityGroups( String[] interestedGroups ) throws Exception { try { EC2DescribeSecurityGroupsResponse groupSet = new EC2DescribeSecurityGroupsResponse(); From c26b02a0a7fd52b4daa1c063d8e04a8866d8e0e0 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Tue, 12 Feb 2013 13:40:58 -0800 Subject: [PATCH 16/42] CLOUDSTACK-1129: [EC2 Query API] DescribeVolumes, add support for filter attachment.status 1. If volume is attached to a VM set attachment state based on the state of the VM it is attached to. Running, Starting, Stopped -> Attached Starting -> Attaching Destroyed, Error -> Detached 2. If volume is not attached to a VM set attachment state as 'detached' --- .../bridge/service/EC2SoapServiceImpl.java | 32 +++---------------- .../bridge/service/core/ec2/EC2Engine.java | 32 +++++++++++++++++-- .../bridge/service/core/ec2/EC2Volume.java | 16 ++++++++++ .../service/core/ec2/EC2VolumeFilterSet.java | 5 ++- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java index 442b4606a1f..2fefb28e5be 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java +++ b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java @@ -1290,7 +1290,7 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { param5.setInstanceId(vol.getInstanceId().toString()); String devicePath = engine.cloudDeviceIdToDevicePath( vol.getHypervisor(), vol.getDeviceId()); param5.setDevice( devicePath ); - param5.setStatus( toVolumeAttachmentState( vol.getInstanceId(), vol.getVMState())); + param5.setStatus(vol.getAttachmentState()); if (vol.getAttached() == null) { param5.setAttachTime( cal ); } else { @@ -1546,25 +1546,7 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { else if (cloudState.equalsIgnoreCase( "Expunging" )) return new String( "terminated"); else return new String( "running" ); } - - /** - * We assume a state for the volume based on what its associated VM is doing. - * - * @param vmId - * @param vmState - * @return - */ - public static String toVolumeAttachmentState(String instanceId, String vmState ) { - if (null == instanceId || null == vmState) return "detached"; - - if (vmState.equalsIgnoreCase( "Destroyed" )) return "detached"; - else if (vmState.equalsIgnoreCase( "Stopped" )) return "attached"; - else if (vmState.equalsIgnoreCase( "Running" )) return "attached"; - else if (vmState.equalsIgnoreCase( "Starting" )) return "attaching"; - else if (vmState.equalsIgnoreCase( "Stopping" )) return "attached"; - else if (vmState.equalsIgnoreCase( "Error" )) return "detached"; - else return "detached"; - } + public static StopInstancesResponse toStopInstancesResponse(EC2StopInstancesResponse engineResponse) { StopInstancesResponse response = new StopInstancesResponse(); @@ -1808,10 +1790,7 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { param1.setVolumeId( engineResponse.getId().toString()); param1.setInstanceId( engineResponse.getInstanceId().toString()); param1.setDevice( engineResponse.getDevice()); - if ( null != engineResponse.getState()) - param1.setStatus( engineResponse.getState()); - else param1.setStatus( "" ); // ToDo - throw an Soap Fault - + param1.setStatus(engineResponse.getAttachmentState()); param1.setAttachTime( cal ); param1.setRequestId( UUID.randomUUID().toString()); @@ -1828,10 +1807,7 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { param1.setVolumeId( engineResponse.getId().toString()); param1.setInstanceId( (null == engineResponse.getInstanceId() ? "" : engineResponse.getInstanceId().toString())); param1.setDevice( (null == engineResponse.getDevice() ? "" : engineResponse.getDevice())); - if ( null != engineResponse.getState()) - param1.setStatus( engineResponse.getState()); - else param1.setStatus( "" ); // ToDo - throw an Soap Fault - + param1.setStatus(engineResponse.getAttachmentState()); param1.setAttachTime( cal ); param1.setRequestId( UUID.randomUUID().toString()); diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index 1d278a245c8..277cdc80f8c 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -1125,6 +1125,7 @@ public class EC2Engine extends ManagerBase { resp.setState(vol.getState()); resp.setType(vol.getVolumeType()); resp.setVMState(vol.getVirtualMachineState()); + resp.setAttachmentState(mapToAmazonVolumeAttachmentState(vol.getVirtualMachineState())); resp.setZoneName(vol.getZoneName()); return resp; } @@ -1211,6 +1212,7 @@ public class EC2Engine extends ManagerBase { resp.setState(vol.getState()); resp.setType(vol.getVolumeType()); resp.setVMState(vol.getVirtualMachineState()); + resp.setAttachmentState("detached"); resp.setZoneName(vol.getZoneName()); return resp; } @@ -1639,11 +1641,16 @@ public class EC2Engine extends ManagerBase { ec2Vol.setSize(vol.getSize()); ec2Vol.setType(vol.getVolumeType()); - if(vol.getVirtualMachineId() != null) + if(vol.getVirtualMachineId() != null) { ec2Vol.setInstanceId(vol.getVirtualMachineId()); + if (vol.getVirtualMachineState() != null) { + ec2Vol.setVMState(vol.getVirtualMachineState()); + ec2Vol.setAttachmentState(mapToAmazonVolumeAttachmentState(vol.getVirtualMachineState())); + } + } else { + ec2Vol.setAttachmentState("detached"); + } - if(vol.getVirtualMachineState() != null) - ec2Vol.setVMState(vol.getVirtualMachineState()); ec2Vol.setZoneName(vol.getZoneName()); List resourceTags = vol.getTags(); @@ -2404,6 +2411,25 @@ public class EC2Engine extends ManagerBase { return "error"; } + /** + * Map CloudStack VM state to Amazon volume attachment state + * + * @param CloudStack VM state + * @return Amazon Volume attachment state + */ + private String mapToAmazonVolumeAttachmentState (String vmState) { + if ( vmState.equalsIgnoreCase("Running") || vmState.equalsIgnoreCase("Stopping") || + vmState.equalsIgnoreCase("Stopped") ) { + return "attached"; + } + else if (vmState.equalsIgnoreCase("Starting")) { + return "attaching"; + } + else { // VM state is 'destroyed' or 'error' or other + return "detached"; + } + } + /** * Map Amazon resourceType to CloudStack resourceType * diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Volume.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Volume.java index 23d6d701646..79bc6e88911 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Volume.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Volume.java @@ -35,6 +35,7 @@ public class EC2Volume { private String hypervisor; private String created; private String attached; + private String attachmentState; private List tagsSet; public EC2Volume() { @@ -50,6 +51,7 @@ public class EC2Volume { hypervisor = null; created = null; attached = null; + attachmentState = null; tagsSet = new ArrayList(); } @@ -236,6 +238,20 @@ public class EC2Volume { this.attached = attached; } + /** + * @param state of the attached VM to set + */ + public void setAttachmentState(String attachedState) { + this.attachmentState = attachedState; + } + + /** + * @return state of the vm + */ + public String getAttachmentState() { + return attachmentState; + } + public void addResourceTag( EC2TagKeyValue param ) { tagsSet.add( param ); } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2VolumeFilterSet.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2VolumeFilterSet.java index 0594231413e..b8021f3d4ba 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2VolumeFilterSet.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2VolumeFilterSet.java @@ -43,7 +43,7 @@ public class EC2VolumeFilterSet { filterTypes.put( "attachment.delete-on-termination", "null" ); filterTypes.put( "attachment.device", "string" ); filterTypes.put( "attachment.instance-id", "string" ); - filterTypes.put( "attachment.status", "null" ); + filterTypes.put( "attachment.status", "set:attached|attaching|detached|detaching" ); filterTypes.put( "availability-zone", "string" ); filterTypes.put( "create-time", "xsd:dateTime" ); filterTypes.put( "size", "integer" ); @@ -136,6 +136,9 @@ public class EC2VolumeFilterSet { return containsDevice(vol.getDeviceId(), valueSet ); else if (filterName.equalsIgnoreCase( "attachment.instance-id" )) return containsString(String.valueOf(vol.getInstanceId()), valueSet ); + else if ( filterName.equalsIgnoreCase( "attachment.status" ) ) { + return containsString(vol.getAttachmentState(), valueSet ); + } else if (filterName.equalsIgnoreCase("tag-key")) { EC2TagKeyValue[] tagSet = vol.getResourceTags(); From b0b2fd48336d7b7620fdb4ed9f20ca46bef81a19 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Tue, 12 Feb 2013 13:56:19 -0800 Subject: [PATCH 17/42] CLOUDSTACK-1125: [EC2 Query API] Permission denied exception when a parameter value contains space Convert space characters in the parameters to %20 while forming a query string after url-encode because java.net.URLEncoder performs application/x-www-form-urlencoded-type encoding and not percent-encoding. According to RFC 3986 as required by Amazon, we need to percent-encode. --- awsapi/src/com/cloud/bridge/service/EC2RestServlet.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java index 5d151bac7e4..b5296a4699e 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java @@ -1899,10 +1899,14 @@ public class EC2RestServlet extends HttpServlet { String paramName = (String) params.nextElement(); // exclude the signature string obviously. ;) if (paramName.equalsIgnoreCase("Signature")) continue; + // URLEncoder performs application/x-www-form-urlencoded-type encoding and not Percent encoding + // according to RFC 3986 as required by Amazon, we need to Percent-encode (URL Encode) + String encodedValue = URLEncoder.encode(request.getParameter(paramName), "UTF-8") + .replace("+", "%20").replace("*", "%2A"); if (queryString == null) - queryString = paramName + "=" + URLEncoder.encode(request.getParameter(paramName), "UTF-8"); + queryString = paramName + "=" + encodedValue; else - queryString = queryString + "&" + paramName + "=" + URLEncoder.encode(request.getParameter(paramName), "UTF-8"); + queryString = queryString + "&" + paramName + "=" + encodedValue; } } } From 333710669351b93a04b07d8aba81f95e79ca20f0 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Tue, 12 Feb 2013 14:01:01 -0800 Subject: [PATCH 18/42] CLOUDSTACK-1131 [EC2 Query API] RunInstances allows negative values for paramters 'MinCount' and 'MaxCount' Add parameter validation to ensure MinCount is greater than 0 and MaxCount is great than or equal to MinCount --- .../cloud/bridge/service/EC2RestServlet.java | 24 ++++++++++++++----- .../bridge/service/EC2SoapServiceImpl.java | 13 ++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java index b5296a4699e..fe9303832ff 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java @@ -1142,14 +1142,26 @@ public class EC2RestServlet extends HttpServlet { else { response.sendError(530, "Missing ImageId parameter" ); return; } String[] minCount = request.getParameterValues( "MinCount" ); - if ( null != minCount && 0 < minCount.length ) - EC2request.setMinCount( Integer.parseInt( minCount[0] )); - else { response.sendError(530, "Missing MinCount parameter" ); return; } + if ( minCount == null || minCount.length < 1) { + response.sendError(530, "Missing MinCount parameter" ); + return; + } else if ( Integer.parseInt(minCount[0]) < 1) { + throw new EC2ServiceException(ClientError.InvalidParameterValue, + "Value of parameter MinCount should be greater than 0"); + } else { + EC2request.setMinCount( Integer.parseInt( minCount[0]) ); + } String[] maxCount = request.getParameterValues( "MaxCount" ); - if ( null != maxCount && 0 < maxCount.length ) - EC2request.setMaxCount( Integer.parseInt( maxCount[0] )); - else { response.sendError(530, "Missing MaxCount parameter" ); return; } + if ( maxCount == null || maxCount.length < 1) { + response.sendError(530, "Missing MaxCount parameter" ); + return; + } else if ( Integer.parseInt(maxCount[0]) < 1) { + throw new EC2ServiceException(ClientError.InvalidParameterValue, + "Value of parameter MaxCount should be greater than 0"); + } else { + EC2request.setMaxCount( Integer.parseInt( maxCount[0]) ); + } String[] instanceType = request.getParameterValues( "InstanceType" ); if ( null != instanceType && 0 < instanceType.length ) diff --git a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java index 2fefb28e5be..4e9445e8089 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java +++ b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java @@ -731,8 +731,17 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { EC2RunInstances request = new EC2RunInstances(); request.setTemplateId(rit.getImageId()); - request.setMinCount(rit.getMinCount()); - request.setMaxCount(rit.getMaxCount()); + + if (rit.getMinCount() < 1) { + throw new EC2ServiceException(ClientError.InvalidParameterValue, + "Value of parameter MinCount should be greater than 0"); + } else request.setMinCount( rit.getMinCount() ); + + if (rit.getMaxCount() < 1) { + throw new EC2ServiceException(ClientError.InvalidParameterValue, + "Value of parameter MaxCount should be greater than 0"); + } else request.setMaxCount(rit.getMaxCount()); + if (null != type) request.setInstanceType(type); if (null != prt) request.setZoneName(prt.getAvailabilityZone()); if (null != userData) request.setUserData(userData.getData()); From 57969843d658fe01cf1f55bb7990a0716726250f Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Tue, 12 Feb 2013 14:03:51 -0800 Subject: [PATCH 19/42] CLOUDSTACK-1133: [EC2 Query API] In StopInstances add support for parameter 'force' Propagate this parameter to CS to force stop an instance --- awsapi/src/com/cloud/bridge/service/EC2RestServlet.java | 5 +++++ .../src/com/cloud/bridge/service/EC2SoapServiceImpl.java | 3 +++ .../src/com/cloud/bridge/service/core/ec2/EC2Engine.java | 3 ++- .../cloud/bridge/service/core/ec2/EC2StopInstances.java | 9 +++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java index fe9303832ff..e20966237ee 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java @@ -1269,6 +1269,11 @@ public class EC2RestServlet extends HttpServlet { } if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } + String[] force = request.getParameterValues("Force"); + if ( force != null) { + EC2request.setForce( Boolean.parseBoolean(force[0])); + } + // -> execute the request StopInstancesResponse EC2response = EC2SoapServiceImpl.toStopInstancesResponse( ServiceProvider.getInstance().getEC2Engine().stopInstances( EC2request )); serializeResponse(response, EC2response); diff --git a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java index 4e9445e8089..9fc581be86f 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java +++ b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java @@ -773,6 +773,7 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { public StopInstancesResponse stopInstances(StopInstances stopInstances) { EC2StopInstances request = new EC2StopInstances(); StopInstancesType sit = stopInstances.getStopInstances(); + Boolean force = sit.getForce(); // -> toEC2StopInstances InstanceIdSetType iist = sit.getInstancesSet(); @@ -780,6 +781,8 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { if (null != items) { // -> should not be empty for( int i=0; i < items.length; i++ ) request.addInstanceId( items[i].getInstanceId()); } + + if (force) request.setForce(sit.getForce()); return toStopInstancesResponse( engine.stopInstances( request )); } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index 277cdc80f8c..a835d8a258b 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -1512,6 +1512,7 @@ public class EC2Engine extends ManagerBase { // -> first determine the current state of each VM (becomes it previous state) try { String[] instanceSet = request.getInstancesSet(); + Boolean forced = request.getForce(); EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null, null ); virtualMachines = previousState.getInstanceSet(); @@ -1533,7 +1534,7 @@ public class EC2Engine extends ManagerBase { instances.addInstance(vm); continue; } - resp = getApi().stopVirtualMachine(vm.getId(), false); + resp = getApi().stopVirtualMachine(vm.getId(), forced); if(logger.isDebugEnabled()) logger.debug("Stopping VM " + vm.getId() + " job " + resp.getJobId()); } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2StopInstances.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2StopInstances.java index 13e23d1a51f..be140008019 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2StopInstances.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2StopInstances.java @@ -23,6 +23,7 @@ public class EC2StopInstances { private List instancesSet = new ArrayList(); // a list of strings identifying instances private boolean destroyInstances; // we are destroying the instances rather than stopping them + private Boolean force = false; public EC2StopInstances() { destroyInstances = false; @@ -43,5 +44,13 @@ public class EC2StopInstances { public boolean getDestroyInstances() { return this.destroyInstances; } + + public void setForce( Boolean force ) { + this.force = force; + } + + public Boolean getForce() { + return this.force; + } } From c3be0f995d1b3f5ee1355971143234871ad517d9 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Tue, 12 Feb 2013 14:45:29 -0800 Subject: [PATCH 20/42] CLOUDSTACK-1135: [EC2 Query API] AuthorizeSecurityGroupIngress and RevokeSecurityGroupIngress fails when icmp code and type is provided For AuthorizeSecurityGroupIngress RevokeSecurityGroupIngress query calls parse and set the icmp code and type --- .../cloud/bridge/service/EC2RestServlet.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java index e20966237ee..29a002cebaf 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java @@ -707,12 +707,20 @@ public class EC2RestServlet extends HttpServlet { else break; String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" ); - if ( null != fromPort && 0 < fromPort.length) - perm.setFromPort( Integer.parseInt( fromPort[0])); + if ( null != fromPort && 0 < fromPort.length ) { + if ( protocol[0].equalsIgnoreCase("icmp") ) + perm.setIcmpType( fromPort[0] ) ; + else + perm.setFromPort( Integer.parseInt( fromPort[0]) ); + } String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" ); - if ( null != toPort && 0 < toPort.length) - perm.setToPort( Integer.parseInt( toPort[0])); + if ( null != toPort && 0 < toPort.length ) { + if ( protocol[0].equalsIgnoreCase("icmp") ) + perm.setIcmpCode( toPort[0] ); + else + perm.setToPort( Integer.parseInt( toPort[0]) ); + } // -> list: IpPermissions.n.IpRanges.m.CidrIp mCount = 1; @@ -780,12 +788,20 @@ public class EC2RestServlet extends HttpServlet { else break; String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" ); - if ( null != fromPort && 0 < fromPort.length) - perm.setFromPort( Integer.parseInt( fromPort[0])); + if ( null != fromPort && 0 < fromPort.length ) { + if ( protocol[0].equalsIgnoreCase("icmp") ) + perm.setIcmpType( fromPort[0] ) ; + else + perm.setFromPort( Integer.parseInt( fromPort[0]) ); + } String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" ); - if ( null != toPort && 0 < toPort.length) - perm.setToPort( Integer.parseInt( toPort[0])); + if ( null != toPort && 0 < toPort.length ) { + if ( protocol[0].equalsIgnoreCase("icmp") ) + perm.setIcmpCode( toPort[0] ); + else + perm.setToPort( Integer.parseInt( toPort[0]) ); + } // -> list: IpPermissions.n.IpRanges.m.CidrIp int mCount = 1; From 2293caa32e4387d7c71c1d4f677e3015856eff0a Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Tue, 12 Feb 2013 15:58:38 -0800 Subject: [PATCH 21/42] moved console proxy into services directory. no code change --- client/pom.xml | 2 +- .../bindir/cloud-setup-console-proxy.in | 220 --- console-proxy/certs/localhost.crt | 22 - console-proxy/certs/localhost.key | 27 - console-proxy/certs/realhostip.crt | 31 - console-proxy/certs/realhostip.csr | 15 - console-proxy/certs/realhostip.key | 24 - console-proxy/certs/realhostip.keystore | Bin 8690 -> 0 bytes console-proxy/conf.dom0/agent.properties.in | 46 - .../conf.dom0/consoleproxy.properties.in | 23 - console-proxy/conf.dom0/log4j-cloud.xml.in | 101 -- console-proxy/conf/agent.properties | 19 - console-proxy/conf/consoleproxy.properties | 23 - console-proxy/conf/log4j-cloud.xml | 102 -- console-proxy/css/ajaxviewer.css | 144 -- console-proxy/css/logger.css | 139 -- .../rc.d/init.d/cloud-console-proxy.in | 98 -- .../rc.d/init.d/cloud-console-proxy.in | 98 -- .../rc.d/init.d/cloud-console-proxy.in | 98 -- .../SYSCONFDIR/init.d/cloud-console-proxy.in | 112 -- console-proxy/images/back.gif | Bin 149 -> 0 bytes console-proxy/images/bright-green.png | Bin 3903 -> 0 bytes console-proxy/images/cad.gif | Bin 918 -> 0 bytes console-proxy/images/cannotconnect.jpg | Bin 1810 -> 0 bytes console-proxy/images/clr_button.gif | Bin 1274 -> 0 bytes console-proxy/images/clr_button_hover.gif | Bin 437 -> 0 bytes console-proxy/images/dot.cur | Bin 326 -> 0 bytes console-proxy/images/gray-green.png | Bin 3833 -> 0 bytes console-proxy/images/grid_headerbg.gif | Bin 196 -> 0 bytes console-proxy/images/left.png | Bin 3024 -> 0 bytes console-proxy/images/minimize_button.gif | Bin 634 -> 0 bytes .../images/minimize_button_hover.gif | Bin 227 -> 0 bytes console-proxy/images/notready.jpg | Bin 1827 -> 0 bytes console-proxy/images/play_button.gif | Bin 657 -> 0 bytes console-proxy/images/play_button_hover.gif | Bin 243 -> 0 bytes console-proxy/images/right.png | Bin 3131 -> 0 bytes console-proxy/images/right2.png | Bin 3156 -> 0 bytes console-proxy/images/shrink_button.gif | Bin 655 -> 0 bytes console-proxy/images/shrink_button_hover.gif | Bin 243 -> 0 bytes console-proxy/images/stop_button.gif | Bin 649 -> 0 bytes console-proxy/images/stop_button_hover.gif | Bin 231 -> 0 bytes console-proxy/images/winlog.png | Bin 2629 -> 0 bytes console-proxy/js/ajaxkeys.js | 77 - console-proxy/js/ajaxviewer.js | 1444 ----------------- console-proxy/js/cloud.logger.js | 338 ---- console-proxy/js/handler.js | 72 - console-proxy/js/jquery.js | 19 - console-proxy/libexec/console-proxy-runner.in | 90 - console-proxy/pom.xml | 246 --- console-proxy/scripts/_run.sh | 63 - console-proxy/scripts/config_auth.sh | 69 - console-proxy/scripts/config_ssl.sh | 174 -- console-proxy/scripts/ipfirewall.sh | 50 - console-proxy/scripts/run-proxy.sh | 48 - console-proxy/scripts/run.bat | 18 - console-proxy/scripts/run.sh | 45 - console-proxy/scripts/ssvm-check.sh | 136 -- .../consoleproxy/AjaxFIFOImageCache.java | 83 - .../consoleproxy/AuthenticationException.java | 33 - .../com/cloud/consoleproxy/ConsoleProxy.java | 499 ------ .../consoleproxy/ConsoleProxyAjaxHandler.java | 406 ----- .../ConsoleProxyAjaxImageHandler.java | 159 -- .../ConsoleProxyAuthenticationResult.java | 81 - .../ConsoleProxyBaseServerFactoryImpl.java | 48 - .../consoleproxy/ConsoleProxyClient.java | 69 - .../consoleproxy/ConsoleProxyClientBase.java | 457 ------ .../ConsoleProxyClientListener.java | 25 - .../consoleproxy/ConsoleProxyClientParam.java | 110 -- .../ConsoleProxyClientStatsCollector.java | 88 - .../consoleproxy/ConsoleProxyCmdHandler.java | 70 - .../consoleproxy/ConsoleProxyGCThread.java | 109 -- .../ConsoleProxyHttpHandlerHelper.java | 74 - .../ConsoleProxyLoggerFactory.java | 89 - .../consoleproxy/ConsoleProxyMonitor.java | 153 -- .../ConsoleProxyPasswordBasedEncryptor.java | 142 -- .../ConsoleProxyResourceHandler.java | 181 --- .../ConsoleProxySecureServerFactoryImpl.java | 145 -- .../ConsoleProxyServerFactory.java | 29 - .../ConsoleProxyThumbnailHandler.java | 212 --- .../consoleproxy/ConsoleProxyVncClient.java | 235 --- .../cloud/consoleproxy/InputEventType.java | 58 - .../consoleproxy/util/ITileScanListener.java | 25 - .../cloud/consoleproxy/util/ImageHelper.java | 32 - .../com/cloud/consoleproxy/util/Logger.java | 223 --- .../consoleproxy/util/LoggerFactory.java | 21 - .../com/cloud/consoleproxy/util/RawHTTP.java | 249 --- .../com/cloud/consoleproxy/util/Region.java | 90 - .../consoleproxy/util/RegionClassifier.java | 58 - .../com/cloud/consoleproxy/util/TileInfo.java | 55 - .../cloud/consoleproxy/util/TileTracker.java | 269 --- .../consoleproxy/vnc/BufferedImageCanvas.java | 150 -- .../consoleproxy/vnc/FrameBufferCanvas.java | 30 - .../vnc/FrameBufferUpdateListener.java | 26 - .../vnc/PaintNotificationListener.java | 27 - .../cloud/consoleproxy/vnc/RfbConstants.java | 82 - .../com/cloud/consoleproxy/vnc/VncClient.java | 451 ----- .../vnc/VncClientPacketSender.java | 258 --- .../vnc/VncScreenDescription.java | 89 - .../vnc/VncServerPacketReceiver.java | 123 -- .../vnc/packet/client/ClientPacket.java | 26 - .../FramebufferUpdateRequestPacket.java | 53 - .../packet/client/KeyboardEventPacket.java | 42 - .../vnc/packet/client/MouseEventPacket.java | 43 - .../vnc/packet/client/SetEncodingsPacket.java | 45 - .../packet/client/SetPixelFormatPacket.java | 75 - .../vnc/packet/server/AbstractRect.java | 53 - .../vnc/packet/server/CopyRect.java | 39 - .../server/FrameBufferSizeChangeRequest.java | 39 - .../server/FramebufferUpdatePacket.java | 102 -- .../vnc/packet/server/RawRect.java | 75 - .../consoleproxy/vnc/packet/server/Rect.java | 33 - .../vnc/packet/server/ServerCutText.java | 49 - console-proxy/systemvm-descriptor.xml | 113 -- console-proxy/ui/viewer-bad-sid.ftl | 29 - console-proxy/ui/viewer-connect-failed.ftl | 29 - console-proxy/ui/viewer-update.ftl | 24 - console-proxy/ui/viewer.ftl | 60 - console-proxy/vm-script/vmops | 119 -- pom.xml | 2 +- 119 files changed, 2 insertions(+), 11094 deletions(-) delete mode 100755 console-proxy/bindir/cloud-setup-console-proxy.in delete mode 100644 console-proxy/certs/localhost.crt delete mode 100644 console-proxy/certs/localhost.key delete mode 100644 console-proxy/certs/realhostip.crt delete mode 100644 console-proxy/certs/realhostip.csr delete mode 100644 console-proxy/certs/realhostip.key delete mode 100644 console-proxy/certs/realhostip.keystore delete mode 100644 console-proxy/conf.dom0/agent.properties.in delete mode 100644 console-proxy/conf.dom0/consoleproxy.properties.in delete mode 100644 console-proxy/conf.dom0/log4j-cloud.xml.in delete mode 100644 console-proxy/conf/agent.properties delete mode 100644 console-proxy/conf/consoleproxy.properties delete mode 100644 console-proxy/conf/log4j-cloud.xml delete mode 100644 console-proxy/css/ajaxviewer.css delete mode 100644 console-proxy/css/logger.css delete mode 100644 console-proxy/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in delete mode 100644 console-proxy/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in delete mode 100644 console-proxy/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in delete mode 100755 console-proxy/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in delete mode 100644 console-proxy/images/back.gif delete mode 100644 console-proxy/images/bright-green.png delete mode 100644 console-proxy/images/cad.gif delete mode 100644 console-proxy/images/cannotconnect.jpg delete mode 100644 console-proxy/images/clr_button.gif delete mode 100644 console-proxy/images/clr_button_hover.gif delete mode 100644 console-proxy/images/dot.cur delete mode 100644 console-proxy/images/gray-green.png delete mode 100644 console-proxy/images/grid_headerbg.gif delete mode 100644 console-proxy/images/left.png delete mode 100644 console-proxy/images/minimize_button.gif delete mode 100644 console-proxy/images/minimize_button_hover.gif delete mode 100644 console-proxy/images/notready.jpg delete mode 100644 console-proxy/images/play_button.gif delete mode 100644 console-proxy/images/play_button_hover.gif delete mode 100644 console-proxy/images/right.png delete mode 100644 console-proxy/images/right2.png delete mode 100644 console-proxy/images/shrink_button.gif delete mode 100644 console-proxy/images/shrink_button_hover.gif delete mode 100644 console-proxy/images/stop_button.gif delete mode 100644 console-proxy/images/stop_button_hover.gif delete mode 100644 console-proxy/images/winlog.png delete mode 100644 console-proxy/js/ajaxkeys.js delete mode 100644 console-proxy/js/ajaxviewer.js delete mode 100644 console-proxy/js/cloud.logger.js delete mode 100644 console-proxy/js/handler.js delete mode 100644 console-proxy/js/jquery.js delete mode 100755 console-proxy/libexec/console-proxy-runner.in delete mode 100644 console-proxy/pom.xml delete mode 100755 console-proxy/scripts/_run.sh delete mode 100755 console-proxy/scripts/config_auth.sh delete mode 100755 console-proxy/scripts/config_ssl.sh delete mode 100755 console-proxy/scripts/ipfirewall.sh delete mode 100644 console-proxy/scripts/run-proxy.sh delete mode 100644 console-proxy/scripts/run.bat delete mode 100755 console-proxy/scripts/run.sh delete mode 100644 console-proxy/scripts/ssvm-check.sh delete mode 100644 console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClient.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/InputEventType.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/Logger.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/Region.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java delete mode 100644 console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java delete mode 100644 console-proxy/systemvm-descriptor.xml delete mode 100644 console-proxy/ui/viewer-bad-sid.ftl delete mode 100644 console-proxy/ui/viewer-connect-failed.ftl delete mode 100644 console-proxy/ui/viewer-update.ftl delete mode 100644 console-proxy/ui/viewer.ftl delete mode 100644 console-proxy/vm-script/vmops diff --git a/client/pom.xml b/client/pom.xml index 3651b79bbb3..0c37df3a5f5 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -273,7 +273,7 @@ - + diff --git a/console-proxy/bindir/cloud-setup-console-proxy.in b/console-proxy/bindir/cloud-setup-console-proxy.in deleted file mode 100755 index 6439c0fc329..00000000000 --- a/console-proxy/bindir/cloud-setup-console-proxy.in +++ /dev/null @@ -1,220 +0,0 @@ -#!/usr/bin/env python - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -import sys, os, subprocess, errno, re, getopt - -# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- -# ---- We do this so cloud_utils can be looked up in the following order: -# ---- 1) Sources directory -# ---- 2) waf configured PYTHONDIR -# ---- 3) System Python path -for pythonpath in ( - "@PYTHONDIR@", - os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), - ): - if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) -# ---- End snippet of code ---- -import cloud_utils -from cloud_utils import stderr - -E_GENERIC= 1 -E_NOKVM = 2 -E_NODEFROUTE = 3 -E_DHCP = 4 -E_NOPERSISTENTNET = 5 -E_NETRECONFIGFAILED = 6 -E_VIRTRECONFIGFAILED = 7 -E_FWRECONFIGFAILED = 8 -E_CPRECONFIGFAILED = 9 -E_CPFAILEDTOSTART = 10 -E_NOFQDN = 11 - -def bail(errno=E_GENERIC,message=None,*args): - if message: stderr(message,*args) - stderr("Cloud Console Proxy setup aborted") - sys.exit(errno) - - -#---------------- boilerplate for python 2.4 support - - -# CENTOS does not have this -- we have to put this here -try: - from subprocess import check_call - from subprocess import CalledProcessError -except ImportError: - def check_call(*popenargs, **kwargs): - import subprocess - retcode = subprocess.call(*popenargs, **kwargs) - cmd = kwargs.get("args") - if cmd is None: cmd = popenargs[0] - if retcode: raise CalledProcessError(retcode, cmd) - return retcode - - class CalledProcessError(Exception): - def __init__(self, returncode, cmd): - self.returncode = returncode ; self.cmd = cmd - def __str__(self): return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) - -# ------------ end boilerplate ------------------------- - -def check_hostname(): return check_call(["hostname",'--fqdn']) - -class Command: - def __init__(self,name,parent=None): - self.__name = name - self.__parent = parent - def __getattr__(self,name): - if name == "_print": name = "print" - return Command(name,self) - def __call__(self,*args): - cmd = self.__get_recursive_name() + list(args) - #print " ",cmd - popen = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) - m = popen.communicate() - ret = popen.wait() - if ret: - e = CalledProcessError(ret,cmd) - e.stdout,e.stderr = m - raise e - class CommandOutput: - def __init__(self,stdout,stderr): - self.stdout = stdout - self.stderr = stderr - return CommandOutput(*m) - def __lt__(self,other): - cmd = self.__get_recursive_name() - #print " ",cmd,"<",other - popen = subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) - m = popen.communicate(other) - ret = popen.wait() - if ret: - e = CalledProcessError(ret,cmd) - e.stdout,e.stderr = m - raise e - class CommandOutput: - def __init__(self,stdout,stderr): - self.stdout = stdout - self.stderr = stderr - return CommandOutput(*m) - - def __get_recursive_name(self,sep=None): - m = self - l = [] - while m is not None: - l.append(m.__name) - m = m.__parent - l.reverse() - if sep: return sep.join(l) - else: return l - def __str__(self): - return ''%self.__get_recursive_name(sep=" ") - - def __repr__(self): return self.__str__() - -ip = Command("ip") -service = Command("service") -chkconfig = Command("chkconfig") -ufw = Command("ufw") -iptables = Command("iptables") -augtool = Command("augtool") -ifconfig = Command("ifconfig") -uuidgen = Command("uuidgen") - -Fedora = os.path.exists("/etc/fedora-release") -CentOS = os.path.exists("/etc/centos-release") or ( os.path.exists("/etc/redhat-release") and not os.path.exists("/etc/fedora-release") ) - -#--------------- procedure starts here ------------ - -def main(): - # parse cmd line - opts, args = getopt.getopt(sys.argv[1:], "a", ["host=", "zone=", "pod="]) - host=None - zone=None - pod=None - autoMode=False - do_check_kvm = True - for opt, arg in opts: - if opt == "--host": - if arg != "": - host = arg - elif opt == "--zone": - if arg != "": - zone = arg - elif opt == "--pod": - if arg != "": - pod = arg - elif opt == "-a": - autoMode=True - servicename = "@PACKAGE@-console-proxy" - - if autoMode: - cloud_utils.setLogFile("/var/log/cloud/setupConsoleProxy.log") - - stderr("Welcome to the Cloud Console Proxy setup") - stderr("") - - try: - check_hostname() - stderr("The hostname of this machine is properly set up") - except CalledProcessError,e: - bail(E_NOFQDN,"This machine does not have an FQDN (fully-qualified domain name) for a hostname") - - stderr("Stopping the Cloud Console Proxy") - cloud_utils.stop_service(servicename) - stderr("Cloud Console Proxy stopped") - - ports = "8002".split() - if Fedora or CentOS: - try: - o = chkconfig("--list","iptables") - if ":on" in o.stdout and os.path.exists("/etc/sysconfig/iptables"): - stderr("Setting up firewall rules to permit traffic to Cloud services") - service.iptables.start() ; print o.stdout + o.stderr - for p in ports: iptables("-I","INPUT","1","-p","tcp","--dport",p,'-j','ACCEPT') - o = service.iptables.save() ; print o.stdout + o.stderr - except CalledProcessError,e: - print e.stdout+e.stderr - bail(E_FWRECONFIGFAILED,"Firewall rules could not be set") - else: - stderr("Setting up firewall rules to permit traffic to Cloud services") - try: - for p in ports: ufw.allow(p) - stderr("Rules set") - except CalledProcessError,e: - print e.stdout+e.stderr - bail(E_FWRECONFIGFAILED,"Firewall rules could not be set") - - stderr("We are going to enable ufw now. This may disrupt network connectivity and service availability. See the ufw documentation for information on how to manage ufw firewall policies.") - try: - o = ufw.enable < "y\n" ; print o.stdout + o.stderr - except CalledProcessError,e: - print e.stdout+e.stderr - bail(E_FWRECONFIGFAILED,"Firewall could not be enabled") - - cloud_utils.setup_consoleproxy_config("@CPSYSCONFDIR@/agent.properties", host, zone, pod) - stderr("Enabling and starting the Cloud Console Proxy") - cloud_utils.enable_service(servicename) - stderr("Cloud Console Proxy restarted") - -if __name__ == "__main__": - main() - -# FIXMES: 1) nullify networkmanager on ubuntu (asking the user first) and enable the networking service permanently diff --git a/console-proxy/certs/localhost.crt b/console-proxy/certs/localhost.crt deleted file mode 100644 index 005d98b8c72..00000000000 --- a/console-proxy/certs/localhost.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDrTCCApWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJVUzEL -MAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzESMBAGA1UECgwJQ2xvdWQu -Y29tMRAwDgYDVQQLDAdEZWZhdWx0MRswGQYDVQQDDBJTZWNvbmRhcnlTdG9yYWdl -Vk0wHhcNMTAwNTI3MTgzNjI1WhcNMTMwMjIwMTgzNjI1WjBxMQswCQYDVQQGEwJV -UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzESMBAGA1UECgwJQ2xv -dWQuY29tMRAwDgYDVQQLDAdEZWZhdWx0MRswGQYDVQQDDBJTZWNvbmRhcnlTdG9y -YWdlVk0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbhstQ5Gn2gzrk -ZX1es+tuz4rnrcONRHUzyY/UdoT5jiVfmmS9CML/GgSpKzmnEMNZcCSh7G/GKPwD -gBZywpTVD56nYT4ZzK0GjPxcg0a+BvxxA2esQ2/kFBvtdcZ1TNExtjdOqysjK0It -M6U2891wbn+Y9oHqooTA0uaZELTpe/MCg2eBx7A4+u26novHHfOaKEEqtBscpDP8 -0+nQduNQf61haV25Lv2CDqrCIuv/FrNmgQhcUg8e1dFkk4VCsflEDuSYh9PpaD7J -t+oqmNTVw8k6u3JAYJFkcu457uYz0wrED7Cai7Y6gUy7xwmY2SSY/r2mJJHEpSpZ -NhiH47kZAgMBAAGjUDBOMB0GA1UdDgQWBBQ2hUX5Jdhn277SBisACnEABqg52zAf -BgNVHSMEGDAWgBQ2hUX5Jdhn277SBisACnEABqg52zAMBgNVHRMEBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQBAVrkGGDPHDPQdZRgI1+1L87sX5xdNoet9sJUVRtz9 -ZwhGWAmca30cJGlhSFNx5y01E6T7lHDLrF9HCf9nVC10t0FwQwTVYijE8VyM7fAA -4Hv/whSKg1kkQQSTis2ZW0wMU6TnanhJy6MrxwdhRampeXjNOpNtoLrF/5LbhWxO -Gm0S5u+4q7eYnUPD4o3sb17idG62kkejHeToPnJwXtDwyq0XYNlL/OoqRaaY5f0b -IKdeqqEkdtkzfU4N1dG7bJA29gBl48gPn+CSrh9u3D0s1OYM7MWi1/TjpwCR18ir -CslVzO6kVNyQoNEYhZ9+2Sz0ceZVrYDFFzp8qAF8qbZ7 ------END CERTIFICATE----- diff --git a/console-proxy/certs/localhost.key b/console-proxy/certs/localhost.key deleted file mode 100644 index 6d95765e4f0..00000000000 --- a/console-proxy/certs/localhost.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAm4bLUORp9oM65GV9XrPrbs+K563DjUR1M8mP1HaE+Y4lX5pk -vQjC/xoEqSs5pxDDWXAkoexvxij8A4AWcsKU1Q+ep2E+GcytBoz8XINGvgb8cQNn -rENv5BQb7XXGdUzRMbY3TqsrIytCLTOlNvPdcG5/mPaB6qKEwNLmmRC06XvzAoNn -gcewOPrtup6Lxx3zmihBKrQbHKQz/NPp0HbjUH+tYWlduS79gg6qwiLr/xazZoEI -XFIPHtXRZJOFQrH5RA7kmIfT6Wg+ybfqKpjU1cPJOrtyQGCRZHLuOe7mM9MKxA+w -mou2OoFMu8cJmNkkmP69piSRxKUqWTYYh+O5GQIDAQABAoIBAQCI5S8VNtimaYBv -BX5C26+BzCECKIsWT4myWdrGMsR9PUdcTXQaiBnLncU4epm2miS5FuLHvGvuSqj5 -E8eun+ONXsBRqGCXKPer6nE/pTWhklilyU9566oTYjfq3l4fZcxFK5SnJDdGL4+C -ZhEou6LQkhKyO1jDhOXBLGJZnMEBOf+zXhgZGLDbQwCcCQ5PAZUiSf0cnVRk3rI9 -GwdsbCDNJk6awy8ANlFATemDvHwHZ7ZwmvVzsjsYlYJbY/vJYlOyxa7tzYROVAlm -m8oiPfDvGjnXcGxVQwT0pgDvwtUkUFijZORpkJOPavxqSkpOzDFrOe9UW6HJYGzH -ujhmZBVhAoGBAMnbfssRwYI5egfsxNbA62NkxRcGe5HIlnyZ1Ln0BCE8ae60frcf -4IluZzT53Ly3jUQP2uGdp6nJVq/5ymRYSySNr46DXek2qcJ2TMbtRBhjebPwi+Rb -qTNjccNSgqs0j+1qP78PoTUO5fUWGL9XqIlfPU6Vji4+SmdNMvz6z84lAoGBAMU9 -/3l2WVXykD6FNrRbtvpzU1C9LDixOaVVpGnZHO8krW440LnhIZJbkXnXDVZXc7eI -D/doecfL8rtFteqeBr0LiXGQXU5mIMjXAOAPSvDqIciYzhL8KOK5CWEgVZo6RQlU -G3ne0mk93I+w8N0SE2VKeuxepz4yw0oiKUpAgWrlAoGAS06qNRSAvxa2YjKBFSWQ -K9qydO6kNzVAf2fcpytURxoE41tPUv5/hIY91tPI+Fb6SwQnQrjQjlVhE/H7Agi2 -sAJ0FpUH+jO8jaIY7rYiC39BLlJ1vlI8A8H79UTZHwpTD93tvlgUankObas6vFf1 -tppjgufkzXfLxlJUzXC9CkkCgYA7gy9YOKtP0XZQRuVwmdOl0bIrxEhZeq/IAQUw -or+mMEzb2uyviQwWGubT+l0d1hkmITmgDrff3tuIQcpX1gJ2e8qmp0Zf51SxBJ5Q -/IxCEILNAb374HV9oxL/aUAq3rYB0IzRwrd95ZptCJhEO7X6c/SO6ShRDgP6lEAd -FUV3OQKBgQCFC0Xx/fCX1yquARgoe5pbK7IpXWaTvjBu//gbHsfR2lk3dZbESdeg -OquPDdfp+jFPGISsDhPLzcfkZIPbz5ZVs8KdmpB/YLwyJwFqjDyjwVaDnRnuycb1 -/4PlVWKp7j5SDDNCfleYvmiRn8k6P4mxVJOHKzwb/IwQcKghyqAF1w== ------END RSA PRIVATE KEY----- diff --git a/console-proxy/certs/realhostip.crt b/console-proxy/certs/realhostip.crt deleted file mode 100644 index 7520b0ca6c4..00000000000 --- a/console-proxy/certs/realhostip.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFZTCCBE2gAwIBAgIHKBCduBUoKDANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE -BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY -BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydGlm -aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkxMDAuBgNVBAMTJ0dvIERhZGR5 -IFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTERMA8GA1UEBRMIMDc5Njky -ODcwHhcNMTIwMjAzMDMzMDQwWhcNMTcwMjA3MDUxMTIzWjBZMRkwFwYDVQQKDBAq -LnJlYWxob3N0aXAuY29tMSEwHwYDVQQLDBhEb21haW4gQ29udHJvbCBWYWxpZGF0 -ZWQxGTAXBgNVBAMMECoucmVhbGhvc3RpcC5jb20wggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCDT9AtEfs+s/I8QXp6rrCw0iNJ0+GgsybNHheU+JpL39LM -TZykCrZhZnyDvwdxCoOfE38Sa32baHKNds+y2SHnMNsOkw8OcNucHEBX1FIpOBGp -h9D6xC+umx9od6xMWETUv7j6h2u+WC3OhBM8fHCBqIiAol31/IkcqDxxsHlQ8S/o -CfTlXJUY6Yn628OA1XijKdRnadV0hZ829cv/PZKljjwQUTyrd0KHQeksBH+YAYSo -2JUl8ekNLsOi8/cPtfojnltzRI1GXi0ZONs8VnDzJ0a2gqZY+uxlz+CGbLnGnlN4 -j9cBpE+MfUE+35Dq121sTpsSgF85Mz+pVhn2S633AgMBAAGjggG+MIIBujAPBgNV -HRMBAf8EBTADAQEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNV -HQ8BAf8EBAMCBaAwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nb2RhZGR5 -LmNvbS9nZHMxLTY0LmNybDBTBgNVHSAETDBKMEgGC2CGSAGG/W0BBxcBMDkwNwYI -KwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3Np -dG9yeS8wgYAGCCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au -Z29kYWRkeS5jb20vMEoGCCsGAQUFBzAChj5odHRwOi8vY2VydGlmaWNhdGVzLmdv -ZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RfaW50ZXJtZWRpYXRlLmNydDAfBgNVHSME -GDAWgBT9rGEyk2xF1uLuhV+auud2mWjM5zArBgNVHREEJDAighAqLnJlYWxob3N0 -aXAuY29tgg5yZWFsaG9zdGlwLmNvbTAdBgNVHQ4EFgQUZyJz9/QLy5TWIIscTXID -E8Xk47YwDQYJKoZIhvcNAQEFBQADggEBAKiUV3KK16mP0NpS92fmQkCLqm+qUWyN -BfBVgf9/M5pcT8EiTZlS5nAtzAE/eRpBeR3ubLlaAogj4rdH7YYVJcDDLLoB2qM3 -qeCHu8LFoblkb93UuFDWqRaVPmMlJRnhsRkL1oa2gM2hwQTkBDkP7w5FG1BELCgl -gZI2ij2yxjge6pOEwSyZCzzbCcg9pN+dNrYyGEtB4k+BBnPA3N4r14CWbk+uxjrQ -6j2Ip+b7wOc5IuMEMl8xwTyjuX3lsLbAZyFI9RCyofwA9NqIZ1GeB6Zd196rubQp -93cmBqGGjZUs3wMrGlm7xdjlX6GQ9UvmvkMub9+lL99A5W50QgCmFeI= ------END CERTIFICATE----- diff --git a/console-proxy/certs/realhostip.csr b/console-proxy/certs/realhostip.csr deleted file mode 100644 index 61395c9f8d9..00000000000 --- a/console-proxy/certs/realhostip.csr +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN NEW CERTIFICATE REQUEST----- -MIICsDCCAZgCAQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlDdXBlcnRp -bm8xDjAMBgNVBAoTBVZNT3BzMRAwDgYDVQQLEwdVbmtub3duMRkwFwYDVQQDDBAqLnJlYWxob3N0 -aXAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg0/QLRH7PrPyPEF6eq6wsNIj -SdPhoLMmzR4XlPiaS9/SzE2cpAq2YWZ8g78HcQqDnxN/Emt9m2hyjXbPstkh5zDbDpMPDnDbnBxA -V9RSKTgRqYfQ+sQvrpsfaHesTFhE1L+4+odrvlgtzoQTPHxwgaiIgKJd9fyJHKg8cbB5UPEv6An0 -5VyVGOmJ+tvDgNV4oynUZ2nVdIWfNvXL/z2SpY48EFE8q3dCh0HpLAR/mAGEqNiVJfHpDS7DovP3 -D7X6I55bc0SNRl4tGTjbPFZw8ydGtoKmWPrsZc/ghmy5xp5TeI/XAaRPjH1BPt+Q6tdtbE6bEoBf -OTM/qVYZ9kut9wIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAF5lhhni9dW9MqSL2ixNbViPWpFS -ecOggshYChJfZKrhsuZaDpumJ/+ebICS4zv/oxDwNLSmeAmydiaUQC9LFQEEwvPBYDTtTzwCrtwH -yyFJQSm6pyeIBP/Bih/5hLW8JPm0bDbp5ldrHCDEgKQeeyQhyYOKFODkTuMLw+FLD+V86IVHxElL -/urCRWyZEPwyMsgfsU6ywNX9XNShyk1uDHjFDE67sPhfw52ooxXrYQnBdTk+g0UXPbULzrCK/1kU -fjRq347V9Fwi5NFyGADOaA+q6mtnlb1i3uH1n1YVUzevvpnIr3/RxPSYHB47Kj9iYKeDlYdTRHJy -NpuvTfmQO2Y= ------END NEW CERTIFICATE REQUEST----- diff --git a/console-proxy/certs/realhostip.key b/console-proxy/certs/realhostip.key deleted file mode 100644 index 53bdc86e273..00000000000 --- a/console-proxy/certs/realhostip.key +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCDT9AtEfs+s/I8QXp6rrCw0iNJ -0+GgsybNHheU+JpL39LMTZykCrZhZnyDvwdxCoOfE38Sa32baHKNds+y2SHnMNsOkw8OcNucHEBX -1FIpOBGph9D6xC+umx9od6xMWETUv7j6h2u+WC3OhBM8fHCBqIiAol31/IkcqDxxsHlQ8S/oCfTl -XJUY6Yn628OA1XijKdRnadV0hZ829cv/PZKljjwQUTyrd0KHQeksBH+YAYSo2JUl8ekNLsOi8/cP -tfojnltzRI1GXi0ZONs8VnDzJ0a2gqZY+uxlz+CGbLnGnlN4j9cBpE+MfUE+35Dq121sTpsSgF85 -Mz+pVhn2S633AgMBAAECggEAH/Szd9RxbVADenCA6wxKSa3KErRyq1YN8ksJeCKMAj0FIt0caruE -qO11DebWW8cwQu1Otl/cYI6pmg24/BBldMrp9IELX/tNJo+lhPpRyGAxxC0eSXinFfoASb8d+jJd -Bd1mmemM6fSxqRlxSP4LrzIhjhR1g2CiyYuTsiM9UtoVKGyHwe7KfFwirUOJo3Mr18zUVNm7YqY4 -IVhOSq59zkH3ULBlYq4bG50jpxa5mNSCZ7IpafPY/kE/CbR+FWNt30+rk69T+qb5abg6+XGm+OAm -bnQ18yZEqX6nJLk7Ch0cfA5orGgrTMOrM71wK7tBBDQ308kOxDGebx6j0qD36QKBgQDTRDr8kuhA -9sUyKr9vk2DQCMpNvEeiwI3JRMqmmxpNAtg01aJ3Ya57vX5Fc+zcuV87kP6FM1xgpHQvnw5LWo2J -s7ANwQcP8ricEW5zkZhSjI4ssMeAubmsHOloGxmLFYZqwx0JI7CWViGTLMcUlqKblmHcjeQDeDfP -P1TaCItFmwKBgQCfHZwVvIcaDs5vxVpZ4ftvflIrW8qq0uOVK6QIf9A/YTGhCXl2qxxTg2A6+0rg -ZqI7zKzUDxIbVv0KlgCbpHDC9d5+sdtDB3wW2pimuJ3p1z4/RHb4n/lDwXCACZl1S5l24yXX2pFZ -wdPCXmy5PYkHMssFLNhI24pprUIQs66M1QKBgQDQwjAjWisD3pRXESSfZRsaFkWJcM28hdbVFhPF -c6gWhwQLmTp0CuL2RPXcPUPFi6sN2iWWi3zxxi9Eyz+9uBn6AsOpo56N5MME/LiOnETO9TKb+Ib6 -rQtKhjshcv3XkIqFPo2XdVvOAgglPO7vajX91iiXXuH7h7RmJud6l0y/lwKBgE+bi90gLuPtpoEr -VzIDKz40ED5bNYHT80NNy0rpT7J2GVN9nwStRYXPBBVeZq7xCpgqpgmO5LtDAWULeZBlbHlOdBwl -NhNKKl5wzdEUKwW0yBL1WSS5PQgWPwgARYP25/ggW22sj+49WIo1neXsEKPGWObk8e050f1fTt92 -Vo1lAoGAb1gCoyBCzvi7sqFxm4V5oapnJeiQQJFjhoYWqGa26rQ+AvXXNuBcigIeDXNJPctSF0Uc -p11KbbCgiruBbckvM1vGsk6Sx4leRk+IFHRpJktFUek4o0eUg0shOsyyvyet48Dfg0a8FvcxROs0 -gD+IYds5doiob/hcm1hnNB/3vk4= ------END PRIVATE KEY----- \ No newline at end of file diff --git a/console-proxy/certs/realhostip.keystore b/console-proxy/certs/realhostip.keystore deleted file mode 100644 index c8d54d47ebabe889d3d9bf6ed5b5e50ccd9c1c4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8690 zcmeI1c|4Ts-^b^kjqHpq>qPeLo?(U&N>M~f)?%u6D%rP)l!&O4 zB})sXB1F-Gick`f<+(>kr*nSi_j+FE`QtoWf6Q{twaj&2*Z2P1@6Y#I99tZN!C-Lc zg{evA#O{G%h#C z9%;HRSJu#Du0ku;cb~r4!(4ly-7o)9) zk6()9Vzhb^F1d>sXLF;tt6U2x21xT%xxAl#Vm6x=&^@F*n&oNkQX6aGwv?iaijw>0 z4}MVm4^{VT zrd)Q;?ZQV4n9ZM;Dyd0{k<5|P(wE=>B9C1UvTuO0(EiE_AjOfQkDAIJo#?a{4yqux z8s{uNE85rj3S`G~>J^dfD3Bc<_LpsPyz?pl$gYM%5zWV$4hD$=5dg6C{T4&<4Q(+1 z!&`kUziV=0UZf?5>v#eyTNf?rnj|MV!+p+}XZ37Mm~a4f@RS6&q<6xTZZO^^1LVn` zRL#dtA8fMko*u$EBXZmBd-R<&XH?di-&tGvI2>N87($J&uG)6L-TJPs;v1RR;*4Xa z0pfSCLvfk+*7XL?97$EiirOoc1Tdn8)7E70_=O7;eZI*zCnV>0Y9fZeNsxT3bUZ>M z<#R%EB%L&h9f=r=xOX~$``6L!%2f=F!H#L`MdhHEWMSskB`2vE!C(vRc3Y}MHe7_9 z^7!F~av|&SrVB!w&J|?xJWlaA%ikUw-i+rP)W|X)=Fd`1zg(m4;#8_Hcu6+2??x6R z5CDvaP9gjG&=^c=;Id4d5}ky>PynP43!ng+M*|HY0d=@AqzW)(IP&eY8xeKKpDS1; z9X;LX)0>txYhF@dyp@*%ekM&g3AVmJR_O75$maCDeAi-LZ_O*r={Y(W6noQ6nQPoy5WQ;)i#prd zk~Oo$rBjr0QAAC?%_LE z)AzL(*tN4R^O3O^rQAw6%&hC?iG(LM)yWygs?Bp(M?kFP z^F#Q%=^yAj9X|y44oBQY`mA*yhGAwR$P1UvGBiFAU8mu@3lc9_bqTb;*6^h}q}{jZ zTF2^9IW{J2=uA{0P%)oAa1H0&KpoII?W=p+Y>cw^WV+dh8i9v4xng$iWs$Qy*?ji{ z3)*I{3SXUPwM#TD<)w~R*Cda!h59_k2ugKV1x}~> z=6V9ot^6+ROl?e6@dMwEmImeAj89j{{%dd_pE5Q&l@5F{HBbpE^3}*+Qmjy24JXSu zD5Kn$f{Q(a8dV6hrMR=vt{%VVcT~UA1oc20GnkQX92#xB0aXlWXr3U0iZcJ93o=*j*~Slt?V3Bg<7) zukA>Zd|f8(%aqw4H|R!eKfK?^V5vv#`L@r^-Gz~l=ENhbh4#7L(8-p0-Dc`Kx!`~G zsw$d-u74nwmS~H7Rozwkbm(@6ZfDz!xAgU&gwi|RG>f&kV)=aQ z-nA~aq#NC`Wij-QxmHaGlo9f8xW!OJUx^>|u|4OaFVo=tL0P8|-Iif~EhB2J(Jey{ z#g{R%S{37!1)A}~lMKNNn^ecIPek@#6cNvj8^-&XH!(wKsGgJz?ojm)+FT`Xw{u0r z>0g#Svb*fnZ9TGVvQlS$q5Je!ht3urfoS2m#V;+9U9EMSz4N+`jfU;=ZnYpaZV2S= zQ&%3jh9eVFdRjJG;!NkUgA;}%BU7oRsGXb1!Uj-BQ^;^ z5(+U9H-^iUPCZBqAmcEgFy|L843C{Bjmczqk^Lw*DNvI0BM(M+3vC&!v^{D5zhRXw zh~w1pV$^+@%s@jO9nWttMPX=r)4Vt!#%ZrZrv%a%R3?ocf&)QqPA3FLjLh=tp+8Q z=glXiuKoLCgfp2HVC3bKG^6>GsR7tcv;ZcZ=7)78`%%5fOp4b^Z^Xa$hS*Et`=$NL zWP%(f<5_012%865!e2}(CXGxF9=upq_DIpP<3)Cb@;y0;lb_F84LoYwmY&a3Mc#WT zqK+$wCn5_ID&l)M&4(VpzrFm)n%7{Tz%POVfqm&R8=N|Ksu>8Mk9jcHq;oM%-iLL; z+S#nLu6i!Ux7Jzfek8`|P$0V~HZ0fe(|nvvkx@`t$c_&>Z&pnWxu!_IjhpLh410Va zPp#9N`j{D&Mf}vdWSm@(U?gN~RKnUAWBOJT8F~hYEb2*7`tX)tyD|6Uj9}%QV&-m! zS^VZbTB{BEj2r_$s%)+bKj%F6j?(@-+OMW1)9yfGH;`|0{IIFXK+;IJzu)#WkuZ0X z?)vkNtEa6>XW$3`2IPeUwa|)OTi!WY3^HmD12qO3ep6I2IPvz4>gMk}m@8r|;?5$5g1z-bZnv_0v5pdF_Z7HJJ` z1uf9LKSf&r(F^_nmjnQkK>d|Jg9G9kD`WlVVXFhO!&Z6%0FnvP!L=)85QvuiZ6ul} zBk&K?0=Ir^1&=oQ8~^9+DwzP!3+ELu#dzESfU(3_`!{&~#xo6R{jUbJVO)jPV*i>H`)Dk-ggad|aw zS9Dd_y_^Q*Ad)0FAz&`O!%S0EiJeS5YFyr8AUE<$WP@fZuTkHs+s63=8N@2Qq?PGQ z8#bCz|FmDDJM2_|&BYeO2P4L@g~MO!Uy~GGA@T0G2BW;1!$W0N_1O&ciC8Q3rsz}yHs zC-(B2<|1E0dfN|%BU-*{E)?^BTCab^Ni3UGA`XWmf;fVnu0AA#`0tDVw>1Ah6W%7R z$709Rm95|538U`Sy8xGSWcIFgfu1F=o*Lg$ufC((5i)2vW&|omCRBi;Rpl{_#wqs5 z%z@Nryh?Iw`YCEh2u0`ZN2h1-q)ugrl+uYWi_X#AnNzU%;l1*#&iv54P$QR*2!AivbvZD)+)}vm4xPuOA@%xyouwb zY$7U-`%JsxC8d~p%aOtb?-}!i(`}-pm&B;bBU1Vc;?qIGXPKb*BIab?`bd7872(B0 z!mIt=-TjwJ!AY5{NXcJ1W)V(J;Q{_Frmp2z}h}@ANM!&F?nPS(f-)d$#!Ik8z`{dO7la2)#5M>mF zU-R>wTRcT;^OO2FcPJ@}BD`-3bZi)$FX^(3H8R$mQdO{LLRT2R&PLlJeEm zfG2cKwE=xV{)`@U@#Z`Zn0GAU~<2kyUHD6}{sYA#?% zr1hFtnCF__>FhS!Zc@YC|2P-ppxF|WSDAVN9T^iEjGnI(wH9a#Xc{oSDiSpH=7a49 z1y#%EvQ{xgdTr?3XCf{SqlsP!0*%HB7@dsTab`~m!mjj$qNlug`n{TRl&9f~DIaMG z6d}cK{CBbYk&yeA75tHq`;n0Qf18kl!%&`d8iTQ%i!=9j`duNGARPJvy}W+IA%Oo+ ziVCu?;~*-9BpkAr(j1?~$-A!@E>7lsJB7)j(S30epctnW4@O|6)o*5#u_;4!fQ|$1g6vy4?1p@H?f>owg^mpVY_*sE zYq(~{Ss8gG1ZyA!YuSWo-M@ir|1Ge_X2U8s%ofT{k1Xa(ZFDi45$lZF99LhP`RdhZ z^DF1L#N;feq99F+hwSnY`3q>m>ay(u&eEb4XV?#sa<{qeL}VQ> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/console-proxy/conf/agent.properties b/console-proxy/conf/agent.properties deleted file mode 100644 index 4e217f21100..00000000000 --- a/console-proxy/conf/agent.properties +++ /dev/null @@ -1,19 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -instance=ConsoleProxy -resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource diff --git a/console-proxy/conf/consoleproxy.properties b/console-proxy/conf/consoleproxy.properties deleted file mode 100644 index bb452f5823c..00000000000 --- a/console-proxy/conf/consoleproxy.properties +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -consoleproxy.tcpListenPort=0 -consoleproxy.httpListenPort=8088 -consoleproxy.httpCmdListenPort=8001 -consoleproxy.jarDir=./applet/ -consoleproxy.viewerLinger=180 -consoleproxy.reconnectMaxRetry=5 diff --git a/console-proxy/conf/log4j-cloud.xml b/console-proxy/conf/log4j-cloud.xml deleted file mode 100644 index 5b31c9db967..00000000000 --- a/console-proxy/conf/log4j-cloud.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/console-proxy/css/ajaxviewer.css b/console-proxy/css/ajaxviewer.css deleted file mode 100644 index 5ea552b176f..00000000000 --- a/console-proxy/css/ajaxviewer.css +++ /dev/null @@ -1,144 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -body { - margin:0 0; - text-align: center; -} - -#main_panel { - clear:both; - margin: 0 auto; - text-align: left; -} - -.canvas_tile { - cursor:crosshair; -} - -#toolbar { - font:normal 12px 'Trebuchet MS','Arial'; - margin:0 auto; - text-align: left; - padding:0 0; - height:32px; - background-image:url(/resource/images/back.gif); - background-repeat:repeat-x; -} - -#toolbar ul { - margin:0 0; - padding:0 10px 0 10px; - float:left; - display:block; - line-height:32px; - list-style:none; -} - -#toolbar li { - float:left; - display:inline; - padding:0; - height:32px; -} - -#toolbar a { - color:white; - float:left; - display:block; - padding:0 3px 0 3px; - text-decoration:none; - line-height:32px; -} - -#toolbar a span { - display:block; - float:none; - padding:0 10px 0 7px; -} - -#toolbar a span img { - border:none; - margin:8px 4px 0 0; -} - -#toolbar a:hover { - background: url(/resource/images/left.png) no-repeat left center; -} - -#toolbar a:hover span { - background:url(/resource/images/right.png) no-repeat right center; -} - - -#toolbar ul li ul { - position: absolute; - top:32; - width: 260; - height: 65; - display: block; - display: none; - border-top: 1px solid black; - background-image:url(/resource/images/back.gif); - background-repeat:repeat-x repeat-y; -} - -#toolbar ul li ul li { - display: list-item; - float:none; - padding-left: 20; -} - -#toolbar ul li ul li.current { - background: url(/resource/images/cad.gif) no-repeat left center; -} - -#toolbar ul li ul li a { - display:block; - padding:0 3px 0 3px; - text-decoration:none; - line-height:32px; - vertical-align: bottom; /* this is to fix the list gap in IE */ -} - -#toolbar ul li ul li a:hover { - background: url(/resource/images/left.png) no-repeat left center; -} - -#toolbar ul li ul li a:hover span { - background: url(/resource/images/right2.png) no-repeat right center; -} - -span.dark { - margin-right:20px; - float:right; - display:block; - width:32px; - height:30px; - background:url(/resource/images/gray-green.png) no-repeat center center; -} - -span.bright { - margin-right:20px; - float:right; - display:block; - width:32px; - height:30px; - background:url(/resource/images/bright-green.png) no-repeat center center; -} diff --git a/console-proxy/css/logger.css b/console-proxy/css/logger.css deleted file mode 100644 index 42ac8071ed5..00000000000 --- a/console-proxy/css/logger.css +++ /dev/null @@ -1,139 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -@charset "UTF-8"; -.logwin { - position: absolute; - - z-index:2147483648; - width: 800px; - border: 1px solid gray; - background: white; - text-align: left; -} - -.logwin_title{ - width:auto; - height: 23px; - background:url(../images/grid_headerbg.gif) repeat-x top left; - border: 1px sold #737373; -} - -.logwin_title_actionbox{ - width:175px; - height:16px; - float:left; - margin:4px 0 0 7px; - display:inline; -} - - -.logwin_title_actionbox .select { - background: #424242; - font: normal 10px Arial, Helvetica, sans-serif; - float:left; - border: 1px solid #6e6e6e; - height: 16px; - width: 100px; - margin-left:3px; - padding:0 0 0 3px; - color:#CCC; -} - -.logwin_title_rgtactionbox{ - width:49px; - height:15px; - float:right; - margin:4px 0 0 7px; - display:inline; -} - - -a.logwin_playbutton { - width:18px; - height:15px; - float:left; - background:url(../images/play_button.gif) no-repeat top left; - margin-right:2px; - padding:0; -} - -a:hover.logwin_playbutton { - background:url(../images/play_button_hover.gif) no-repeat top left; -} - -a.logwin_stopbutton { - width:18px; - height:15px; - float:left; - background:url(../images/stop_button.gif) no-repeat top left; - margin-right:2px; - padding:0; -} - -a:hover.logwin_stopbutton { - background:url(../images/stop_button_hover.gif) no-repeat top left; -} - -a.logwin_clrbutton { - width:28px; - height:15px; - float:left; - background:url(../images/clr_button.gif) no-repeat top left; - margin:0; - padding:0; -} - -a:hover.logwin_clrbutton { - background:url(../images/clr_button_hover.gif) no-repeat top left; -} - -a.logwin_shrinkbutton { - width:18px; - height:15px; - float:right; - background:url(../images/shrink_button.gif) no-repeat top left; - margin-right:7px; - margin-top:4px; - padding:0; -} - -a:hover.logwin_shrinkbutton { - background:url(../images/shrink_button_hover.gif) no-repeat top left; -} - -a.logwin_minimizebutton { - width:18px; - height:15px; - float:left; - background:url(../images/minimize_button.gif) no-repeat top left; - margin-right:2px; - padding:0; -} - -a:hover.logwin_minimizebutton { - background:url(../images/minimize_button_hover.gif) no-repeat top left; -} - -.logwin_content { - overflow:scroll; - height: 477px; - background: white; -} - diff --git a/console-proxy/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/console-proxy/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in deleted file mode 100644 index 47fdaef1d96..00000000000 --- a/console-proxy/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -# chkconfig: 35 99 10 -# description: Cloud Console Proxy - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well - -. /etc/rc.d/init.d/functions - -whatami=cloud-console-proxy - -# set environment variables - -SHORTNAME="$whatami" -PIDFILE=@PIDDIR@/"$whatami".pid -LOCKFILE=@LOCKDIR@/"$SHORTNAME" -LOGFILE=@CPLOG@ -PROGNAME="Cloud Console Proxy" - -unset OPTIONS -[ -r @SYSCONFDIR@/sysconfig/"$SHORTNAME" ] && source @SYSCONFDIR@/sysconfig/"$SHORTNAME" -DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize -PROG=@LIBEXECDIR@/console-proxy-runner - -start() { - echo -n $"Starting $PROGNAME: " - if hostname --fqdn >/dev/null 2>&1 ; then - daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ - -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS - RETVAL=$? - echo - else - failure - echo - echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr - RETVAL=9 - fi - [ $RETVAL = 0 ] && touch ${LOCKFILE} - return $RETVAL -} - -stop() { - echo -n $"Stopping $PROGNAME: " - killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME - RETVAL=$? - echo - [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} -} - - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - status) - status -p ${PIDFILE} $SHORTNAME - RETVAL=$? - ;; - restart) - stop - sleep 3 - start - ;; - condrestart) - if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then - stop - sleep 3 - start - fi - ;; - *) - echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" - RETVAL=3 -esac - -exit $RETVAL - diff --git a/console-proxy/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/console-proxy/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in deleted file mode 100644 index 47fdaef1d96..00000000000 --- a/console-proxy/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -# chkconfig: 35 99 10 -# description: Cloud Console Proxy - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well - -. /etc/rc.d/init.d/functions - -whatami=cloud-console-proxy - -# set environment variables - -SHORTNAME="$whatami" -PIDFILE=@PIDDIR@/"$whatami".pid -LOCKFILE=@LOCKDIR@/"$SHORTNAME" -LOGFILE=@CPLOG@ -PROGNAME="Cloud Console Proxy" - -unset OPTIONS -[ -r @SYSCONFDIR@/sysconfig/"$SHORTNAME" ] && source @SYSCONFDIR@/sysconfig/"$SHORTNAME" -DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize -PROG=@LIBEXECDIR@/console-proxy-runner - -start() { - echo -n $"Starting $PROGNAME: " - if hostname --fqdn >/dev/null 2>&1 ; then - daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ - -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS - RETVAL=$? - echo - else - failure - echo - echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr - RETVAL=9 - fi - [ $RETVAL = 0 ] && touch ${LOCKFILE} - return $RETVAL -} - -stop() { - echo -n $"Stopping $PROGNAME: " - killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME - RETVAL=$? - echo - [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} -} - - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - status) - status -p ${PIDFILE} $SHORTNAME - RETVAL=$? - ;; - restart) - stop - sleep 3 - start - ;; - condrestart) - if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then - stop - sleep 3 - start - fi - ;; - *) - echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" - RETVAL=3 -esac - -exit $RETVAL - diff --git a/console-proxy/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/console-proxy/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in deleted file mode 100644 index 47fdaef1d96..00000000000 --- a/console-proxy/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -# chkconfig: 35 99 10 -# description: Cloud Console Proxy - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well - -. /etc/rc.d/init.d/functions - -whatami=cloud-console-proxy - -# set environment variables - -SHORTNAME="$whatami" -PIDFILE=@PIDDIR@/"$whatami".pid -LOCKFILE=@LOCKDIR@/"$SHORTNAME" -LOGFILE=@CPLOG@ -PROGNAME="Cloud Console Proxy" - -unset OPTIONS -[ -r @SYSCONFDIR@/sysconfig/"$SHORTNAME" ] && source @SYSCONFDIR@/sysconfig/"$SHORTNAME" -DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize -PROG=@LIBEXECDIR@/console-proxy-runner - -start() { - echo -n $"Starting $PROGNAME: " - if hostname --fqdn >/dev/null 2>&1 ; then - daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ - -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS - RETVAL=$? - echo - else - failure - echo - echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr - RETVAL=9 - fi - [ $RETVAL = 0 ] && touch ${LOCKFILE} - return $RETVAL -} - -stop() { - echo -n $"Stopping $PROGNAME: " - killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME - RETVAL=$? - echo - [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} -} - - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - status) - status -p ${PIDFILE} $SHORTNAME - RETVAL=$? - ;; - restart) - stop - sleep 3 - start - ;; - condrestart) - if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then - stop - sleep 3 - start - fi - ;; - *) - echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" - RETVAL=3 -esac - -exit $RETVAL - diff --git a/console-proxy/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in b/console-proxy/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in deleted file mode 100755 index 550f2fbedfa..00000000000 --- a/console-proxy/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/bash - -# chkconfig: 35 99 10 -# description: Cloud Console Proxy - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well - -. /lib/lsb/init-functions -. /etc/default/rcS - -whatami=cloud-console-proxy - -# set environment variables - -SHORTNAME="$whatami" -PIDFILE=@PIDDIR@/"$whatami".pid -LOCKFILE=@LOCKDIR@/"$SHORTNAME" -LOGFILE=@CPLOG@ -PROGNAME="Cloud Console Proxy" - -unset OPTIONS -[ -r @SYSCONFDIR@/default/"$SHORTNAME" ] && source @SYSCONFDIR@/default/"$SHORTNAME" -DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize -PROG=@LIBEXECDIR@/console-proxy-runner - -start() { - log_daemon_msg $"Starting $PROGNAME" "$SHORTNAME" - if [ -s "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then - log_progress_msg "apparently already running" - log_end_msg 0 - exit 0 - fi - if hostname --fqdn >/dev/null 2>&1 ; then - true - else - log_failure_msg "The host name does not resolve properly to an IP address. Cannot start $PROGNAME" - log_end_msg 1 - exit 1 - fi - - if start-stop-daemon --start --quiet \ - --pidfile "$PIDFILE" \ - --exec "$DAEMONIZE" -- -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS - RETVAL=$? - then - rc=0 - sleep 1 - if ! kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then - log_failure_msg "$PROG failed to start" - rc=1 - fi - else - rc=1 - fi - - if [ $rc -eq 0 ]; then - log_end_msg 0 - else - log_end_msg 1 - rm -f "$PIDFILE" - fi -} - -stop() { - echo -n $"Stopping $PROGNAME" "$SHORTNAME" - start-stop-daemon --stop --quiet --oknodo --pidfile "$PIDFILE" - log_end_msg $? - rm -f "$PIDFILE" -} - - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - status) - status_of_proc -p "$PIDFILE" "$PROG" "$SHORTNAME" - RETVAL=$? - ;; - restart) - stop - sleep 3 - start - ;; - *) - echo $"Usage: $whatami {start|stop|restart|status|help}" - RETVAL=3 -esac - -exit $RETVAL - diff --git a/console-proxy/images/back.gif b/console-proxy/images/back.gif deleted file mode 100644 index 5c61ae27719fd2cccd372b6c95a19328812feb46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmZ?wbhEHbWMoiaIKsfNa{o1U&r*K>Dv{t?r|N|s^-B^aY}jz(29t&(i$^JoM+vJ( z35RzXw@|$V*QYgrYYKxr~7#Qs65x7W)@iIsAXNQ>QwH*Qs)&M$I+{&1Z`2aHjfX&pzz!shdsShJ9 z{51TikOvPYKNIIG0I+aT9~zMUh#vrEYi|XwDFiP2)a8Lps5Z>=@en z_bGRIY-CyQFb^`!h(5U=@igX6=hkG%(+i*LHokT5y!@g)SF)N})yvSp$eLn;*r-rg z3-yKfeT`^uX`7sp*9~VD_XC`aRp&&nY>Ux=%`i>PBZnFo-U6EIA6XbdgJnj;(Sy+h z$AUg~iiUEVrXe8VZ*xWpH_#2fub&5Wjc6#DX_9szn+~{kdALu5<8r`N>gKnPfRee1 zh0p+(BvBrk>;xdf;xV-?dtaR9`>@H9@YER>}TZ`-q90>+})>lW_yeM zbOD$Q2Pk=l}?8n(`|lj!q+Tv5B5G}y6rytE#COg zD;DC9(8oh9rtURPGThTP`~B}5q~Bdt$~_dT|L}0DL_cwEYC?94R(nDPRGinQ2}zJE zIPF3E(_D(vDWG9$1AvuU-^NeUjI>x;KE>2vBcpQ9YS?0UH5mcDdUcZHCQtHLK; zA>{@euc$;ncKIfU*X&w;9KYQdvl^xA!kOswIvBa!M2BN&-0wJv-Dl*(A~!`FP3ZQ= ziHp3C;kG$so}gl)XnV(A#KA;pMeFh&_2^SZZxW7m0Hs@hqUwwllfo-)v_7%EB-IqC z9k^}ugyqAz0LAop-t3GImtP#vx~rNq_o3K}V>n)$)PA(#J3j-`a`C8qTeJT2zh#*c zJ-VJBDq$^dJ5X_=gtj(z-J|z;Al?3O!v-c*xVis_Lx3@X5YWR z->CnRt=PP5VTQ8F0kOpQ!{;tkGN>oXiX7>PoXMPNo_a5^3`)<3)Epvk>p_4h;ELk#HP)5PNuqdx+yokkN)uGEizVM7)Xqk}% zVmDpL^eLs-p(ykCqMe`}x$sy)bP>70*IwsYnDcfly>qigvo-0NP4@G%({*hkEFJA-l1B=6qgDEsgau`JxNCf;qJYf$uKmFQ;X_JwwT|@Xa3&s-OFPFjQgN ze%t4F{<<-%6MYq{IQkV5;&zc!@P&AOyqw`8}}$=eAveVR`PjP4n6 z-1TldH7!2v)8^CmRFYm&RMOmX!E!m>*0Lt=aNaSCV;1e*c=wa;&zMSvmf{zsxs17t zFBucBTu)v9F=%#5mxrlo?B@@3U!@8#&x$GGz2!&%PAl@HAwq^~x! zeL5_UlaF4QC<(JBS%;Hf&WR~_C?q(ReyMns8(6gA_JdFRr}pK?)7sOTYU4`d&S!D8 zhF2Z0x~!Xhv(83NkZy_*=ZIjU59CuMXvnm&;d7n9OY2GK@n(-a9`^n72L`uqZgI_FtWuHDk&aP1_x)m= z4)iIess#3plCDe`{5D827|$RpTC11KnW@NV7s;R399Day{6?ou9(BeCoHCtEFPa}z zmzOD1y>#*I#Gj?r=&8&pmgx|_G=+}SenB}sO>2?)yoK`nWi?WCEn=+xPWq1?r%VCw zU5OR0K#BXzK}|wUe5pJ)2c<`^&k9EScD}<#lU@`Sx73{L zMf=hDZ{u=VF6=2qsozrBFR={`zhIj<@nUKHr{Rb~YPa-AZA3Mx=IJ}l*L9xx?|ZtB z>DWKBU$g&hue#hn_R{Uk=B2)6!H~M2%Kr?ExYTWIYrlUx;y~kIIe-p$ZkcXXZRnK6 z7R_&_Z26onwes;_YNzd|-DVJDul`c=X+Wr7%icEr^N~ek_V&Nt?qvQ5lw(t6bGY}Y zZA#3~AZTWOgK1(zm`!8&+TD}~W+rEbhROdLbsc$99Wv+9GjX27_s1K~fXRMC zB5v+j-LaliE3X4qDo19y9A7)8musB$SqNCr+)4as-Cp~sHodIo)PzwFDqo0su+` z0KW)$&I54uC;$tt0BEEEz~}q$Y^xCfwBWdfy&^_v;g91GY)fyTaU@ag3v3tN1 zeuD;o`~MjN015yG0DMIw0Q3+9ASggEfZzZjs6i9~C<;&vpg2GYYA%KV3AHjshG5I1X?sQ$QeqKmmaP0*ADzz*K061&d)eLG42|6hSZq!4ZU@wuK&wA}EHS zID!&X7BGmR2!eR3N>Jy6ZJ~!@D2AgLK^+&efI%EXaU8`7I6Wi zG(lYvH5-PZ4~>8ugF?djU|Z+Zg(!QB;7W!~KiHFsBNs_MJiQA>B_pe|u| zLMqZOY3fdPc~F6Oq2YCaTY}=jnP4je_lo22LhlQ(wX&n$@BcL*QHr?(K%-}Bpl5%5 zxX!lGj8>5|)Wh7U)!llkaId#K(!jd>)SlYLQU>PON{um zK9#&Ju#y(kTxaa@Crti7QeVGTnjbmII(IgXL9mPCDy^Jgr)*M?uOm5`=iO>}BuCg5 z|J)*%Rc*Jpk<;m=CvjccPGiy$1I^R1DKlyg9LjqX-Opzf{#=(G;muW~EZiF?uwf`K zkx$btw^aM^vf}vE(%ABu&~6o#!Q|>QHM6)?Xuy=K4GsSuT^uNVpit7p(y`cFTa&vF z`1Kl)J$v>%quj2RD`wKRn@(y?^_F^y_n*1t?IE4~$kf@{nLqDd|I*LtYr3)f&4XkY zr=yaT_|8;RT5UbFG{TgGFw)uN?kuJ7$mGa@C_62ka%na$3A3a;g*oA279o2$QvPV9%*hz#<1s47ViixttDKBiKA5I-Hc9zhs@ll}wX><3 zr&Bb}rfOfv)V-Xedo^F@dV%)MBCT5`ns>{r&gL3i$TGQHV1BK{@Lsj)ohr*4jp0SMv&=&C7eUu#z#jU9qln{acO)Mc#MI8iLs?cDWH?5OPz5+k5aIkhb{$79D8q3eb+5p?P`X&6ymR LSE)(}FjxZsXaKX; diff --git a/console-proxy/images/cannotconnect.jpg b/console-proxy/images/cannotconnect.jpg deleted file mode 100644 index 0599f17beb25987e999230a1429047408e85284a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1810 zcmbW%c{tR07y$6^Z|<4IxJCxK8b+yg4MS~`qb6iiIg>P2GflCq5S4Z27{qL)Hp!JM z%9SkZN^Uxckeh^cWOCQcj@^CQ=h?q@d%w^7{_}mG@AH11@AJI;H~b-hKV(KT0}up( zQ-TBd!(h9qAJYv08f`nk001BW!I6Rr08x+|aW5PKaRq|jK41(`2qX%LK%tN*G#Z7$ z;c+-D7AGMrB7~Qc*s?`RfrUo%CZE4g1UmTimKXnHEH>s8av4vB(fTL;}Hmr zM&mFzaU4#ZOe7G=|2z2AKokSafh!zR0x(esE(-DMfV^OMq(HWT`#T^QTre*hgT-wU zG?d^041&X92sjdnKnS`o3+@3z6e*^tX@n9#;(}K4l+X%_Pr)efFQ}2UemTEQ`?Oau z7DtfUB26TzsFKyT>riy{sQL!RCZ=Ww4$=-CJ$Bs2*3RC6?)tsk83yz0kKPx2eElx^ zhlGZOUyX>2O5pr_{l?A2Tenlw(lhR7W@YCT78RE~E`3t=YwgpzXY~z@P0g=5I$w8n zzv<~69vK}Qe>d@da_YkZ_v7Ny^2+Mkh6@7lzgS;n|8j{6TrdOzjzDj?AegTp;i3qn zq9#hr=m^@yQ(Q?a2qUpSKBb@rtE_E3FL~PQC62I7XPES1gZ8KFzrljPO7;czkE<65 z!6Cut!9{@~ShbEK--CTc3Ug}w zRO&Oz1074}qes%6k_gXEaaGT|jkYf&>T2W(1*EszhQ%qXD&rh(4a?(qg;74)+9bPo zP-N)Li)TriQkFGAwl%#|%@42$&qjrKN1p75$DH9dvsp_S)6NHZ6UFO#$*8=-&oZsT zPVRg1aVtyZ=f5C8Vj)?my(_F)=xp20PeSk@D3 zqzCHTr!s2ITC2m_b%P;k_~vLEKEP3I$`S^aZC|RZ@H#JVC6(Q_c>Y5Q23x4RYG*JU zi_X{5Mn_jFB#X+6-RQVY=yF%j$Fewz(Z7EvJe5%9)mOw})2kdxaxYo%UcCO@qpI*y zRS_%Gp=asNY;$d;jgKcUb1tx?Bi7Vyae)u|Joun=^Xf(3;lUVopU74Jq)x&#X*P+K z-Jf1)Kgp>`W|Y~rpmvntb{(QZ{pONGvh30kkudMM%}-M*lOFU9mMn(qS=R?UHMe5b z^-Mxs8sMZS`jXl5b*!VF^z0K1k6q>Wu?e-KGl@Zx{r#63D=Nwr;~yE{qn-!}xy!0s z`&?O``99)YMlc^(6`8B4tnXut$7nx_Sc2t+G`+1}f~DEq4B!#@pt@@4Tm6c>l}SE8 z4MyfZWDLm8&lD4V?o(JX?kvpgz{mjO)g~s1O_McK+5hU4mb;76sAu)=D-73p;gqev zsMqs>r2$*lJgX%G&6&~cyMvz?Ti{LaxGrp`)+CFU%Ua2$b*az1>nc{uFKG#&6gg&8 zY1WWUdSsdiwXIVrW;Ukivtt+9%9FSDwjq{TX$C9d6P8Z90_w~Ap$MgVCzm(4M*4T@ zb30n_Y05;VZ{T#`iqtf>(>!x@#d)#d^!o7L-1FITdv`jUlvl(Z39%@P8em%Wq$kG9 z=xhFTY&0n&;{Coo$L;q@PpPns&g8uBe!S4;iCDJb(#GwJF&-<7jEv2I!$uMqE`##T^~wjC!|bEh=pd`Hgf zdI(J!Y;(}*S}Hsl-6t+LYat}=BIa)VZrseThwJ*WHfZpA-&*&l@KWb)E3Uo)IW;l( NE7r!wsuAJ$`~kw83zz@^ diff --git a/console-proxy/images/clr_button.gif b/console-proxy/images/clr_button.gif deleted file mode 100644 index f5c385829da0dda9ec5a86372aa2fb7a57c85a69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1274 zcmVkP*Z=AOLmD$XmxLUfJjwZ zYI%Qhlc86Dl52H&KSD!kdxL9hYfOfodaJ)ndW~*!zU`S6=Q-G6ScY$wuepFjtQ+``WiK)21V}FZeWMoQcbcC3o)!EuWMoDF5Wx31JTX=939w!FV>euPwNb4^Z8RCk7JZf{g!XI*G*ReFkNYi)+3uV!y^ zNnK^P!pBQ>he%FQf}pIs#mSAOuWERHUxbv^*xGDzbV_f2Yjt{HZgj)X)Oe7YM@UGy z#>-r8c1dJ!LPSJsba+xyQd@U}Sd^`TpQ~wlfUUT_dzYiPz{JDO(}9zlRe6Y4e2Z*v zaDAMpewCd@M@Mpzoob1d)YsT@m858XhmWSPS95-Hi>>KU2Ab*Y;j6!c}i`2l(D^2e2&D<({6l%Szu#$n5BQ4 zrf7hQYlM$nbbe1zQD1d_Uv_^`P*G=WZ@I+Dn5(l`ldM;Qlxuc+y2Z*=WNVL~rEiOt zSAmjPXm4eAep6FZW^iqOdyG|okyn9}NK{x!Vr)Q2OHNKsPESwD z(9%nEhRxB_A^8LV00000EC2ui02}}h000R80A0Wc*I)nw007Vcn1Ue5fB_&5c4?un z1povCQuz`Xz*`y#D0FCeqCv+Q3N<9upm64i2@}^eP>`Tz&ptE!OgZ|%Aw;|;V?uCP zbnXKrWS(4Yld-J{hfHMp1R(N49K0VutZWg3r5TWUd49Na#!Xo#M0QrOnKdX-fB-LO zMfw)zAB4NLj1e0JE=MqJTzZtT)@a?XXv~}hNcP84oqw-busCrASRz!L!2CMZgA<){ zxyW^c=0-pOUBaHly2CEWJ#!vBpaa#bRFF54NRU_~N)fDf*i7&6qi52%3p_61xX`9j zwrV_Zz=`pNR}5mVm@fXG3i515>V8F1VRh1DVstAgc&%q(n2M8=# z7K#cfKr_q`g%Ro*GK4Bdlu!aA{m4_m1{MgALIp)gfrJX5D4+lpeBe@uh6)HFix7`Y zBaJ$E*g${*0uUj`0TVb-fDe~M07V25WT2xKP|)LpEl3owfCB{FP=OPgG{A%bG)Q5? z5(`9m1R(`fdBiOaJiq_}1khl?0ZastMG6flalj1|5Wv8e1mN?)23k%~garoFc|Zk9 kU^0gYUcA$Q01+iHMFNB(WM~2oJv4v-0#H(n2mt{AJI!|_qW}N^ diff --git a/console-proxy/images/clr_button_hover.gif b/console-proxy/images/clr_button_hover.gif deleted file mode 100644 index f882fa0baef700d492cd02fe95a274ab7962e5a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmZ?wbhEHblwsg!xXQrb?(S}HZ=ai+TU1mO7Z(>25|WXT5g8eol$5k>+qS^K!1ng` z{QUfT_wFrPw8+`n+0D(Zrl#iT(W8}>m3#N@ZEkKpeE4v5bhML`Q$az&&Ye3O8XC@? zJ)51KJ!{r1e}Dfyd-hzrcFoVv@9Nd7Yu2nOE-tRFu1-x&4GId%$;t8c^=)l!y>jKs znKNf*%$N}n5HMxRl!+53_VxAc-o1PE>eX3US;4`)Ucg&Ru`i;Q89oC%A9bg#5RFP~hm8~c1KvH6SH zjRhAAiU|s8iy19;W8EYywP_oxl<+oTWhq@{gY9k%N5plGFmUNHaPpks;XJ}AdV=Bb z2~IvPE!3T`{ Z8WDw0sv;Efe8Qr diff --git a/console-proxy/images/gray-green.png b/console-proxy/images/gray-green.png deleted file mode 100644 index e785a63f83eca4d54e2ead7ee4035b1ce4f508ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3833 zcmb_e`9D=_)PGzuPZ^VR%_Q?ST=Q^@nK?o#Q}$7kAvfZtBbnk7GHfC$^N@%#l_|tA zr!toz(WN&-l-Tcc{Pg|-@A`c9dCod(t?&A-?|RPXv*T^8P1%@_G6MkE%*~AK;dzYu zFw(+L<57hocrXQ+xm*Q+g`4`&fb9E60ARMcNFdnS`uGR?U-j`15H=?egad;7(TkV8 z0SKMQbs!NPmictHXEqHj;>hhA3Nv>>k^$t)fKI2mg%h+N$hqX9b++S(^YTNz#gn$S@eM$l@N)2etZ z7Ux(o#Lm)?Z_~5}r~GBXNag|hVM&HXK;MLhoSP--0P^X8d!Lu*Jg}7m?ou~Dj{O}!{q`S9tkL5eF^e}RJh)l%Mc;0aqOJd~5qIX2v%;>lh z#D(9)@z{x4q^O!H+20`wJDLft>jeFw5qs98F-5BfDBt=W(`>4AFS5o?=Phd;p`k?m z=xvjSEJGK9m9mrh@^glQo*mUmR4Z5-D)-@-NY*8EE4F?)!hm#MR+R7RFns!#EK{0S z-&4^F*7B~S)u$_Ho8q^;2A_t|aYY)pGO5vp7z%qFH!yRybhiFrRw8^%T;=ctlR6Xa zZM$|U(X5$Cb>jz!@G#=u{WyiYF*bHQfEf{OM!j2-k?o z$Q66BW!cgk6|TSj_|`CJ+>;eP*%Q5xyU?+KEC?=$th+Mjdg=#0-f@_dw(q@k zN??(5QDu?dJ4@NX@lm$@bV&j6uxj!d!^(ns`zLm>CEO~1XKVCF@tIo`Rz6H0J?g{n zvwiW&m*B?3)9J^pGl!f;`j$<?0a!po&C{AQ*#r02^lrS~%ve?+IS+CUen95yx%*CZUd(mK88s#GlNzo@ZNw$STioiC z&(xLA5$u}oX<0ZGycKxvQ`pE>Zcc4Z`)bMLlQp%qi)*n{4=5@RPUN{DSBE-!2p{bh zhxJ$0PpG}8zpKO(P^^fV{Ef7Fbx8YJd7 z81Nb-->ST|*j6dx)AQfmN|EiQtbvq)i|o<15ntX7F?f9TNa%=Qm5Ppyc8bwU3W#$) zI;@nb8ZtaZxH@a}%P7NWCWoYCqwz%Ul&XwwnfxW~3H3&mM!hb1)P*wUoa=0U+2WXn zyiAqamCG+@f3I%D&gIUr%!l)5DfFBRxLz>Oz8PK2S1QjXtC^v18E5_1z5g-blqum$ zlvw8ukw{{`-Y(Qm5>*&h8;<)xI4g79dfiOMDlGNClxEfI;;)fH*~Nz2Ql)pBE!ixm zb2Rh#vabpIDeGvqC`F~F+zm7=6R}FGC#{e^qu2T>yrXOOUtdXhu5dh^;f-(Y-HJml za?JIHMPG<#q~FUh)R4TL&{zHOy&4(ic_;fp z{@hY=Nux$P@cJb4Jnx~CF{kM(v|rq*m+>33DPMKUtcqxn472i$Jm1!PtF4a4_bZDd z^J4UkuT$?=&)GODxXjjh{EEpH)a8x8Q^hmMpUHdkgY;DBqF}6l?<;KT-m}v3&V~zv z=m@&}c}6bJl|7>@^K&Nq6}It-XKb^lpRI2FFrHM%?3bQwimE3xJbI=5qS?Fn%|O4F z9`P}8llY6Mwl*?d=kb2$%J7C_@~bI(_n$BKbH9biv8k~+{(HY` zPAtIa`oi)y)9m(fHqD7^iQyHW7)JlRCPWfaxMY~Uo^6ujkiF~b>Dp*R(H(xbG86XO zbn;J*`zqQl*H3K_-&~wnzBQUgS|3czP0zg%A^&sAeez*__>$Ma>?IEWZ;hP6pGJ)F zge9$Jt%0-aFM`)=CKtJ#UN~hx(RA}$30~3OPaC!AZo1!;UDa^bw_z=;r7j%ZUKY$# zADG^3x&ECGe>aw=p)=jG5$3=BdV6b@Z#p7KYHy&gX*@(~t+Lss*=d+tkfVts(gv!&9c)^wCj+`zc$=9E3y3SaM$sTg=zIzjQS;=JbO0mVXx$1}_Xs z=JD@8-0!R6=TnH-8TELD38barbhKF;zr7H-N%+1g>}6(e1wfbt0OTkD ze&X<42H73vhrR9PotS zpuyk%{|o^D1pos8zM|m(dI$m#6d)KtC;-8!K@{6oBE> zQVIenD4<|~LID&iQ-C7?M*)rjoC0Z6fvM0C3l_s{oZ5$KD1u-JLO~Fm+7@~!il7*R zQVri3#Tea4MQIq1;cQvkZ?ZO7J3v4 zM!})3a9qd&1~Ht1;ZR4YG!y{xg6U9bI4zVC>H%rOYA6Tf1P6u_Le>xrl7^L#7UTgN zLu?3*Q&&XIhGFPK!{NrDkZ?ZO7J8I}OGef8VBgf>!3wD|A8du1dmuDb^#kpwr3Z3Q zmvAs473qL9bteZrsK5u%@H)UPLGj>Bu$7T#wJp5RhlA~{9jN#F{~C~}#5`ee*WAc} z7&`GRRmrzQku!SMqHV=CpxgS#V+sG&d-%+Qz@oeQ@x>zr5<{=)uL|+F2Blu9)DQTw zvo@n8^yl~8@sDn)A=?^PLgqJ~X)Q3I2K~o2oi4b>#xrssK5oswkT^^e_KNGRD4FqY zLXc{CjjANz)|)H0yjFF;M=~&2NafPo{F?tjHz@Y2T#si54K5^0sOYMu6zeFZw_-zDF#m}g$| zUirWR11B9R))PyN4(3>8>doI`xFTwO*P62I7ghUPH&pm&%s$_}rHVIWJ9FNFM8Eaz H3=Gx)?!i0) diff --git a/console-proxy/images/left.png b/console-proxy/images/left.png deleted file mode 100644 index fb7506618d7c3db40d02c31c3fdad17f5c6b3bd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3024 zcmV;>3orDEP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}0002%NklzoL|HW`rOdWXls`3UQ>JMn-*tmxN`y92)&a$w*~Y{s zY`f#>;HR-QyF24(A+nAP2}fWJ1n4%si5i;13K*4)cch zh9qHTXg6Wz>6u48GrSq*4fXc>Y^sX$@d+9o9W%XuV7Om|*4#qR*ruy&{A Sd=wA>0000OZy#r8XODmo zcQ0>uU;ntcIFGRC?Ck9H+yW;jr;Ln@kkGJ*tiq(EWDozKjEqc=kVucvsECwI58r^u zm^eQ_zmSj+e}DgstgOhWsEERv$b{s?^vv{}e9!odxVZSp$jI2VOpoA*kcg<* z+_;2955EA{PLLGVs{_k#GHbJ+~V|{T#v|v zh`jQoloZdD{P3dMn1V_VzrdiNpoF~Q)YQ~~fB?_f)RdHzz`(%Z;9!P92^4>_fZe47 zB0+J&z<#}fozIhtp{>1xfla=NK|z>#;-tyU>}+xha!j-5%$+O1AjB>#p=fTWCb&w? z&RkJKxQ$)R(L`0x#@5}|Mo-nmQLK$mLtj_R!phRc(#k?hS6`z|Q%P1uTGT<=S=m8U zT18f=O>(V|7DGaUp>j?hTI3to(IX0)ieNZ)W9{ z;9+s#cVyz@4$G)mka(1dk5hnWO2PuAW?9|@k;aP&?93Ax86*M)7A!kE+k#Ptalrx= Yzd39w28|6%JQuojGA(Hk;9#%@0IxicasU7T diff --git a/console-proxy/images/minimize_button_hover.gif b/console-proxy/images/minimize_button_hover.gif deleted file mode 100644 index 7837ed00baf57a8978cf13cafcc71b456a1f7b30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmZ?wbhEHb6k_0KIKsf-?(S}HZ=aNu6c-m485x<8k&&C5TU1mO5)yLn-aR)rH)m(( z?CfkOCnrBYKYxG!prD|@z`)eh)RdHz;NakZfB*)(K=CIFSY8K2g6w2qO;}(M;+-%r zV}U}_2N8Fj^X&^P`jg+(+&6A)Ti~E0)vwlAbTWs_r+Gz*m+TCV8*9qE4HWm?Z>gBW vpzPDIrRpr(g4-XySl{E*dQp0U={%ng6L)z97q1|Xkcg_LCIbtPB7-#mJBdP0 diff --git a/console-proxy/images/notready.jpg b/console-proxy/images/notready.jpg deleted file mode 100644 index 406599cd595865c8d4dcd4ec79a0cefc370bcaeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1827 zcmbV~c{mh!7{`Bek1@jxGeg=E8dsJ|NkUIThRxC8DoT!`hLDV-7*UR7-8nYpm>ka} zAA`6*kRmt0oLP+I`yR*}r!C{@(BNyuau7zVGvXp69ppZ0R+?+uKrY z0R#d8hh+yW34nPx_j^bgYKUG zfx)5Sk!R2O0^#_t6K~$Wo17B={_&65PoE`U=9al20QMKwH?n_m5oBCYI2;B?EptJj zkut*waD>JNq|y!-luxL#=0*mZXq9=bu0u}Cc#nv5BGav_>W5o;iedae5q%Sb^ z(AylNOzA7UlTpJx_E;s>zJGBe{jDH4Bc^Jy1pD4$)Po+}PtLbHXM<(o-Zk-dB@}6I zF&*=H#nOw(Y7_v?dW2#4UZCc_CKs(+*>|JNQSGtDyT#cPPX#60OTq1njnQ?x1xVXM^PW_xf#2R4)4BR*+joQ49lft}o zy!mSLC4ql#>4uDk^Sd)^DpPivUkDA!4KWWDT3DSq62pqy*HYxM-^-C&>vy^h(=(+* z;nHE6>t7a=P?bb)`m3(!i*sEg(Ti0PCbz2D#o69=?_){1gN_boMM!HOj;X$)vmVdU z`wWd+G%zzN))*W1S^~K3t7a`Lq-Bf5g%y(6HY1VOrE7xsT6g&hHAj6bEEpqxgExeV z6EE7zJ~g}ck!?MA2T0CtK_6U^oRB28ThijnvwZRuOuwZfzx=GKSni2Xy!qh_^P|&z zO2WgFZS=H9#n%28HSaNgI@g;$;U}4Plk|#VU90g;B4*WiiBga^xyf$)f_3ZA4CPje zR(rZDGA!N@`2?k^TIMY29hZ9IpesN8Aj$fquGswp+dNY3PFEU}4G64`xEHyYX1$9v z)k!KXU~c-YNIG2qG~|r?tE|b-A)m*l-5MJ#FH1CDf561<7j3!V$Ps)w(P|MRl%9Af zxTE9VmfE9%Ow@Lw1QdE@p`2rf$L=Q`c>vwF8ZM!V=7l}6Mq=yLiUYC62x(J_EvxiK zT{tq^oK3sgd^;-GFxb;wE8M`Pr_*2cU~PWZ@i#Txv@2%ZK?=7;d_6hXsG>P*#-Sj> zgYQ;o*IGa^I7H#E(F&MyrkRI&>2#=HYFYy5R)@ft@KuX-Oi%vUD4#zXv^&tUfYN8R zg;SSPo<<8ZVNwW9FDHxf!}U2=QYu2U%X;H1B1Ycep4%ACECzOS^Q7BWsy9s3CN!{o zJEKDq8;r2;*okHad+ChWbu}-h@_d~$FE>y_DMOv6c3p?2isK(mdGsu_8F|-=KabZh z&`Nj=Rzz4Kr97{@N>b!$_NeakYS*9B(s;xJPvr?}=zHx>I$LXXE7NB~I16KAUL4I| W8XRsdB&6zAuhlJ01>fVP=YIlBFcT{P diff --git a/console-proxy/images/play_button.gif b/console-proxy/images/play_button.gif deleted file mode 100644 index 6f483082ecb33d34bb09dd744ff77521d6a54aa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 657 zcmZ?wbhEHb6k_0Kc*ekBZ*TAJ?w*vC6c-m~@8A#;5_0d}y~xPOjEoF7H#c`LZ)a!c z+}vDu4-a>5A9qjBqN3uWq9P|JC$H4P?CflhfMCz4q};rGkDzc5-vB>9KM%ivqT&*N zfB&4q;&Yd;rWKaD`}h?V7iVN;c|^o}#-_S^dZp(TCT8UY1O%iNR|E$KM`RcK6gGNB zCb)b1=H}-4p=j5iQrbbt_$0n!6C8c;KW_$PthZNL=*L3GK_atWKgoK6X6cpv;7p&WR*fTkI z)z)1eA(8RrjY%nK=dN5!E3S+$sR@Zqj4!T^NYC}kt&S(@*$mi#;!hT^unvd>#R&uZy9Op^O>Ty^_6`P4)+Pp74nYAW z8G9Ke0RcflCQd0?DIT6AaZ@=5IbT0iVsilrZ`TaK5cexcFW^8QocIgp5 z@7fsuN~#D~T&Na0>?gtc_s3Um$HOPYm{X#BHe5LB;>7TmY0rTKMz1Lr!d_=MJ}NA1 zVfibPGr_URms32bPa|VOz`3S6ewmyV4UE36JbW2ybC?qxo7nh8EF>N~G;%KFR9Ui6 WA;ndZDdKv9YJtW9My^H%25SKAOWz&< diff --git a/console-proxy/images/play_button_hover.gif b/console-proxy/images/play_button_hover.gif deleted file mode 100644 index 6689e3eec8145fca0cb0179380228b77bd320bf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243 zcmZ?wbhEHb6k_0KIKsf-?(S}HZ=aNul#!7U7Z;bCn;RJ!84?mwR8(~D-aR)rH&0Jb zXJ_Z^>})3|r*r4d`TP6(`T3=$rUnKE1_uWR1qCH0CKeVJ1_T7GTD5BB%9U%@tjWyG zOi4*OfBrlJ1B2pE79h#MpaUX6b~3OQEO1EhNtljAhIHOa^h)I6}dU@u9axa=I>npw8nt(UV%f^<__jNWtGeF qj^8$|spaFUY^)XKsqD&Ulx5^;>CWfk6Xq2XQPtFBVB=L}um%8Cp;dVR diff --git a/console-proxy/images/right.png b/console-proxy/images/right.png deleted file mode 100644 index 4abac818410842f8fdb83eced02d0034451cab7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3131 zcmV-B48-$^P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}00042NklxfWM?K8xy9psNBBP8o8igv`NaZ;Dza7^ zd9ROrV+->7@nwOP@%{6QoBJWk#R}wh^lX8ZvE9v4RTVC;uE9w?HzUe;8GmALu!*{^ z!J?|oh(hKeAjAk3)zoZg4BSM7n83g3%xoxR)`Vr?!UC(L4iQaE_Q}%w21kUHzz~z! zP`aXHN?=IQY-pX}n7|nA&Y_VeM5MF;A4gASLm`6>@%BCjAtpm3&Mm{zi8C7t5fKOg zI$B_jc<&(s&^d25RIX?T5drBfu+|B75YRhY(OLt0FCa6T4TX>dodZb=taXA(f@Eek zlyS7bgK|KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0004hNklKkoVC>7qo*AcL4~OP`mhcvz=2*VAdvLfA00e0VLygupG+KWzyZLj+ z009Vd|L;L3KTb7SEXlrJTrofZ!fdXN)ODRcA05l5{fr_2!d&0})rYNj>ib?k?s|#< z2sO)DxfJ=hd!hz_*a4as*3cLoSR{0qq4f})U+kl3^=kFe;(^sI=K%$)oJ&*-GM z+D?zqD38F<^z2-(%(9TI;-sYHkkHWBqH52S{P>FI+`RmNiq7pvPy6}#rRNqTrKCl* z&x}h)(FPRGT^C*%}HMn!qXrF(?M1Ox=c)%64h2BwzR1qB6VWMo8k&h<(! z&dtqDNl8gfO?CJ2o3d(4aBwigzyZadEMUuZKqM$m7}(!5aPWF^F|@UJFtCd?F$nsx zOq?{Cg@avNP&?3ALt0drZ=tZLw1zQ1g8+w+se+D$p}CK_p@oiusZbk-vaY17qq?k@ zth%GCq^@!suZzC8lYx<*zn+nSleoT1n}eIZl&U4`lP8v{Quc0bVk&kvN+w!QwM>+3 z>{Qwqnb|mH6w7!t4N}}W_&9lZZkRV5Wa64;NQE4`lS1byw4NX&Z*fm^leq?A-Yrz785TAs384DDW3xRbh-gaR1%!>V+F$f39jQaO7%gW0d4+ cYhn~<cs$TEn7Iz;%cMlJDPcP4;oZQ@8&!{Aiu$YXD43CgVcW+;hfDk7qr|j(Pocw~w zj@h|+`Oy`v;q?UY}M*;Q2(l^!!GDXOTxZewKR z7v})?jKYxG!)YR0VprC+&fZ*WZl$4afz(5APK=CIFSY8K2g6w2qO<7>z;hiuq zV}U}_2LVr=^PLMU`jg+(+&5=zYjDt!>Q`$lI+?@e)4Za@OLhj|jhw{j6KTF38)ifc zbo(}JnYAWt@ofgPKfLRAOLFbD6Nr5AmQjR}yRx2vi$|1KKv-2%lYyOAk--`O8$&_% diff --git a/console-proxy/images/winlog.png b/console-proxy/images/winlog.png deleted file mode 100644 index c7668c52f532f703f9dd8e4655fdb31dac58a9d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2629 zcmV-L3cB@)P) zc&5)Z@6<^gI}azWy?*V>`41%AK}MmpJ<-8*x>`ObH~a~)=kq5t;5 z>3`s`l7D9I8hGz7j7WqoacRgJR$fTa*rhpn8&7#7N@@|sX4&=ln@-9(R4iEWsTD;p zITnq5^JcPSnpRfYRIb)YhD7rVHHu?JMiW(Q0lt&e3L@Hj-x#eg#28hg;9E;?Hy$ISgyk~w zeLl; z+YnCnM2fZ@IxzEtE(l}2tD3kmmg2+QlD?O*$mtX0X2C!o7yu(Ioq)hPkv!Z`6#2o@ za`EW(x6oH4O%E>CX1^m;;qy99{n1G4geE;NwRWU5!;OyI-U#HKejzpe1PEgr?xVPl8Qi0kFD z)-KmhkxbU0=E^9N20p8OazTTe=*u>E9#IB$K6hs6Wu&XGg4mF0uY%|>>mIJ0v8Ik^ zE>u#-E=L+D%3MeZDg?esO1i_S`<_-s&(7qCK*=zATFIt^L`ID^MdxxZoE6 zz(6cQTzlkLEplwU46=DCFdK@}()7ylr1GmE9?N~P3qUhLACcpe6SJ4Te$c;RIv(ET z6vl2J=o=dFip8vb%~0|33Rk}yp1tthYsP>1g}(NI&sm?ne?U#E-Gllxixsj1a)~y>D z@$#O?<$TKLJn}uXOUA1Po3Gv9*t_qE{NnV}ZFgz7J~?xz?_zL?7|M^4Ou+VeHw5dQ zCoEXDCdc(@dR86*V@u3&yXMseaYMG#y+Ap))usJBWi}SAlns@GLiCi-a7$&8D`WB_ z>gNshaFFsoQ{H&pL+s?wfAiWK)gR3-o_>7i_Wr&?-X$BNw4ft_Y6;l8?P%hG22-4Z zvYKy|)T^%VhD;>-VLcG*`yqF9Tcs7>RhLxMn7>9PUT71#>`E92jk=Kxx6*PW%VIUO zGs_=*`r*f4d42NY{#TEme#m%@PdILCV>;Ebf;h&yNX{>%mn)_I$;PaERF>p{uwFfu z>GONT~t>;$WIZ)euM2IEw9c%3HI(MYLHiA-d1RMN4Wx9`M7<)C!lbmvcQd)Xo5v?9 zl`lh6Rnj#WcE`}$?UA`-7uW=2va5wPAZ&nupWh#}VyFo}f&mkOflyG>P5{JM1GRJ3 z-Tnw8>V?6@<0hK^hRN(87B2!=fSJ=ktn|*iwvFeuEz%8iMQqVjrEh9sIheGsbPu~) z6RsniR$bo{v?H?76$)%HSghn5Qq5OLx;=rG+v zptQP<(NiM=L#Bnn(TZ_zir$*@4%Hhg%RXFJV{!*U`yCLL5StqH@Fv;r(QV*(#^PN@ zVlc7-2B4HKh-Rc|L!bC7RsX!rS}%u{oCmrXZx0}*-37r-1FEi*X(D+;p*>@&vEk_C zWjgFw3}ejDsa7kKueVN3998wXslIfkp3CKQHeV=o`ZnKQ%8fo?53IX(ET?B}er_)7mF{FH z15bK6Q3?hY$%xP;D1q*lK{xSRBSQN-bZW^zF?C_#eChJkZHYf3l6~m-Du{)JGe4gC z^$&lhn~M{8S`|hYS;Z`6#d;d_1}N<{o@><>HqD2dA6LN*nQAN!5w)|o1gl;apGtRqy>tGiI zWn=TD5RTB7M%m)mSf!OR>lePH=9V39Cp%rNkv$>|1Wh4;bnuB_0O4VF%v#0zo8#pp zL8F$xGQLZQq#n6d5J@}zvm@#2NZas-xwcL1XMXyJ&3fnX;v+k*X>844pOEb0RG0wK zm^F#T8)`$`=T*A=i`T*z#wV*Y=*Skn{$P^$3XrMQa=hQ8yS=wV@~pUhq_X9SmuE+R zHF{?8mY@Y&Pz?u(7kjI8tP#W+&@LYddfjkwZi1?6@SbxiUX5=5il n*~`Xj|HAnHe+~4GzX>n^g*+DN(p{zl00000NkvXXu0mjf;#L?^ diff --git a/console-proxy/js/ajaxkeys.js b/console-proxy/js/ajaxkeys.js deleted file mode 100644 index 2ecf5b561e0..00000000000 --- a/console-proxy/js/ajaxkeys.js +++ /dev/null @@ -1,77 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -/* - * This var contains the limited keyboard translation tables. - * This is the table that users can modify to make special keyboard to work properly. - * They are used by the ajaxviewer.js - */ - -//client event type. corresponds to events in ajaxviewer. -X11_KEY_CIRCUMFLEX_ACCENT = 0x5e; // ^ -X11_KEY_YEN_MARK = 0xa5; -X11_KEY_OPEN_BRACKET = 0x5b; -X11_KEY_CLOSE_BRACKET = 0x5d; -X11_KEY_COLON = 0x3a; -X11_KEY_REVERSE_SOLIUS = 0x5c; // another back slash (back slash on JP keyboard) -X11_KEY_CAPSLOCK = 0xffe5; -X11_KEY_SEMI_COLON = 0x3b; -X11_KEY_SHIFT = 0xffe1; -X11_KEY_ADD = 0x2b; - -KEY_DOWN = 5; -KEY_UP = 6; - -//JP keyboard type -// -var keyboardTables = [ - {tindex: 0, keyboardType: "EN-Cooked", mappingTable: - {X11: [ {keycode: 222, entry: X11_KEY_CIRCUMFLEX_ACCENT}, - {keycode: 220, entry: X11_KEY_YEN_MARK}, - {keycode: 219, entry: X11_KEY_OPEN_BRACKET}, - {keycode: 221, entry: X11_KEY_CLOSE_BRACKET}, - {keycode: 59, entry: X11_KEY_COLON, browser: "Firefox"}, - {keycode: 186, entry: X11_KEY_COLON, browser: "Chrome"}, - {keycode: 9, entry: 9, guestos: "XenServer"}, - {keycode: 226, entry: X11_KEY_REVERSE_SOLIUS}, - {keycode: 240, entry: [ - {type: KEY_DOWN, code: X11_KEY_CAPSLOCK, modifiers: 0 }, - {type: KEY_UP, code: X11_KEY_CAPSLOCK, modifiers: 0 }, - ] - }, - ], - keyPress: [ - {keycode: 59, entry: [ - {type: KEY_DOWN, code: X11_KEY_SEMI_COLON, modifiers: 0 }, - {type: KEY_UP, code: X11_KEY_SEMI_COLON, modifiers: 0 }, - ] - }, - {keycode: 43, entry: [ - {type: KEY_DOWN, code: X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: KEY_DOWN, code: X11_KEY_ADD, modifiers: 0, shift: false }, - {type: KEY_UP, code: X11_KEY_ADD, modifiers: 0, shift: false }, - {type: KEY_UP, code: X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: KEY_DOWN, code: X11_KEY_ADD, modifiers: 0, shift: true }, - {type: KEY_UP, code: X11_KEY_ADD, modifiers: 0, shift: true }, - ] - }, - ] - } - } ] - diff --git a/console-proxy/js/ajaxviewer.js b/console-proxy/js/ajaxviewer.js deleted file mode 100644 index e95615d8946..00000000000 --- a/console-proxy/js/ajaxviewer.js +++ /dev/null @@ -1,1444 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -var g_logger; - -///////////////////////////////////////////////////////////////////////////// -// class StringBuilder -// -function StringBuilder(initStr) { - this.strings = new Array(""); - this.append(initStr); -} - -StringBuilder.prototype = { - append : function (str) { - if (str) { - this.strings.push(str); - } - return this; - }, - - clear : function() { - this.strings.length = 1; - return this; - }, - - toString: function() { - return this.strings.join(""); - } -}; - - -function getCurrentLanguage() { - if(acceptLanguages) { - var tokens = acceptLanguages.split(','); - if(tokens.length > 0) - return tokens[0]; - - return "en-us"; - } else { - return "en-us"; - } -} - -///////////////////////////////////////////////////////////////////////////// -// class KeyboardMapper -// -function KeyboardMapper() { - this.mappedInput = []; - this.jsX11KeysymMap = []; - this.jsKeyPressX11KeysymMap = []; -} - -// -// RAW keyboard -// Primarily translates KeyDown/KeyUp event, either as is (if there is no mapping entry) -// or through mapped result. -// -// For KeyPress event, it translates it only if there exist a mapping entry -// in jsX11KeysymMap map and the entry meets the condition -// -// COOKED keyboard -// Primarily translates KeyPress event, either as is or through mapped result -// It translates KeyDown/KeyUp only there exists a mapping entry, or if there -// is no mapping entry, translate when certain modifier key is pressed (i.e., -// CTRL or ALT key -// -// Mapping entry types -// direct : will be directly mapped into the entry value with the same event type -// boolean : only valid for jsX11KeysymMap, existence of this type, no matter true or false -// in value, corresponding KeyDown/KeyUp event will be masked -// array : contains a set of conditional mapping entry -// -// Conditional mapping entry -// -// { -// type: , code: , modifiers: , -// shift : , -- match on shift state -// guestos : , -- match on guestos type -// browser: , -- match on browser -// browserVersion: -- match on browser version -// } -// -KeyboardMapper.KEYBOARD_TYPE_RAW = 0; -KeyboardMapper.KEYBOARD_TYPE_COOKED = 1; - -KeyboardMapper.prototype = { - - setKeyboardType : function(keyboardType) { - this.keyboardType = keyboardType; - - if(keyboardType == KeyboardMapper.KEYBOARD_TYPE_RAW) { - // intialize keyboard mapping for RAW keyboard - this.jsX11KeysymMap[AjaxViewer.JS_KEY_CAPSLOCK] = AjaxViewer.X11_KEY_CAPSLOCK; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACKSPACE] = AjaxViewer.X11_KEY_BACKSPACE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_TAB] = AjaxViewer.X11_KEY_TAB; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ENTER] = AjaxViewer.X11_KEY_ENTER; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ESCAPE] = AjaxViewer.X11_KEY_ESCAPE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_INSERT] = AjaxViewer.X11_KEY_INSERT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DELETE] = AjaxViewer.X11_KEY_DELETE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_HOME] = AjaxViewer.X11_KEY_HOME; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_END] = AjaxViewer.X11_KEY_END; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEUP] = AjaxViewer.X11_KEY_PAGEUP; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEDOWN] = AjaxViewer.X11_KEY_PAGEDOWN; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_LEFT] = AjaxViewer.X11_KEY_LEFT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_UP] = AjaxViewer.X11_KEY_UP; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_RIGHT] = AjaxViewer.X11_KEY_RIGHT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DOWN] = AjaxViewer.X11_KEY_DOWN; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F1] = AjaxViewer.X11_KEY_F1; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F2] = AjaxViewer.X11_KEY_F2; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F3] = AjaxViewer.X11_KEY_F3; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F4] = AjaxViewer.X11_KEY_F4; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F5] = AjaxViewer.X11_KEY_F5; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F6] = AjaxViewer.X11_KEY_F6; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F7] = AjaxViewer.X11_KEY_F7; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F8] = AjaxViewer.X11_KEY_F8; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F9] = AjaxViewer.X11_KEY_F9; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F10] = AjaxViewer.X11_KEY_F10; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F11] = AjaxViewer.X11_KEY_F11; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F12] = AjaxViewer.X11_KEY_F12; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_SHIFT] = AjaxViewer.X11_KEY_SHIFT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_CTRL] = AjaxViewer.X11_KEY_CTRL; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ALT] = AjaxViewer.X11_KEY_ALT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_GRAVE_ACCENT] = AjaxViewer.X11_KEY_GRAVE_ACCENT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_SUBSTRACT] = AjaxViewer.X11_KEY_SUBSTRACT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ADD] = AjaxViewer.X11_KEY_ADD; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_OPEN_BRACKET] = AjaxViewer.X11_KEY_OPEN_BRACKET; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_CLOSE_BRACKET] = AjaxViewer.X11_KEY_CLOSE_BRACKET; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACK_SLASH] = AjaxViewer.X11_KEY_BACK_SLASH; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_SINGLE_QUOTE] = AjaxViewer.X11_KEY_SINGLE_QUOTE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_COMMA] = AjaxViewer.X11_KEY_COMMA; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_PERIOD] = AjaxViewer.X11_KEY_PERIOD; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_FORWARD_SLASH] = AjaxViewer.X11_KEY_FORWARD_SLASH; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DASH] = AjaxViewer.X11_KEY_DASH; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_SEMI_COLON] = AjaxViewer.X11_KEY_SEMI_COLON; - - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD0] = AjaxViewer.X11_KEY_NUMPAD0; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD1] = AjaxViewer.X11_KEY_NUMPAD1; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD2] = AjaxViewer.X11_KEY_NUMPAD2; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD3] = AjaxViewer.X11_KEY_NUMPAD3; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD4] = AjaxViewer.X11_KEY_NUMPAD4; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD5] = AjaxViewer.X11_KEY_NUMPAD5; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD6] = AjaxViewer.X11_KEY_NUMPAD6; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD7] = AjaxViewer.X11_KEY_NUMPAD7; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD8] = AjaxViewer.X11_KEY_NUMPAD8; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD9] = AjaxViewer.X11_KEY_NUMPAD9; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DECIMAL_POINT] = AjaxViewer.X11_KEY_DECIMAL_POINT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DIVIDE] = AjaxViewer.X11_KEY_DIVIDE; - - this.jsX11KeysymMap[AjaxViewer.JS_KEY_MULTIPLY] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0 }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ASTERISK, modifiers: 0 }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ASTERISK, modifiers: 0 }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0 } - ]; - - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ADD] = false; - this.jsKeyPressX11KeysymMap = []; - this.jsKeyPressX11KeysymMap[61] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false } - ]; - this.jsKeyPressX11KeysymMap[43] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true } - ]; - } else { - // initialize mapping for COOKED keyboard - this.jsX11KeysymMap[AjaxViewer.JS_KEY_CAPSLOCK] = AjaxViewer.X11_KEY_CAPSLOCK; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACKSPACE] = AjaxViewer.X11_KEY_BACKSPACE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_TAB] = AjaxViewer.X11_KEY_TAB; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ENTER] = AjaxViewer.X11_KEY_ENTER; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ESCAPE] = AjaxViewer.X11_KEY_ESCAPE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_INSERT] = AjaxViewer.X11_KEY_INSERT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DELETE] = AjaxViewer.X11_KEY_DELETE; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_HOME] = AjaxViewer.X11_KEY_HOME; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_END] = AjaxViewer.X11_KEY_END; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEUP] = AjaxViewer.X11_KEY_PAGEUP; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEDOWN] = AjaxViewer.X11_KEY_PAGEDOWN; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_LEFT] = AjaxViewer.X11_KEY_LEFT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_UP] = AjaxViewer.X11_KEY_UP; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_RIGHT] = AjaxViewer.X11_KEY_RIGHT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_DOWN] = AjaxViewer.X11_KEY_DOWN; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F1] = AjaxViewer.X11_KEY_F1; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F2] = AjaxViewer.X11_KEY_F2; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F3] = AjaxViewer.X11_KEY_F3; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F4] = AjaxViewer.X11_KEY_F4; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F5] = AjaxViewer.X11_KEY_F5; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F6] = AjaxViewer.X11_KEY_F6; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F7] = AjaxViewer.X11_KEY_F7; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F8] = AjaxViewer.X11_KEY_F8; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F9] = AjaxViewer.X11_KEY_F9; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F10] = AjaxViewer.X11_KEY_F10; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F11] = AjaxViewer.X11_KEY_F11; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_F12] = AjaxViewer.X11_KEY_F12; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_SHIFT] = AjaxViewer.X11_KEY_SHIFT; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_CTRL] = AjaxViewer.X11_KEY_CTRL; - this.jsX11KeysymMap[AjaxViewer.JS_KEY_ALT] = AjaxViewer.X11_KEY_ALT; - } - }, - RawkeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) { - if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) { - - // special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del - if(code == AjaxViewer.JS_KEY_INSERT) { - if((modifiers & AjaxViewer.ALT_KEY_MASK) != 0 && (modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) { - this.mappedInput.push({type : eventType, code: 0xffff, modifiers: modifiers}); - return; - } - } - - var X11Keysym = code; - if(this.jsX11KeysymMap[code] != undefined) { - X11Keysym = this.jsX11KeysymMap[code]; - if(typeof this.jsX11KeysymMap[code] == "boolean") { - return; - } else if($.isArray(X11Keysym)) { - for(var i = 0; i < X11Keysym.length; i++) { - // How to set the guestos, browser, version value??? add later - if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser, browserVersion)) { - this.mappedInput.push(X11Keysym[i]); - } - } - } else { - this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); - } - } else { - this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); - } - - // special handling for ALT/CTRL key - if(eventType == AjaxViewer.KEY_UP && (code == AjaxViewer.JS_KEY_ALT || code == code == AjaxViewer.JS_KEY_CTRL)) - this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers}); - - } else if(eventType == AjaxViewer.KEY_PRESS) { - var X11Keysym = code; - X11Keysym = this.jsKeyPressX11KeysymMap[code]; - if(X11Keysym) { - if($.isArray(X11Keysym)) { - for(var i = 0; i < X11Keysym.length; i++) { - if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser)) - this.mappedInput.push(X11Keysym[i]); - } - } else { - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: X11Keysym, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: X11Keysym, modifiers: modifiers}); - } - } - } - }, - - CookedKeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) { - if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) { - - // special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del - if(code == AjaxViewer.JS_KEY_INSERT) { - if((modifiers & AjaxViewer.ALT_KEY_MASK) != 0 && (modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) { - this.mappedInput.push({type : eventType, code: 0xffff, modifiers: modifiers}); - return; - } - } - - var X11Keysym = code; - if(this.jsX11KeysymMap[code] != undefined) { - X11Keysym = this.jsX11KeysymMap[code]; - if(typeof this.jsX11KeysymMap[code] == "boolean") { - return; - } else if($.isArray(X11Keysym)) { - for(var i = 0; i < X11Keysym.length; i++) { - if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser, browserVersion)) { - this.mappedInput.push(X11Keysym[i]); - } - } - } else { - this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); - } - } else { - if((modifiers & (AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK)) != 0) { - this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); - } - } - - // special handling for ALT/CTRL key - if(eventType == AjaxViewer.KEY_UP && (code == AjaxViewer.JS_KEY_ALT || code == code == AjaxViewer.JS_KEY_CTRL)) - this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers}); - - } else if(eventType == AjaxViewer.KEY_PRESS) { - // special handling for * and + key on number pad - if(code == AjaxViewer.JS_NUMPAD_MULTIPLY) { - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: 42, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: 42, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); - return; - } - - if(code == AjaxViewer.JS_NUMPAD_PLUS) { - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: 43, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: 43, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); - return; - } - - // ENTER/BACKSPACE key should already have been sent through KEY DOWN/KEY UP event - if(code == AjaxViewer.JS_KEY_ENTER || code == AjaxViewer.JS_KEY_BACKSPACE) - return; - - if(code > 0) { - var X11Keysym = code; - X11Keysym = this.jsKeyPressX11KeysymMap[code]; - if(X11Keysym) { - if($.isArray(X11Keysym)) { - for(var i = 0; i < X11Keysym.length; i++) { - if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser)) - this.mappedInput.push(X11Keysym[i]); - } - } else { - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: X11Keysym, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: X11Keysym, modifiers: modifiers}); - } - } else { - // if there is no mappting entry, use the JS keypress code directly - this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: code, modifiers: modifiers}); - this.mappedInput.push({type : AjaxViewer.KEY_UP, code: code, modifiers: modifiers}); - } - } - } - }, - - inputFeed : function(eventType, code, modifiers, guestos, browser, browserVersion) { - if(this.keyboardType == KeyboardMapper.KEYBOARD_TYPE_RAW) - this.RawkeyboardInputHandler(eventType, code, modifiers, guestos, browser, browserVersion); - else - this.CookedKeyboardInputHandler(eventType, code, modifiers, guestos, browser, browserVersion); - }, - - getMappedInput : function() { - var mappedInput = this.mappedInput; - this.mappedInput = []; - return mappedInput; - }, - - isConditionalEntryMatched : function(eventType, code, modifiers, entry, guestos, browser, browserVersion) { - if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) { - // for KeyDown/KeyUp events, we require that the type in entry should match with - // the real input - if(entry.type != eventType) - return false; - } - - // check conditional match - if(entry.shift != undefined) { - var shift = ((modifiers & AjaxViewer.SHIFT_KEY_MASK) != 0 ? true : false); - if(entry.shift ^ shift) - return false; - } - - if(entry.guestos != undefined) { - if(entry.guestos != guestos) - return false; - } - - if(entry.browser != undefined) { - if(entry.browser != browser) - return false; - } - - if(entry.browserVersion != undefined) { - if(entry.browserVersion != browserVersion) - return false; - } - - return true; - }, - - isModifierInput : function(code) { - return $.inArray(code, [AjaxViewer.ALT_KEY_MASK, AjaxViewer.SHIFT_KEY_MASK, AjaxViewer.CTRL_KEY_MASK, AjaxViewer.META_KEY_MASK]) >= 0; - } -}; - -///////////////////////////////////////////////////////////////////////////// -// class AjaxViewer -// -function AjaxViewer(panelId, imageUrl, updateUrl, tileMap, width, height, tileWidth, tileHeight) { - // logging is disabled by default so that it won't have negative impact on performance - // however, a back door key-sequence can trigger to open the logger window, it is designed to help - // trouble-shooting - g_logger = new Logger(); - - // g_logger.enable(true); - // g_logger.open(); - - var ajaxViewer = this; - this.imageLoaded = false; - this.fullImage = true; - this.imgUrl = imageUrl; - this.img = new Image(); - $(this.img).attr('src', imageUrl).load(function() { - ajaxViewer.imageLoaded = true; - }); - - this.updateUrl = updateUrl; - this.tileMap = tileMap; - this.dirty = true; - this.width = width; - this.height = height; - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.maxTileZIndex = 1; - - this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_ENGLISH; - this.keyboardMappers = []; - - this.timer = 0; - this.eventQueue = []; - this.sendingEventInProgress = false; - - this.lastClickEvent = { x: 0, y: 0, button: 0, modifiers: 0, time: new Date().getTime() }; - - if(window.onStatusNotify == undefined) - window.onStatusNotify = function(status) {}; - - this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); -// this.setupKeyboardTranslationle(); - this.setupKeyboardTranslationTable(this.keyboardMappers); - this.setupUIController(); -} - -// client event types -AjaxViewer.MOUSE_MOVE = 1; -AjaxViewer.MOUSE_DOWN = 2; -AjaxViewer.MOUSE_UP = 3; -AjaxViewer.KEY_PRESS = 4; -AjaxViewer.KEY_DOWN = 5; -AjaxViewer.KEY_UP = 6; -AjaxViewer.EVENT_BAG = 7; -AjaxViewer.MOUSE_DBLCLK = 8; - -// use java AWT key modifier masks -AjaxViewer.SHIFT_KEY_MASK = 64; -AjaxViewer.CTRL_KEY_MASK = 128; -AjaxViewer.META_KEY_MASK = 256; -AjaxViewer.ALT_KEY_MASK = 512; -AjaxViewer.LEFT_SHIFT_MASK = 1024; -AjaxViewer.LEFT_CTRL_MASK = 2048; -AjaxViewer.LEFT_ALT_MASK = 4096; - -AjaxViewer.EVENT_QUEUE_MOUSE_EVENT = 1; -AjaxViewer.EVENT_QUEUE_KEYBOARD_EVENT = 2; - -AjaxViewer.STATUS_RECEIVING = 1; -AjaxViewer.STATUS_RECEIVED = 2; -AjaxViewer.STATUS_SENDING = 3; -AjaxViewer.STATUS_SENT = 4; - -AjaxViewer.KEYBOARD_TYPE_ENGLISH = "us"; -AjaxViewer.KEYBOARD_TYPE_JAPANESE = "jp"; - -AjaxViewer.JS_KEY_BACKSPACE = 8; -AjaxViewer.JS_KEY_TAB = 9; -AjaxViewer.JS_KEY_ENTER = 13; -AjaxViewer.JS_KEY_SHIFT = 16; -AjaxViewer.JS_KEY_CTRL = 17; -AjaxViewer.JS_KEY_ALT = 18; -AjaxViewer.JS_KEY_PAUSE = 19; -AjaxViewer.JS_KEY_CAPSLOCK = 20; -AjaxViewer.JS_KEY_ESCAPE = 27; -AjaxViewer.JS_KEY_PAGEUP = 33; -AjaxViewer.JS_KEY_PAGEDOWN = 34; -AjaxViewer.JS_KEY_END = 35; -AjaxViewer.JS_KEY_HOME = 36; -AjaxViewer.JS_KEY_LEFT = 37; -AjaxViewer.JS_KEY_UP = 38; -AjaxViewer.JS_KEY_RIGHT = 39; -AjaxViewer.JS_KEY_DOWN = 40; -AjaxViewer.JS_KEY_INSERT = 45; -AjaxViewer.JS_KEY_DELETE = 46; -AjaxViewer.JS_KEY_LEFT_WINDOW_KEY = 91; -AjaxViewer.JS_KEY_RIGHT_WINDOW_KEY = 92; -AjaxViewer.JS_KEY_SELECT_KEY = 93; -AjaxViewer.JS_KEY_NUMPAD0 = 96; -AjaxViewer.JS_KEY_NUMPAD1 = 97; -AjaxViewer.JS_KEY_NUMPAD2 = 98; -AjaxViewer.JS_KEY_NUMPAD3 = 99; -AjaxViewer.JS_KEY_NUMPAD4 = 100; -AjaxViewer.JS_KEY_NUMPAD5 = 101; -AjaxViewer.JS_KEY_NUMPAD6 = 102; -AjaxViewer.JS_KEY_NUMPAD7 = 103; -AjaxViewer.JS_KEY_NUMPAD8 = 104; -AjaxViewer.JS_KEY_NUMPAD9 = 105; -AjaxViewer.JS_KEY_MULTIPLY = 106; -AjaxViewer.JS_KEY_ADD = 107; -AjaxViewer.JS_KEY_SUBSTRACT = 109; -AjaxViewer.JS_KEY_DECIMAL_POINT = 110; -AjaxViewer.JS_KEY_DIVIDE = 111; -AjaxViewer.JS_KEY_F1 = 112; -AjaxViewer.JS_KEY_F2 = 113; -AjaxViewer.JS_KEY_F3 = 114; -AjaxViewer.JS_KEY_F4 = 115; -AjaxViewer.JS_KEY_F5 = 116; -AjaxViewer.JS_KEY_F6 = 117; -AjaxViewer.JS_KEY_F7 = 118; -AjaxViewer.JS_KEY_F8 = 119; -AjaxViewer.JS_KEY_F9 = 120; -AjaxViewer.JS_KEY_F10 = 121; -AjaxViewer.JS_KEY_F11 = 122; -AjaxViewer.JS_KEY_F12 = 123; -AjaxViewer.JS_KEY_NUMLOCK = 144; -AjaxViewer.JS_KEY_SCROLLLOCK = 145; -AjaxViewer.JS_KEY_SEMI_COLON = 186; // ; -AjaxViewer.JS_KEY_EQUAL_SIGN = 187; // = -AjaxViewer.JS_KEY_COMMA = 188; // , -AjaxViewer.JS_KEY_DASH = 189; // - -AjaxViewer.JS_KEY_PERIOD = 190; // . -AjaxViewer.JS_KEY_FORWARD_SLASH = 191; // / -AjaxViewer.JS_KEY_GRAVE_ACCENT = 192; // ` -AjaxViewer.JS_KEY_OPEN_BRACKET = 219; // [ -AjaxViewer.JS_KEY_BACK_SLASH = 220; // \ -AjaxViewer.JS_KEY_CLOSE_BRACKET = 221; // ] -AjaxViewer.JS_KEY_SINGLE_QUOTE = 222; // ' -AjaxViewer.JS_NUMPAD_PLUS = 43; -AjaxViewer.JS_NUMPAD_MULTIPLY = 42; -AjaxViewer.JS_KEY_NUM8 = 56; - -// keycode from Japanese keyboard -AjaxViewer.JS_KEY_JP_COLON = 222; // :* on JP keyboard -AjaxViewer.JS_KEY_JP_CLOSE_BRACKET = 220; // [{ on JP keyboard -AjaxViewer.JS_KEY_JP_AT_SIGN = 219; // @` on JP keyboard -AjaxViewer.JS_KEY_JP_OPEN_BRACKET = 221; // [{ on JP keyboard -AjaxViewer.JS_KEY_JP_BACK_SLASH = 193; // \| on JP keyboard -AjaxViewer.JS_KEY_JP_YEN_MARK = 255; - -AjaxViewer.JS_KEY_JP_EQUAL = 109; // -= ON JP keyboard -AjaxViewer.JS_KEY_JP_ACUTE = 107; // ^~ on JP keyboard - -// X11 keysym definitions -AjaxViewer.X11_KEY_CAPSLOCK = 0xffe5; -AjaxViewer.X11_KEY_BACKSPACE = 0xff08; -AjaxViewer.X11_KEY_TAB = 0xff09; -AjaxViewer.X11_KEY_ENTER = 0xff0d; -AjaxViewer.X11_KEY_ESCAPE = 0xff1b; -AjaxViewer.X11_KEY_INSERT = 0xff63; -AjaxViewer.X11_KEY_DELETE = 0xffff; -AjaxViewer.X11_KEY_HOME = 0xff50; -AjaxViewer.X11_KEY_END = 0xff57; -AjaxViewer.X11_KEY_PAGEUP = 0xff55; -AjaxViewer.X11_KEY_PAGEDOWN = 0xff56; -AjaxViewer.X11_KEY_LEFT = 0xff51; -AjaxViewer.X11_KEY_UP = 0xff52; -AjaxViewer.X11_KEY_RIGHT = 0xff53; -AjaxViewer.X11_KEY_DOWN = 0xff54; -AjaxViewer.X11_KEY_F1 = 0xffbe; -AjaxViewer.X11_KEY_F2 = 0xffbf; -AjaxViewer.X11_KEY_F3 = 0xffc0; -AjaxViewer.X11_KEY_F4 = 0xffc1; -AjaxViewer.X11_KEY_F5 = 0xffc2; -AjaxViewer.X11_KEY_F6 = 0xffc3; -AjaxViewer.X11_KEY_F7 = 0xffc4; -AjaxViewer.X11_KEY_F8 = 0xffc5; -AjaxViewer.X11_KEY_F9 = 0xffc6; -AjaxViewer.X11_KEY_F10 = 0xffc7; -AjaxViewer.X11_KEY_F11 = 0xffc8; -AjaxViewer.X11_KEY_F12 = 0xffc9; -AjaxViewer.X11_KEY_SHIFT = 0xffe1; -AjaxViewer.X11_KEY_CTRL = 0xffe3; -AjaxViewer.X11_KEY_ALT = 0xffe9; -AjaxViewer.X11_KEY_GRAVE_ACCENT = 0x60; -AjaxViewer.X11_KEY_SUBSTRACT = 0x2d; -AjaxViewer.X11_KEY_ADD = 0x2b; -AjaxViewer.X11_KEY_OPEN_BRACKET = 0x5b; -AjaxViewer.X11_KEY_CLOSE_BRACKET = 0x5d; -AjaxViewer.X11_KEY_BACK_SLASH = 0x7c; -AjaxViewer.X11_KEY_REVERSE_SOLIUS = 0x5c; // another back slash (back slash on JP keyboard) -AjaxViewer.X11_KEY_SINGLE_QUOTE = 0x22; -AjaxViewer.X11_KEY_COMMA = 0x3c; -AjaxViewer.X11_KEY_PERIOD = 0x3e; -AjaxViewer.X11_KEY_FORWARD_SLASH = 0x3f; -AjaxViewer.X11_KEY_DASH = 0x2d; -AjaxViewer.X11_KEY_COLON = 0x3a; -AjaxViewer.X11_KEY_SEMI_COLON = 0x3b; -AjaxViewer.X11_KEY_NUMPAD0 = 0x30; -AjaxViewer.X11_KEY_NUMPAD1 = 0x31; -AjaxViewer.X11_KEY_NUMPAD2 = 0x32; -AjaxViewer.X11_KEY_NUMPAD3 = 0x33; -AjaxViewer.X11_KEY_NUMPAD4 = 0x34; -AjaxViewer.X11_KEY_NUMPAD5 = 0x35; -AjaxViewer.X11_KEY_NUMPAD6 = 0x36; -AjaxViewer.X11_KEY_NUMPAD7 = 0x37; -AjaxViewer.X11_KEY_NUMPAD8 = 0x38; -AjaxViewer.X11_KEY_NUMPAD9 = 0x39; -AjaxViewer.X11_KEY_DECIMAL_POINT = 0x2e; -AjaxViewer.X11_KEY_DIVIDE = 0x3f; -AjaxViewer.X11_KEY_TILDE = 0x7e; // ~ -AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT = 0x5e; // ^ -AjaxViewer.X11_KEY_YEN_MARK = 0xa5; // Japanese YEN mark -AjaxViewer.X11_KEY_ASTERISK = 0x2a; - -AjaxViewer.getEventName = function(type) { - switch(type) { - case AjaxViewer.MOUSE_MOVE : - return "MOUSE_MOVE"; - - case AjaxViewer.MOUSE_DOWN : - return "MOUSE_DOWN"; - - case AjaxViewer.MOUSE_UP : - return "MOUSE_UP"; - - case AjaxViewer.KEY_PRESS : - return "KEY_PRESS"; - - case AjaxViewer.KEY_DOWN : - return "KEY_DOWN"; - - case AjaxViewer.KEY_UP : - return "KEY_UP"; - - case AjaxViewer.EVENT_BAG : - return "EVENT_BAG"; - - case AjaxViewer.MOUSE_DBLCLK : - return "MOUSE_DBLCLK"; - } - - return "N/A"; -}; - -AjaxViewer.prototype = { - setDirty: function(value) { - this.dirty = value; - }, - - isDirty: function() { - return this.dirty; - }, - - isImageLoaded: function() { - return this.imageLoaded; - }, - - refresh: function(imageUrl, tileMap, fullImage) { - var ajaxViewer = this; - var img = $(this.img); - this.fullImage = fullImage; - this.imgUrl=imageUrl; - - img.attr('src', imageUrl).load(function() { - ajaxViewer.imageLoaded = true; - }); - this.tileMap = tileMap; - }, - - resize: function(panelId, width, height, tileWidth, tileHeight) { - $(".canvas_tile", document.body).each(function() { - $(this).remove(); - }); - $("table", $("#" + panelId)).remove(); - - this.width = width; - this.height = height; - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); - }, - - start: function() { - var ajaxViewer = this; - this.timer = setInterval(function() { ajaxViewer.heartbeat(); }, 50); - - $(document).bind("ajaxError", function(event, XMLHttpRequest, ajaxOptions, thrownError) { - ajaxViewer.onAjaxError(event, XMLHttpRequest, ajaxOptions, thrownError); - }); - - this.eventQueue = []; // reset event queue - this.sendingEventInProgress = false; - ajaxViewer.installMouseHook(); - ajaxViewer.installKeyboardHook(); - - $(window).bind("resize", function() { - ajaxViewer.onWindowResize(); - }); - }, - - stop: function() { - clearInterval(this.timer); - this.deleteCanvas(); - - this.uninstallMouseHook(); - this.uninstallKeyboardHook(); - this.eventQueue = []; - this.sendingEventInProgress = false; - - $(document).unbind("ajaxError"); - $(window).unbind("resize"); - }, - - sendMouseEvent: function(event, x, y, whichButton, modifiers) { - this.eventQueue.push({ - type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, - event: event, - x: x, - y: y, - code: whichButton, - modifiers: modifiers - }); - this.checkEventQueue(); - }, -//NEW insert the keyboard tables file here -// ajaxKeys.js - - setupKeyboardTranslationTable : function() { - this.keyboardMappers = []; - - var mapper = new KeyboardMapper(); - this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_ENGLISH] = mapper; - mapper.setKeyboardType(KeyboardMapper.KEYBOARD_TYPE_COOKED); - - mapper = new KeyboardMapper(); - this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_JAPANESE] = mapper; - mapper.setKeyboardType(KeyboardMapper.KEYBOARD_TYPE_RAW); - - // JP keyboard plugged in a English host OS -/* - mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_COLON] = AjaxViewer.X11_KEY_COLON; - mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_CLOSE_BRACKET] = AjaxViewer.X11_KEY_CLOSE_BRACKET; - mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_AT_SIGN] = AjaxViewer.X11_KEY_GRAVE_ACCENT; - mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_OPEN_BRACKET] = AjaxViewer.X11_KEY_OPEN_BRACKET; - mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_BACK_SLASH] = AjaxViewer.X11_KEY_REVERSE_SOLIUS; // X11 REVERSE SOLIDUS - mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_YEN_MARK] = AjaxViewer.X11_KEY_YEN_MARK; // X11 YEN SIGN - mapper.jsKeyPressX11KeysymMap[61] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT, modifiers: 0 }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT, modifiers: 0 }, - ]; - - mapper.jsKeyPressX11KeysymMap[43] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_TILDE, modifiers: 0, shift: true }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_TILDE, modifiers: 0, shift: true } - ]; -*/ - -/* Old - // JP keyboard plugged in a Japanese host OS - mapper.jsX11KeysymMap[222] = AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT; - mapper.jsX11KeysymMap[220] = AjaxViewer.X11_KEY_YEN_MARK; - mapper.jsX11KeysymMap[219] = AjaxViewer.X11_KEY_OPEN_BRACKET; - mapper.jsX11KeysymMap[221] = AjaxViewer.X11_KEY_CLOSE_BRACKET; - mapper.jsX11KeysymMap[59] = AjaxViewer.X11_KEY_COLON; // Firefox - mapper.jsX11KeysymMap[186] = AjaxViewer.X11_KEY_COLON; // Chrome - mapper.jsX11KeysymMap[226] = AjaxViewer.X11_KEY_REVERSE_SOLIUS; // \| key left to right SHIFT on JP keyboard - mapper.jsX11KeysymMap[240] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_CAPSLOCK, modifiers: 0 }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_CAPSLOCK, modifiers: 0 }, - ]; - - // for keycode 107, keypress 59 - mapper.jsKeyPressX11KeysymMap[59] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SEMI_COLON, modifiers: 0 }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SEMI_COLON, modifiers: 0 }, - ]; - - // for keycode 107, keypress 43 - mapper.jsKeyPressX11KeysymMap[43] = [ - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, - {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }, - {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }, - ]; - -*/ - // create the mapping table based on the tables input - if (keyboardTables != undefined ) { - - for(var i = 0; i < keyboardTables.length; i++) { - var mappingTbl = keyboardTables[i]; - var mappings = mappingTbl.mappingTable; - var x11Maps = mappings.X11; - for (var j = 0; j < x11Maps.length; j++) { - var code = x11Maps[j].keycode; - var mappedEntry = x11Maps[j].entry; - mapper.jsX11KeysymMap[code] = mappedEntry; - } - var keyPressMaps = mappings.keyPress; - for (var j = 0; j < keyPressMaps.length; j++) { - var code = keyPressMaps[j].keycode; - var mappedEntry = keyPressMaps[j].entry; - mapper.jsKeyPressX11KeysymMap[code] = mappedEntry; - } - - } - } - - }, // end of the setupKeyboardTranslationTable function - - getCurrentKeyboardMapper : function() { - return this.keyboardMappers[this.currentKeyboard]; - }, - - setupUIController : function() { - var ajaxViewer = this; - var pullDownElement = $("#toolbar").find(".pulldown"); - pullDownElement.hover( - function(e) { - var subMenu = pullDownElement.find("ul"); - var offset = subMenu.parent().offset(); - subMenu.css("left", offset.left); - - $("li.current").removeClass("current"); - $("li:has(a[cmd$=" + ajaxViewer.currentKeyboard + "])", subMenu).addClass("current"); - subMenu.css("z-index", "" + ajaxViewer.maxTileZIndex + 1).show(); - return false; - }, - - function(e) { - pullDownElement.find("ul").hide(); - return false; - } - ); - - $("[cmd]", "#toolbar").each(function(i, val) { - $(val).click(function(e) { - var cmd = $(e.target).attr("cmd"); - if(cmd) - ajaxViewer.onCommand(cmd); - else { - var cmdLink = $(e.target).closest("a"); - - if(cmdLink.attr("cmd")) { - var cmd = cmdLink.attr("cmd"); - ajaxViewer.onCommand(cmd); - } - } - }); - }); - }, - - onCommand : function(cmd) { - if(cmd == "keyboard_jp") { - $("#toolbar").find(".pulldown").find("ul").hide(); - this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_JAPANESE; - } else if(cmd == "keyboard_us") { - $("#toolbar").find(".pulldown").find("ul").hide(); - this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_ENGLISH; - } else if(cmd == "sendCtrlAltDel") { - this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffe9, 0); // X11 Alt - this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffe3, 0); // X11 Ctrl - this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffff, 0); // X11 Del - this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffff, 0); - this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffe3, 0); - this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffe9, 0); - } else if(cmd == "sendCtrlEsc") { - this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffe3, 0); // X11 Ctrl - this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xff1b, 0); // X11 ESC - this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xff1b, 0); - this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffe3, 0); - } else if(cmd == "toggle_logwin") { - if(!g_logger.isOpen()) { - g_logger.enable(true); - g_logger.open(); - g_logger.log(Logger.LEVEL_SYS, "Accept languages: " + acceptLanguages + ", current language: " + getCurrentLanguage()); - } else { - g_logger.close(); - } - } - }, - - sendKeyboardEvent: function(event, code, modifiers) { - // back door to open logger window - CTRL-ATL-SHIFT+SPACE - if(code == 32 && - (modifiers & AjaxViewer.SHIFT_KEY_MASK | AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK) == (AjaxViewer.SHIFT_KEY_MASK | AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK)) { - - if(!g_logger.isOpen()) { - g_logger.enable(true); - g_logger.open(); - g_logger.log(Logger.LEVEL_SYS, "Accept languages: " + acceptLanguages + ", current language: " + getCurrentLanguage()); - } else { - g_logger.close(); - } - } - - var len; - g_logger.log(Logger.LEVEL_INFO, "Keyboard event: " + AjaxViewer.getEventName(event) + ", code: " + code + ", modifiers: " + modifiers + ', char: ' + String.fromCharCode(code)); - this.eventQueue.push({ - type: AjaxViewer.EVENT_QUEUE_KEYBOARD_EVENT, - event: event, - code: code, - modifiers: modifiers - }); - - if(event != AjaxViewer.KEY_DOWN) - this.checkEventQueue(); - }, - - aggregateEvents: function() { - var ajaxViewer = this; - var aggratedQueue = []; - - var aggregating = false; - var mouseX; - var mouseY; - $.each(ajaxViewer.eventQueue, function(index, item) { - if(item.type != AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { - aggratedQueue.push(item); - } else { - if(!aggregating) { - if(item.event == AjaxViewer.MOUSE_MOVE) { - aggregating = true; - mouseX = item.x; - mouseY = item.y; - } else { - aggratedQueue.push(item); - } - } else { - if(item.event == AjaxViewer.MOUSE_MOVE) { - // continue to aggregate mouse move event - mouseX = item.x; - mouseY = item.y; - } else { - aggratedQueue.push({ - type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, - event: AjaxViewer.MOUSE_MOVE, - x: mouseX, - y: mouseY, - code: 0, - modifiers: 0 - }); - aggregating = false; - - aggratedQueue.push(item); - } - } - } - }); - - if(aggregating) { - aggratedQueue.push({ - type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, - event: AjaxViewer.MOUSE_MOVE, - x: mouseX, - y: mouseY, - code: 0, - modifiers: 0 - }); - } - - this.eventQueue = aggratedQueue; - }, - - checkEventQueue: function() { - var ajaxViewer = this; - - if(!this.sendingEventInProgress && this.eventQueue.length > 0) { - var sb = new StringBuilder(); - sb.append(""+this.eventQueue.length).append("|"); - $.each(this.eventQueue, function() { - var item = this; - if(item.type == AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { - sb.append(""+item.type).append("|"); - sb.append(""+item.event).append("|"); - sb.append(""+item.x).append("|"); - sb.append(""+item.y).append("|"); - sb.append(""+item.code).append("|"); - sb.append(""+item.modifiers).append("|"); - } else { - sb.append(""+item.type).append("|"); - sb.append(""+item.event).append("|"); - sb.append(""+item.code).append("|"); - sb.append(""+item.modifiers).append("|"); - } - }); - this.eventQueue.length = 0; - - var url = ajaxViewer.updateUrl + "&event=" + AjaxViewer.EVENT_BAG; - - g_logger.log(Logger.LEVEL_TRACE, "Posting client event " + sb.toString() + "..."); - - ajaxViewer.sendingEventInProgress = true; - window.onStatusNotify(AjaxViewer.STATUS_SENDING); - $.post(url, {data: sb.toString()}, function(data, textStatus) { - g_logger.log(Logger.LEVEL_TRACE, "Client event " + sb.toString() + " is posted"); - - ajaxViewer.sendingEventInProgress = false; - window.onStatusNotify(AjaxViewer.STATUS_SENT); - - ajaxViewer.checkUpdate(); - }, 'html'); - } - }, - - onAjaxError: function(event, XMLHttpRequest, ajaxOptions, thrownError) { - if(window.onClientError != undefined && jQuery.isFunction(window.onClientError)) { - window.onClientError(); - } - }, - - onWindowResize: function() { - var offset = this.panel.offset(); - - var row = $('tr:first', this.panel); - var cell = $('td:first', row); - var tile = this.getTile(cell, 'tile'); - - var tileOffset = tile.offset(); - var deltaX = offset.left - tileOffset.left; - var deltaY = offset.top - tileOffset.top; - - if(deltaX != 0 || deltaY != 0) { - $(".canvas_tile").each(function() { - var offsetFrom = $(this).offset(); - $(this).css('left', offsetFrom.left + deltaX).css('top', offsetFrom.top + deltaY); - }); - } - }, - - deleteCanvas: function() { - $('.canvas_tile', $(document.body)).each(function() { - $(this).remove(); - }); - }, - - generateCanvas: function(wrapperDivId, width, height, tileWidth, tileHeight) { - var canvasParent = $('#' + wrapperDivId); - canvasParent.width(width); - canvasParent.height(height); - - if(window.onCanvasSizeChange != undefined && jQuery.isFunction(window.onCanvasSizeChange)) - window.onCanvasSizeChange(width, height); - - var tableDef = '\r\n'; - var i = 0; - var j = 0; - for(i = 0; i < Math.ceil((height + tileHeight - 1) / tileHeight); i++) { - var rowHeight = Math.min(height - i*tileHeight, tileHeight); - tableDef += '\r\n'; - - for(j = 0; j < Math.ceil((width + tileWidth - 1) / tileWidth); j++) { - var colWidth = Math.min(width - j*tileWidth, tileWidth); - tableDef += '\r\n'; - } - tableDef += '\r\n'; - } - tableDef += '
\r\n'; - - return $(tableDef).appendTo(canvasParent); - }, - - getTile: function(cell, name) { - var clonedDiv = cell.data(name); - if(!clonedDiv) { - var offset = cell.offset(); - var divDef = "
"; - - clonedDiv = $(divDef).appendTo($(document.body)); - cell.data(name, clonedDiv); - } - - return clonedDiv; - }, - - initCell: function(cell) { - if(!cell.data("init")) { - cell.data("init", true); - - cell.data("current", 0); - this.getTile(cell, "tile2"); - this.getTile(cell, "tile"); - } - }, - - displayCell: function(cell, bg) { - var div; - var divPrev; - if(!cell.data("current")) { - cell.data("current", 1); - - divPrev = this.getTile(cell, "tile"); - div = this.getTile(cell, "tile2"); - } else { - cell.data("current", 0); - divPrev = this.getTile(cell, "tile2"); - div = this.getTile(cell, "tile"); - } - - var zIndex = parseInt(divPrev.css("z-index")) + 1; - this.maxTileZIndex = Math.max(this.maxTileZIndex, zIndex); - div.css("z-index", zIndex); - div.css("background", bg); - }, - - updateTile: function() { - if(this.dirty) { - var ajaxViewer = this; - var tileWidth = this.tileWidth; - var tileHeight = this.tileHeight; - var imgUrl = this.imgUrl; - var panel = this.panel; - - if(this.fullImage) { - $.each(this.tileMap, function() { - var i = $(this)[0]; - var j = $(this)[1]; - var row = $("TR:eq("+i+")", panel); - var cell = $("TD:eq("+j+")", row); - var attr = "url(" + imgUrl + ") -"+j*tileWidth+"px -"+i*tileHeight + "px"; - - ajaxViewer.initCell(cell); - ajaxViewer.displayCell(cell, attr); - }); - } else { - $.each(this.tileMap, function(index) { - var i = $(this)[0]; - var j = $(this)[1]; - var offset = index*tileWidth; - var attr = "url(" + imgUrl + ") no-repeat -"+offset+"px 0px"; - var row = $("TR:eq("+i+")", panel); - var cell = $("TD:eq("+j+")", row); - - ajaxViewer.initCell(cell); - ajaxViewer.displayCell(cell, attr); - }); - } - - this.dirty = false; - } - }, - - heartbeat: function() { - this.checkEventQueue(); - this.checkUpdate(); - }, - - checkUpdate: function() { - if(!this.isDirty()) - return; - - if(this.isImageLoaded()) { - this.updateTile(); - var url = this.updateUrl; - var ajaxViewer = this; - - window.onStatusNotify(AjaxViewer.STATUS_RECEIVING); - $.getScript(url, function(data, textStatus) { - if(/^/.test(data)) { - ajaxViewer.stop(); - $(document.body).html(data); - } else { - eval(data); - ajaxViewer.setDirty(true); - window.onStatusNotify(AjaxViewer.STATUS_RECEIVED); - - ajaxViewer.checkUpdate(); - } - }); - } - }, - - ptInPanel: function(pageX, pageY) { - var mainPanel = this.panel; - - var offset = mainPanel.offset(); - var x = pageX - offset.left; - var y = pageY - offset.top; - - if(x < 0 || y < 0 || x > mainPanel.width() - 1 || y > mainPanel.height() - 1) - return false; - return true; - }, - - pageToPanel: function(pageX, pageY) { - var mainPanel = this.panel; - - var offset = mainPanel.offset(); - var x = pageX - offset.left; - var y = pageY - offset.top; - - if(x < 0) - x = 0; - if(x > mainPanel.width() - 1) - x = mainPanel.width() - 1; - - if(y < 0) - y = 0; - if(y > mainPanel.height() - 1) - y = mainPanel.height() - 1; - - return { x: Math.ceil(x), y: Math.ceil(y) }; - }, - - installMouseHook: function() { - var ajaxViewer = this; - var target = $(document.body); - - target.mousemove(function(e) { - if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) - return true; - - var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); - ajaxViewer.onMouseMove(pt.x, pt.y); - - e.stopPropagation(); - return false; - }); - - target.mousedown(function(e) { - ajaxViewer.panel.parent().focus(); - - if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) - return true; - - var modifiers = ajaxViewer.getKeyModifiers(e); - var whichButton = e.button; - - var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); - ajaxViewer.onMouseDown(pt.x, pt.y, whichButton, modifiers); - - e.stopPropagation(); - return false; - }); - - target.mouseup(function(e) { - if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) - return true; - - var modifiers = ajaxViewer.getKeyModifiers(e); - var whichButton = e.button; - - var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); - - ajaxViewer.onMouseUp(pt.x, pt.y, whichButton, modifiers); - e.stopPropagation(); - return false; - }); - - // disable browser right-click context menu - target.bind("contextmenu", function() { return false; }); - }, - - uninstallMouseHook : function() { - var target = $(document); - target.unbind("mousemove"); - target.unbind("mousedown"); - target.unbind("mouseup"); - target.unbind("contextmenu"); - }, - - requiresDefaultKeyProcess : function(e) { - switch(e.which) { - case 8 : // backspace - case 9 : // TAB - case 19 : // PAUSE/BREAK - case 20 : // CAPSLOCK - case 27 : // ESCAPE - case 16 : // SHIFT key - case 17 : // CTRL key - case 18 : // ALT key - case 33 : // PGUP - case 34 : // PGDN - case 35 : // END - case 36 : // HOME - case 37 : // LEFT - case 38 : // UP - case 39 : // RIGHT - case 40 : // DOWN - return false; - } - - if(this.getKeyModifiers(e) == AjaxViewer.SHIFT_KEY_MASK) - return true; - - if(this.getKeyModifiers(e) != 0) - return false; - - return true; - }, - - installKeyboardHook: function() { - var ajaxViewer = this; - var target = $(document); - - target.keypress(function(e) { - ajaxViewer.onKeyPress(e.which, ajaxViewer.getKeyModifiers(e)); - - e.stopPropagation(); - if(ajaxViewer.requiresDefaultKeyProcess(e)) - return true; - - e.preventDefault(); - return false; - }); - - target.keydown(function(e) { - ajaxViewer.onKeyDown(e.which, ajaxViewer.getKeyModifiers(e)); - - e.stopPropagation(); - if(ajaxViewer.requiresDefaultKeyProcess(e)) - return true; - - e.preventDefault(); - return false; - }); - - target.keyup(function(e) { - ajaxViewer.onKeyUp(e.which, ajaxViewer.getKeyModifiers(e)); - - e.stopPropagation(); - if(ajaxViewer.requiresDefaultKeyProcess(e)) - return true; - - e.preventDefault(); - return false; - }); - }, - - uninstallKeyboardHook : function() { - var target = $(document); - target.unbind("keypress"); - target.unbind("keydown"); - target.unbind("keyup"); - }, - - onMouseMove: function(x, y) { - this.sendMouseEvent(AjaxViewer.MOUSE_MOVE, x, y, 0, 0); - }, - - onMouseDown: function(x, y, whichButton, modifiers) { - this.sendMouseEvent(AjaxViewer.MOUSE_DOWN, x, y, whichButton, modifiers); - }, - - onMouseUp: function(x, y, whichButton, modifiers) { - this.sendMouseEvent(AjaxViewer.MOUSE_UP, x, y, whichButton, modifiers); - - var curTick = new Date().getTime(); - if(this.lastClickEvent.time && (curTick - this.lastClickEvent.time < 300)) { - this.onMouseDblClick(this.lastClickEvent.x, this.lastClickEvent.y, - this.lastClickEvent.button, this.lastClickEvent.modifiers); - } - - this.lastClickEvent.x = x; - this.lastClickEvent.y = y; - this.lastClickEvent.button = whichButton; - this.lastClickEvent.modifiers = modifiers; - this.lastClickEvent.time = curTick; - }, - - onMouseDblClick: function(x, y, whichButton, modifiers) { - this.sendMouseEvent(AjaxViewer.MOUSE_DBLCLK, x, y, whichButton, modifiers); - }, - - onKeyPress: function(code, modifiers) { - g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-PRESS: " + code + ", modifers: " + modifiers); - - this.dispatchKeyboardInput(AjaxViewer.KEY_PRESS, code, modifiers); - }, - - onKeyDown: function(code, modifiers) { - g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-DOWN: " + code + ", modifers: " + modifiers); - - this.dispatchKeyboardInput(AjaxViewer.KEY_DOWN, code, modifiers); - }, - - onKeyUp: function(code, modifiers) { - g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-UP: " + code + ", modifers: " + modifiers); - - this.dispatchKeyboardInput(AjaxViewer.KEY_UP, code, modifiers); - }, - - dispatchKeyboardInput : function(event, code, modifiers) { - var keyboardMapper = ajaxViewer.getCurrentKeyboardMapper(); - keyboardMapper.inputFeed(event, code, modifiers, this.guestos, $.browser, $.browser.version); - this.dispatchMappedKeyboardInput(keyboardMapper.getMappedInput()); - }, - - dispatchMappedKeyboardInput : function(mappedInput) { - for(var i = 0; i < mappedInput.length; i++) { - switch(mappedInput[i].type) { - case AjaxViewer.KEY_DOWN : - this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, mappedInput[i].code, mappedInput[i].modifiers); - break; - - case AjaxViewer.KEY_UP : - this.sendKeyboardEvent(AjaxViewer.KEY_UP, mappedInput[i].code, mappedInput[i].modifiers); - break; - - case AjaxViewer.KEY_PRESS : - this.sendKeyboardEvent(AjaxViewer.KEY_PRESS, mappedInput[i].code, mappedInput[i].modifiers); - break; - } - } - }, - - getKeyModifiers: function(e) { - var modifiers = 0; - if(e.altKey) - modifiers |= AjaxViewer.ALT_KEY_MASK; - - if(e.altLeft) - modifiers |= AjaxViewer.LEFT_ALT_MASK; - - if(e.ctrlKey) - modifiers |= AjaxViewer.CTRL_KEY_MASK; - - if(e.ctrlLeft) - modifiers |= AjaxViewer.LEFT_CTRL_MASK; - - if(e.shiftKey) - modifiers |= AjaxViewer.SHIFT_KEY_MASK; - - if(e.shiftLeft) - modifiers |= AjaxViewer.LEFT_SHIFT_MASK; - - if(e.metaKey) - modifiers |= AjaxViewer.META_KEY_MASK; - - return modifiers; - } -}; - diff --git a/console-proxy/js/cloud.logger.js b/console-proxy/js/cloud.logger.js deleted file mode 100644 index 751627ed96c..00000000000 --- a/console-proxy/js/cloud.logger.js +++ /dev/null @@ -1,338 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -// Version: 1.9.1.152 - -// -// Javascript logger utility -// Author -// Kelven Yang -// 2/25/2010 -// - -function Logger() { - this.bDockEnabled = true; - - this.logWin = null; - this.logger = null; - this.header = null; - - this.bEnabled = true; - this.level = 0; - - this.bMoving = false; - this.offsetStart = {left: 0, top: 0}; - this.ptStart = {x: 0, y: 0}; -} - -Logger.DEFAULT_WIN_HEIGHT = 500; -Logger.LEVEL_TRACE = 0; -Logger.LEVEL_DEBUG = 1; -Logger.LEVEL_INFO = 2; -Logger.LEVEL_WARN = 3; -Logger.LEVEL_ERROR = 4; -Logger.LEVEL_FATAL = 5; -Logger.LEVEL_SYS = 100; - -Logger.prototype = { - - open: function() { - if(this.logWin) { - this.logWin.show(); - - this.log(Logger.LEVEL_SYS, "Logger is open in browser: " + this.objectToString($.browser)); - return; - } - - var logger = this; - var logWinMarkup = [ - '
', - '
', - '
', - '', - '', - '', - '
', - '', - '
', - '
', - '
', - '', - '', - '
', - '
', - '
', - '
' - ].join(''); - - this.logWin = $(logWinMarkup).appendTo(document.body); - this.header = $('.logwin_title:first', this.logWin); - this.logger = $('.logwin_content:first', this.logWin); - - $(".logwin_title", this.logWin).mousedown(function(e) { - if($(e.target).attr('cmd')) - return true; - - if(!logger.bMoving) { - logger.bMoving = true; - logger.offsetStart = logger.logWin.offset(); - logger.ptStart = {x: e.pageX, y: e.pageY}; - - $(document).bind("mousemove", function(e) { - if(logger.bMoving) { - logger.enableDocking(false); - - var logWinNewLeft = logger.offsetStart.left + e.pageX - logger.ptStart.x; - var logWinNewTop = logger.offsetStart.top + e.pageY - logger.ptStart.y; - - logger.logWin.css("left", logWinNewLeft + "px").css("top", logWinNewTop + "px"); - } - return false; - }); - - $(document).bind("mouseup", function(e) { - if(logger.bMoving) { - logger.bMoving = false; - $(document).unbind("mousemove", arguments.callee.name); - $(document).unbind("mouseup", arguments.callee.name); - - return false; - } - return true; - }); - } - - // prevent default handling - return false; - }).dblclick(function(e) { - logger.expand(!logger.isExpanded()); - }); - - this.logWin.click(function(e) { - if($(e.target).attr('cmd')) { - switch($(e.target).attr('cmd')) { - case '1' : - logger.enable(true); - break; - - case '2' : - logger.enable(false); - break; - - case '3' : - logger.clear(); - break; - - case '4' : - logger.enableDocking(true); - logger.dockIn(); - break; - - case '5' : - logger.expand(!logger.isExpanded()); - break; - - default : - break; - } - } - }); - - $("#template_type", this.logWin).change(function(e) { - logger.setLevel(parseInt($(this).val())); - }); - - this.logWin.css("left", (($(document.body).width() - this.logWin.width()) / 2) + "px"); - this.dockIn(); - - this.log(Logger.LEVEL_SYS, "Logger is open in browser: " + this.objectToString($.browser)); - }, - - close: function() { - if(this.logWin) - this.logWin.hide(); - }, - - isOpen: function() { - if(this.logWin) - return this.logWin.is(":visible"); - return false; - }, - - dockIn: function() { - var logger = this; - var offset = this.logWin.offset(); - var bottom = offset.top + this.logWin.height(); - var delta = bottom - 2; - - this.logWin.animate({top: (offset.top - delta) + "px"}, 200, - function() { - logger.logWin.unbind("mouseleave"); - logger.logWin.bind("mouseenter", function(e) { - if(logger.bDockEnabled) - logger.dockOut(); - }); - } - ); - }, - - dockOut: function() { - var logger = this; - this.logWin.animate({top: "0px"}, 200, - function() { - logger.logWin.unbind("mouseenter"); - logger.logWin.bind("mouseleave", function(e) { - if(logger.bDockEnabled) { - var xPosInLogWin = e.pageX - logger.logWin.offset().left; - var yPosInLogWin = e.pageY - logger.logWin.offset().top; - - if(xPosInLogWin < 0 || yPosInLogWin < 0 || - xPosInLogWin > logger.logWin.width() || yPosInLogWin > logger.logWin.height()) { - logger.dockIn(); - } - } - }); - } - ); - }, - - enableDocking: function(bEnable) { - this.bDockEnabled = bEnable; - }, - - log: function(level, message) { - // Note : LEVEL_SYS message will always be logged - if(this.logger && (level == Logger.LEVEL_SYS || this.bEnabled && level >= this.level)) { - var curTime = new Date(); - var curTimeString = [ - '', curTime.getMonth(), - '/', curTime.getDate(), - '/', curTime.getYear(), - ' ', - curTime.getHours(), - ':', curTime.getMinutes(), - ":", curTime.getSeconds(), - ".", curTime.getMilliseconds()].join(''); - - this.logger.append(this.getLevelDisplayString(level) + " - " + curTimeString + " - " + message + '
'); - } - }, - - clear: function() { - if(this.logger) { - this.logger.empty(); - this.log(Logger.LEVEL_SYS, "Logger is cleared"); - } - }, - - setLevel: function(level) { - this.level = level; - - this.log(Logger.LEVEL_SYS, "Set logger trace level to " + this.getLevelDisplayString(level)); - }, - - enable: function(bEnabled) { - this.bEnabled = bEnabled; - - if(bEnabled) - this.log(Logger.LEVEL_SYS, "Logger is enabled"); - else - this.log(Logger.LEVEL_SYS, "Logger is disabled"); - }, - - expand: function(bExpand) { - if(bExpand) { - this.logWin.height(Logger.DEFAULT_WIN_HEIGHT); - this.logger.height(Logger.DEFAULT_WIN_HEIGHT - this.header.height()); - } else { - this.logWin.height(this.header.height()); - this.logger.height(0); - } - }, - - isExpanded: function() { - return this.logWin.height() > this.header.height(); - }, - - getLevelDisplayString: function(level) { - switch(level) { - case Logger.LEVEL_TRACE : - return "TRACE"; - - case Logger.LEVEL_DEBUG : - return "DEBUG"; - - case Logger.LEVEL_INFO : - return "INFO"; - - case Logger.LEVEL_WARN : - return "WARN"; - - case Logger.LEVEL_ERROR : - return "ERROR"; - - case Logger.LEVEL_FATAL : - return "FATAL"; - - case Logger.LEVEL_SYS : - return "SYSINFO"; - } - - return "LEVEL " + level; - }, - - // this is a util function which actually can be put elsewhere instead of in this class - objectToString : function(object) { - if(object) { - if(object instanceof Object) { - var sb = ['{' ]; - - $.each(object, function(name, val) { - sb.push('' + name + ': '); - - if(val instanceof Object) { - sb.push(this.objectToString(val)); - } else { - sb.push('' + val); - } - - sb.push(','); - }); - - if(sb[sb.length - 1] == ',' ) - sb.length = sb.length - 1; - - sb.push('}'); - return sb.join(""); - } else { - return '' + object; - } - } else { - return 'N/A'; - } - } -}; - diff --git a/console-proxy/js/handler.js b/console-proxy/js/handler.js deleted file mode 100644 index d22ff079ee6..00000000000 --- a/console-proxy/js/handler.js +++ /dev/null @@ -1,72 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - -// -// Callback handlers for AJAX viewer -// Author -// Kelven Yang -// 11/18/2009 -// -function onKickoff() { - ajaxViewer.stop(); - $('#toolbar').remove(); - $('#main_panel').html('

This session is terminated because a session for the same VM has been created elsewhere.

'); -} - -function onDisconnect() { - ajaxViewer.stop(); - $('#toolbar').remove(); - $('#main_panel').html('

This session is terminated as the machine you are accessing has terminated the connection.

'); -} - -function onClientError() { - ajaxViewer.stop(); - $('#toolbar').remove(); - $('#main_panel').html('

Client communication error, please retry later.

'); -} - -function onCanvasSizeChange(width, height) { - $('#toolbar').width(width); -} - -function onStatusNotify(status) { - if(status == ajaxViewer.STATUS_SENDING || status == ajaxViewer.STATUS_RECEIVING) - $('#light').removeClass('dark').addClass('bright'); - else - $('#light').removeClass('bright').addClass('dark'); -} - -function sendCtrlAltDel() { - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 45, ajaxViewer.CTRL_KEY | ajaxViewer.ALT_KEY); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 45, ajaxViewer.CTRL_KEY | ajaxViewer.ALT_KEY); -} - -function sendCtrlEsc() { - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 17, 0); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 27, ajaxViewer.CTRL_KEY); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 27, ajaxViewer.CTRL_KEY); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 17, 0); -} - -function sendAltTab() { - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 18, 0); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 9, ajaxViewer.ALT_KEY); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 9, ajaxViewer.ALT_KEY); - ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 18, 0); -} diff --git a/console-proxy/js/jquery.js b/console-proxy/js/jquery.js deleted file mode 100644 index b1ae21d8b23..00000000000 --- a/console-proxy/js/jquery.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/console-proxy/libexec/console-proxy-runner.in b/console-proxy/libexec/console-proxy-runner.in deleted file mode 100755 index 4f18aab36e5..00000000000 --- a/console-proxy/libexec/console-proxy-runner.in +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -#run.sh runs the agent client. - -cd `dirname "$0"` - -SYSTEMJARS="@SYSTEMJARS@" -SCP=$(build-classpath $SYSTEMJARS) ; if [ $? != 0 ] ; then SCP="@SYSTEMCLASSPATH@" ; fi -DCP="@DEPSCLASSPATH@" -ACP="@AGENTCLASSPATH@" -export CLASSPATH=$SCP:$DCP:$ACP:@CPSYSCONFDIR@ -for jarfile in "@PREMIUMJAVADIR@"/* ; do - if [ ! -e "$jarfile" ] ; then continue ; fi - CLASSPATH=$jarfile:$CLASSPATH -done -for plugin in "@PLUGINJAVADIR@"/* ; do - if [ ! -e "$plugin" ] ; then continue ; fi - CLASSPATH=$plugin:$CLASSPATH -done -export CLASSPATH - -set -e -cd "@CPLIBDIR@" -echo Current directory is "$PWD" -echo CLASSPATH to run the console proxy: "$CLASSPATH" - -export PATH=/sbin:/usr/sbin:"$PATH" -SERVICEARGS= -for x in private public ; do - configuration=`grep -q "^$x.network.device" "@CPSYSCONFDIR@"/agent.properties || true` - if [ -n "$CONFIGURATION" ] ; then - echo "Using manually-configured network device $CONFIGURATION" - else - defaultroute=`ip route | grep ^default | cut -d ' ' -f 5` - test -n "$defaultroute" - echo "Using auto-discovered network device $defaultroute which is the default route" - SERVICEARGS="$SERVICEARGS $x.network.device="$defaultroute - fi -done - -function termagent() { - if [ "$agentpid" != "" ] ; then - echo Killing VMOps Console Proxy "(PID $agentpid)" with SIGTERM >&2 - kill -TERM $agentpid - echo Waiting for agent to exit >&2 - wait $agentpid - ex=$? - echo Agent exited with return code $ex >&2 - else - echo Agent PID is unknown >&2 - fi -} - -trap termagent TERM -while true ; do - java -Xms128M -Xmx384M -cp "$CLASSPATH" "$@" com.cloud.agent.AgentShell $SERVICEARGS & - agentpid=$! - echo "Console Proxy started. PID: $!" >&2 - wait $agentpid - ex=$? - if [ $ex -gt 128 ]; then - echo "wait on console proxy process interrupted by SIGTERM" >&2 - exit $ex - fi - echo "Console proxy exited with return code $ex" >&2 - if [ $ex -eq 0 ] || [ $ex -eq 1 ] || [ $ex -eq 66 ] || [ $ex -gt 128 ]; then - echo "Exiting..." > /dev/stderr - exit $ex - fi - echo "Restarting console proxy..." > /dev/stderr - sleep 1 -done diff --git a/console-proxy/pom.xml b/console-proxy/pom.xml deleted file mode 100644 index ab9af163180..00000000000 --- a/console-proxy/pom.xml +++ /dev/null @@ -1,246 +0,0 @@ - - - 4.0.0 - cloud-console-proxy - Apache CloudStack Console Proxy - - org.apache.cloudstack - cloudstack - 4.1.0-SNAPSHOT - - - mkisofs - - - - log4j - log4j - ${cs.log4j.version} - - - com.google.code.gson - gson - ${cs.gson.version} - - - commons-codec - commons-codec - ${cs.codec.version} - - - - org.apache.cloudstack - cloud-agent - ${project.version} - - - org.apache.cloudstack - cloud-patches - ${project.version} - pom - - - - install - src - - - certs - - realhostip.csr - - - - - - maven-assembly-plugin - 2.3 - - systemvm - false - - systemvm-descriptor.xml - - - - - make-systemvm - package - - single - - - - - - maven-resources-plugin - 2.6 - - - copy-resources - - package - - copy-resources - - - dist - - - target - - systemvm.zip - - - - ../patches/systemvm/debian/config/root/.ssh - - authorized_keys - - - - - - - - - maven-antrun-plugin - 1.7 - - - copy-cloud-scripts - package - - run - - - - - - - - - - - - - - - - genisoimage - - - /usr/bin/genisoimage - - - - genisoimage - - - - vmware - - - nonoss - - - - - org.apache.cloudstack - cloud-plugin-hypervisor-vmware - ${project.version} - - - org.apache.cloudstack - cloud-vmware-base - ${project.version} - - - - - systemvm - - - systemvm - - - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 - - - package - - exec - - - - - ${mkisofs} - dist - - -quiet - -r - -o - systemvm.iso - systemvm.zip - cloud-scripts.tgz - authorized_keys - - - - - - - - - diff --git a/console-proxy/scripts/_run.sh b/console-proxy/scripts/_run.sh deleted file mode 100755 index e408378afbc..00000000000 --- a/console-proxy/scripts/_run.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - - - - -#run.sh runs the console proxy. - -# make sure we delete the old files from the original template -rm console-proxy.jar -rm console-common.jar -rm conf/cloud.properties - -set -x - -CP=./:./conf -for file in *.jar -do - CP=${CP}:$file -done -keyvalues= - -CMDLINE=$(cat /var/cache/cloud/cmdline) - -#CMDLINE="graphical utf8 eth0ip=0.0.0.0 eth0mask=255.255.255.0 eth1ip=192.168.140.40 eth1mask=255.255.255.0 eth2ip=172.24.0.50 eth2mask=255.255.0.0 gateway=172.24.0.1 dns1=72.52.126.11 template=domP dns2=72.52.126.12 host=192.168.1.142 port=8250 mgmtcidr=192.168.1.0/24 localgw=192.168.140.1 zone=5 pod=5" -for i in $CMDLINE - do - KEY=$(echo $i | cut -s -d= -f1) - VALUE=$(echo $i | cut -s -d= -f2) - [ "$KEY" == "" ] && continue - case $KEY in - *) - keyvalues="${keyvalues} $KEY=$VALUE" - esac - done - -tot_mem_k=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}') -let "tot_mem_m=tot_mem_k>>10" -let "eightypcnt=$tot_mem_m*8/10" -let "maxmem=$tot_mem_m-80" - -if [ $maxmem -gt $eightypcnt ] -then - maxmem=$eightypcnt -fi - -java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/console-proxy/scripts/config_auth.sh b/console-proxy/scripts/config_auth.sh deleted file mode 100755 index 4b74f8eb995..00000000000 --- a/console-proxy/scripts/config_auth.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - - - - - -BASE_DIR="/var/www/html/copy/template/" -HTACCESS="$BASE_DIR/.htaccess" - -PASSWDFILE="/etc/httpd/.htpasswd" -if [ -d /etc/apache2 ] -then - PASSWDFILE="/etc/apache2/.htpasswd" -fi - -config_htaccess() { - mkdir -p $BASE_DIR - result=$? - echo "Options -Indexes" > $HTACCESS - let "result=$result+$?" - echo "AuthType Basic" >> $HTACCESS - let "result=$result+$?" - echo "AuthName \"Authentication Required\"" >> $HTACCESS - let "result=$result+$?" - echo "AuthUserFile \"$PASSWDFILE\"" >> $HTACCESS - let "result=$result+$?" - echo "Require valid-user" >> $HTACCESS - let "result=$result+$?" - return $result -} - -write_passwd() { - local user=$1 - local passwd=$2 - htpasswd -bc $PASSWDFILE $user $passwd - return $? -} - -if [ $# -ne 2 ] ; then - echo $"Usage: `basename $0` username password " - exit 0 -fi - -write_passwd $1 $2 -if [ $? -ne 0 ] -then - echo "Failed to update password" - exit 2 -fi - -config_htaccess -exit $? diff --git a/console-proxy/scripts/config_ssl.sh b/console-proxy/scripts/config_ssl.sh deleted file mode 100755 index 8d80c4731ad..00000000000 --- a/console-proxy/scripts/config_ssl.sh +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - - - -help() { - printf " -c use customized key/cert\n" - printf " -k path of private key\n" - printf " -p path of certificate of public key\n" - printf " -t path of certificate chain\n" -} - - -config_httpd_conf() { - local ip=$1 - local srvr=$2 - cp -f /etc/httpd/conf/httpd.conf.orig /etc/httpd/conf/httpd.conf - sed -i -e "s/Listen.*:80$/Listen $ip:80/" /etc/httpd/conf/httpd.conf - echo " " >> /etc/httpd/conf/httpd.conf - echo " DocumentRoot /var/www/html/" >> /etc/httpd/conf/httpd.conf - echo " ServerName $srvr" >> /etc/httpd/conf/httpd.conf - echo " SSLEngine on" >> /etc/httpd/conf/httpd.conf - echo " SSLCertificateFile /etc/httpd/ssl/certs/realhostip.crt" >> /etc/httpd/conf/httpd.conf - echo " SSLCertificateKeyFile /etc/httpd/ssl/keys/realhostip.key" >> /etc/httpd/conf/httpd.conf - echo "" >> /etc/httpd/conf/httpd.conf -} - -config_apache2_conf() { - local ip=$1 - local srvr=$2 - cp -f /etc/apache2/sites-available/default.orig /etc/apache2/sites-available/default - cp -f /etc/apache2/sites-available/default-ssl.orig /etc/apache2/sites-available/default-ssl - sed -i -e "s///" /etc/apache2/sites-available/default - sed -i -e "s///" /etc/apache2/sites-available/default-ssl - sed -i -e "s/Listen .*:80/Listen $ip:80/g" /etc/apache2/ports.conf - sed -i -e "s/Listen .*:443/Listen $ip:443/g" /etc/apache2/ports.conf - sed -i -e "s/NameVirtualHost .*:80/NameVirtualHost $ip:80/g" /etc/apache2/ports.conf - sed -i 's/ssl-cert-snakeoil.key/cert_apache.key/' /etc/apache2/sites-available/default-ssl - sed -i 's/ssl-cert-snakeoil.pem/cert_apache.crt/' /etc/apache2/sites-available/default-ssl -} - -copy_certs() { - local certdir=$(dirname $0)/certs - local mydir=$(dirname $0) - if [ -d $certdir ] && [ -f $customPrivKey ] && [ -f $customPrivCert ] ; then - mkdir -p /etc/httpd/ssl/keys && mkdir -p /etc/httpd/ssl/certs && cp $customprivKey /etc/httpd/ssl/keys && cp $customPrivCert /etc/httpd/ssl/certs - return $? - fi - if [ ! -z customCertChain ] && [ -f $customCertChain ] ; then - cp $customCertChain /etc/httpd/ssl/certs - fi - return 1 -} - -copy_certs_apache2() { - local certdir=$(dirname $0)/certs - local mydir=$(dirname $0) - if [ -f $customPrivKey ] && [ -f $customPrivCert ] ; then - cp $customPrivKey /etc/ssl/private/cert_apache.key && cp $customPrivCert /etc/ssl/certs/cert_apache.crt - fi - if [ ! -z "$customCertChain" ] && [ -f "$customCertChain" ] ; then - cp $customCertChain /etc/ssl/certs/cert_apache_chain.crt - fi - return 0 -} - - -cflag= -cpkflag= -cpcflag= -cccflag= -customPrivKey=$(dirname $0)/certs/realhostip.key -customPrivCert=$(dirname $0)/certs/realhostip.crt -customCertChain= -publicIp= -hostName= -while getopts 'i:h:k:p:t:c' OPTION -do - case $OPTION in - c) cflag=1 - ;; - k) cpkflag=1 - customPrivKey="$OPTARG" - ;; - p) cpcflag=1 - customPrivCert="$OPTARG" - ;; - t) cccflag=1 - customCertChain="$OPTARG" - ;; - i) publicIp="$OPTARG" - ;; - h) hostName="$OPTARG" - ;; - ?) help - ;; - esac -done - - -if [ -z "$publicIp" ] || [ -z "$hostName" ] -then - help - exit 1 -fi - -if [ "$cflag" == "1" ] -then - if [ "$cpkflag$cpcflag" != "11" ] - then - help - exit 1 - fi - if [ ! -f "$customPrivKey" ] - then - printf "priviate key file is not exist\n" - exit 2 - fi - - if [ ! -f "$customPrivCert" ] - then - printf "public certificate is not exist\n" - exit 3 - fi - - if [ "$cccflag" == "1" ] - then - if [ ! -f "$customCertChain" ] - then - printf "certificate chain is not exist\n" - exit 4 - fi - fi -fi - -if [ -d /etc/apache2 ] -then - copy_certs_apache2 -else - copy_certs -fi - -if [ $? -ne 0 ] -then - echo "Failed to copy certificates" - exit 2 -fi - -if [ -d /etc/apache2 ] -then - config_apache2_conf $publicIp $hostName - /etc/init.d/apache2 stop - /etc/init.d/apache2 start -else - config_httpd_conf $publicIp $hostName -fi - - diff --git a/console-proxy/scripts/ipfirewall.sh b/console-proxy/scripts/ipfirewall.sh deleted file mode 100755 index 4711b8ac6db..00000000000 --- a/console-proxy/scripts/ipfirewall.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -BASE_DIR="/var/www/html/copy/" -HTACCESS="$BASE_DIR/.htaccess" - -config_htaccess() { - mkdir -p $BASE_DIR - result=$? - echo "Options -Indexes" > $HTACCESS - let "result=$result+$?" - echo "order deny,allow" >> $HTACCESS - let "result=$result+$?" - echo "deny from all" >> $HTACCESS - let "result=$result+$?" - return $result -} - -ips(){ - echo "allow from $1" >> $HTACCESS - result=$? - return $result -} - -is_append="$1" -shift -if [ $is_append != "true" ]; then - config_htaccess -fi -for i in $@ -do - ips "$i" -done -exit $? - diff --git a/console-proxy/scripts/run-proxy.sh b/console-proxy/scripts/run-proxy.sh deleted file mode 100644 index d6ccf7c0091..00000000000 --- a/console-proxy/scripts/run-proxy.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - - - - -#run.sh runs the console proxy. - -# make sure we delete the old files from the original template -rm console-proxy.jar -rm console-common.jar -rm conf/cloud.properties - -CP=./:./conf -for file in *.jar -do - CP=${CP}:$file -done - -#CMDLINE=$(cat /proc/cmdline) -#for i in $CMDLINE -# do -# KEY=$(echo $i | cut -d= -f1) -# VALUE=$(echo $i | cut -d= -f2) -# case $KEY in -# mgmt_host) -# MGMT_HOST=$VALUE -# ;; -# esac -# done - -java -mx700m -cp $CP:./conf com.cloud.consoleproxy.ConsoleProxy $@ diff --git a/console-proxy/scripts/run.bat b/console-proxy/scripts/run.bat deleted file mode 100644 index ce6dc404574..00000000000 --- a/console-proxy/scripts/run.bat +++ /dev/null @@ -1,18 +0,0 @@ -rem Licensed to the Apache Software Foundation (ASF) under one -rem or more contributor license agreements. See the NOTICE file -rem distributed with this work for additional information -rem regarding copyright ownership. The ASF licenses this file -rem to you under the Apache License, Version 2.0 (the -rem "License"); you may not use this file except in compliance -rem with the License. You may obtain a copy of the License at -rem -rem http://www.apache.org/licenses/LICENSE-2.0 -rem -rem Unless required by applicable law or agreed to in writing, -rem software distributed under the License is distributed on an -rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -rem KIND, either express or implied. See the License for the -rem specific language governing permissions and limitations -rem under the License. - -java -mx700m -cp cloud-console-proxy.jar;;cloud-console-common.jar;log4j-1.2.15.jar;apache-log4j-extras-1.0.jar;gson-1.3.jar;commons-logging-1.1.1.jar;.;.\conf; com.cloud.consoleproxy.ConsoleProxy %* diff --git a/console-proxy/scripts/run.sh b/console-proxy/scripts/run.sh deleted file mode 100755 index 146d96f0287..00000000000 --- a/console-proxy/scripts/run.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - - - - -#_run.sh runs the agent client. - -# set -x - -while true -do - ./_run.sh "$@" & - wait - ex=$? - if [ $ex -eq 0 ] || [ $ex -eq 1 ] || [ $ex -eq 66 ] || [ $ex -gt 128 ]; then - # permanent errors - sleep 5 - fi - - # user stop agent by service cloud stop - grep 'stop' /usr/local/cloud/systemvm/user_request &>/dev/null - if [ $? -eq 0 ]; then - timestamp=$(date) - echo "$timestamp User stops cloud.com service" >> /var/log/cloud.log - exit 0 - fi - sleep 5 -done diff --git a/console-proxy/scripts/ssvm-check.sh b/console-proxy/scripts/ssvm-check.sh deleted file mode 100644 index a4011647f07..00000000000 --- a/console-proxy/scripts/ssvm-check.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -# Health check script for the Secondary Storage VM - -# DNS server is specified. - - -CMDLINE=/var/cache/cloud/cmdline -for i in `cat $CMDLINE` -do - key=`echo $i | cut -d= -f1` - value=`echo $i | cut -d= -f2` - case $key in - host) - MGMTSERVER=$value - ;; - esac -done - - -# ping dns server -echo ================================================ -DNSSERVER=`egrep '^nameserver' /etc/resolv.conf | awk '{print $2}'| head -1` -echo "First DNS server is " $DNSSERVER -ping -c 2 $DNSSERVER -if [ $? -eq 0 ] -then - echo "Good: Can ping DNS server" -else - echo "WARNING: cannot ping DNS server" - echo "route follows" - route -n -fi - - -# check dns resolve -echo ================================================ -nslookup download.cloud.com 1> /tmp/dns 2>&1 -grep 'no servers could' /tmp/dns 1> /dev/null 2>&1 -if [ $? -eq 0 ] -then - echo "ERROR: DNS not resolving download.cloud.com" - echo resolv.conf follows - cat /etc/resolv.conf - exit 2 -else - echo "Good: DNS resolves download.cloud.com" -fi - - -# check to see if we have the NFS volume mounted -echo ================================================ -mount|grep -v sunrpc|grep nfs 1> /dev/null 2>&1 -if [ $? -eq 0 ] -then - echo "NFS is currently mounted" - # check for write access - for MOUNTPT in `mount|grep -v sunrpc|grep nfs| awk '{print $3}'` - do - if [ $MOUNTPT != "/proc/xen" ] # mounted by xen - then - echo Mount point is $MOUNTPT - touch $MOUNTPT/foo - if [ $? -eq 0 ] - then - echo "Good: Can write to mount point" - rm $MOUNTPT/foo - else - echo "ERROR: Cannot write to mount point" - echo "You need to export with norootsquash" - fi - fi - done -else - echo "ERROR: NFS is not currently mounted" - echo "Try manually mounting from inside the VM" - NFSSERVER=`awk '{print $17}' $CMDLINE|awk -F= '{print $2}'|awk -F: '{print $1}'` - echo "NFS server is " $NFSSERVER - ping -c 2 $NFSSERVER - if [ $? -eq 0 ] - then - echo "Good: Can ping NFS server" - else - echo "WARNING: cannot ping NFS server" - echo routing table follows - route -n - fi -fi - - -# check for connectivity to the management server -echo ================================================ -echo Management server is $MGMTSERVER. Checking connectivity. -socatout=$(echo | socat - TCP:$MGMTSERVER:8250,connect-timeout=3 2>&1) -if [ $? -eq 0 ] -then - echo "Good: Can connect to management server port 8250" -else - echo "ERROR: Cannot connect to $MGMTSERVER port 8250" - echo $socatout - exit 4 -fi - - -# check for the java process running -echo ================================================ -ps -eaf|grep -v grep|grep java 1> /dev/null 2>&1 -if [ $? -eq 0 ] -then - echo "Good: Java process is running" -else - echo "ERROR: Java process not running. Try restarting the SSVM." - exit 3 -fi - -echo ================================================ -echo Tests Complete. Look for ERROR or WARNING above. - -exit 0 diff --git a/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java b/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java deleted file mode 100644 index cff00b3317d..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; - -public class AjaxFIFOImageCache { - private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class); - - private List fifoQueue; - private Map cache; - private int cacheSize; - private int nextKey = 0; - - public AjaxFIFOImageCache(int cacheSize) { - this.cacheSize = cacheSize; - fifoQueue = new ArrayList(); - cache = new HashMap(); - } - - public synchronized void clear() { - fifoQueue.clear(); - cache.clear(); - } - - public synchronized int putImage(byte[] image) { - while(cache.size() >= cacheSize) { - Integer keyToRemove = fifoQueue.remove(0); - cache.remove(keyToRemove); - - if(s_logger.isTraceEnabled()) - s_logger.trace("Remove image from cache, key: " + keyToRemove); - } - - int key = getNextKey(); - - if(s_logger.isTraceEnabled()) - s_logger.trace("Add image to cache, key: " + key); - - cache.put(key, image); - fifoQueue.add(key); - return key; - } - - public synchronized byte[] getImage(int key) { - if (key == 0) { - key = nextKey; - } - if (cache.containsKey(key)) { - if (s_logger.isTraceEnabled()) - s_logger.trace("Retrieve image from cache, key: " + key); - - return cache.get(key); - } - - if (s_logger.isTraceEnabled()) - s_logger.trace("Image is no long in cache, key: " + key); - return null; - } - - public synchronized int getNextKey() { - return ++nextKey; - } -} \ No newline at end of file diff --git a/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java b/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java deleted file mode 100644 index 3fa266792ae..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/AuthenticationException.java +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -public class AuthenticationException extends Exception { - private static final long serialVersionUID = -393139302884898842L; - public AuthenticationException() { - super(); - } - public AuthenticationException(String s) { - super(s); - } - public AuthenticationException(String message, Throwable cause) { - super(message, cause); - } - public AuthenticationException(Throwable cause) { - super(cause); - } -} \ No newline at end of file diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java deleted file mode 100644 index a722d8305a2..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxy.java +++ /dev/null @@ -1,499 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.File; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.InetSocketAddress; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Hashtable; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executor; - -import org.apache.commons.codec.binary.Base64; -import org.apache.log4j.xml.DOMConfigurator; - -import com.cloud.consoleproxy.util.Logger; -import com.google.gson.Gson; -import com.sun.net.httpserver.HttpServer; - -/** - * - * ConsoleProxy, singleton class that manages overall activities in console proxy process. To make legacy code work, we still - */ -public class ConsoleProxy { - private static final Logger s_logger = Logger.getLogger(ConsoleProxy.class); - - public static final int KEYBOARD_RAW = 0; - public static final int KEYBOARD_COOKED = 1; - - public static int VIEWER_LINGER_SECONDS = 180; - - public static Object context; - - // this has become more ugly, to store keystore info passed from management server (we now use management server managed keystore to support - // dynamically changing to customer supplied certificate) - public static byte[] ksBits; - public static String ksPassword; - - public static Method authMethod; - public static Method reportMethod; - public static Method ensureRouteMethod; - - static Hashtable connectionMap = new Hashtable(); - static int httpListenPort = 80; - static int httpCmdListenPort = 8001; - static int reconnectMaxRetry = 5; - static int readTimeoutSeconds = 90; - static int keyboardType = KEYBOARD_RAW; - static String factoryClzName; - static boolean standaloneStart = false; - - static String encryptorPassword = genDefaultEncryptorPassword(); - - private static String genDefaultEncryptorPassword() { - try { - SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); - - byte[] randomBytes = new byte[16]; - random.nextBytes(randomBytes); - return Base64.encodeBase64String(randomBytes); - } catch (NoSuchAlgorithmException e) { - s_logger.error("Unexpected exception ", e); - assert(false); - } - - return "Dummy"; - } - - private static void configLog4j() { - URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); - if(configUrl == null) - configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); - - if(configUrl == null) - configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); - - if(configUrl != null) { - try { - System.out.println("Configure log4j using " + configUrl.toURI().toString()); - } catch (URISyntaxException e1) { - e1.printStackTrace(); - } - - try { - File file = new File(configUrl.toURI()); - - System.out.println("Log4j configuration from : " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); - } catch (URISyntaxException e) { - System.out.println("Unable to convert log4j configuration Url to URI"); - } - // DOMConfigurator.configure(configUrl); - } else { - System.out.println("Configure log4j with default properties"); - } - } - - private static void configProxy(Properties conf) { - s_logger.info("Configure console proxy..."); - for(Object key : conf.keySet()) { - s_logger.info("Property " + (String)key + ": " + conf.getProperty((String)key)); - } - - String s = conf.getProperty("consoleproxy.httpListenPort"); - if (s!=null) { - httpListenPort = Integer.parseInt(s); - s_logger.info("Setting httpListenPort=" + s); - } - - s = conf.getProperty("premium"); - if(s != null && s.equalsIgnoreCase("true")) { - s_logger.info("Premium setting will override settings from consoleproxy.properties, listen at port 443"); - httpListenPort = 443; - factoryClzName = "com.cloud.consoleproxy.ConsoleProxySecureServerFactoryImpl"; - } else { - factoryClzName = ConsoleProxyBaseServerFactoryImpl.class.getName(); - } - - s = conf.getProperty("consoleproxy.httpCmdListenPort"); - if (s!=null) { - httpCmdListenPort = Integer.parseInt(s); - s_logger.info("Setting httpCmdListenPort=" + s); - } - - s = conf.getProperty("consoleproxy.reconnectMaxRetry"); - if (s!=null) { - reconnectMaxRetry = Integer.parseInt(s); - s_logger.info("Setting reconnectMaxRetry=" + reconnectMaxRetry); - } - - s = conf.getProperty("consoleproxy.readTimeoutSeconds"); - if (s!=null) { - readTimeoutSeconds = Integer.parseInt(s); - s_logger.info("Setting readTimeoutSeconds=" + readTimeoutSeconds); - } - } - - public static ConsoleProxyServerFactory getHttpServerFactory() { - try { - Class clz = Class.forName(factoryClzName); - try { - ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory)clz.newInstance(); - factory.init(ConsoleProxy.ksBits, ConsoleProxy.ksPassword); - return factory; - } catch (InstantiationException e) { - s_logger.error(e.getMessage(), e); - return null; - } catch (IllegalAccessException e) { - s_logger.error(e.getMessage(), e); - return null; - } - } catch (ClassNotFoundException e) { - s_logger.warn("Unable to find http server factory class: " + factoryClzName); - return new ConsoleProxyBaseServerFactoryImpl(); - } - } - - public static ConsoleProxyAuthenticationResult authenticateConsoleAccess(ConsoleProxyClientParam param, boolean reauthentication) { - - ConsoleProxyAuthenticationResult authResult = new ConsoleProxyAuthenticationResult(); - authResult.setSuccess(true); - authResult.setReauthentication(reauthentication); - authResult.setHost(param.getClientHostAddress()); - authResult.setPort(param.getClientHostPort()); - - if(standaloneStart) { - return authResult; - } - - if(authMethod != null) { - Object result; - try { - result = authMethod.invoke(ConsoleProxy.context, - param.getClientHostAddress(), - String.valueOf(param.getClientHostPort()), - param.getClientTag(), - param.getClientHostPassword(), - param.getTicket(), - new Boolean(reauthentication)); - } catch (IllegalAccessException e) { - s_logger.error("Unable to invoke authenticateConsoleAccess due to IllegalAccessException" + " for vm: " + param.getClientTag(), e); - authResult.setSuccess(false); - return authResult; - } catch (InvocationTargetException e) { - s_logger.error("Unable to invoke authenticateConsoleAccess due to InvocationTargetException " + " for vm: " + param.getClientTag(), e); - authResult.setSuccess(false); - return authResult; - } - - if(result != null && result instanceof String) { - authResult = new Gson().fromJson((String)result, ConsoleProxyAuthenticationResult.class); - } else { - s_logger.error("Invalid authentication return object " + result + " for vm: " + param.getClientTag() + ", decline the access"); - authResult.setSuccess(false); - } - } else { - s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and allow access to vm: " + param.getClientTag()); - } - - return authResult; - } - - public static void reportLoadInfo(String gsonLoadInfo) { - if(reportMethod != null) { - try { - reportMethod.invoke(ConsoleProxy.context, gsonLoadInfo); - } catch (IllegalAccessException e) { - s_logger.error("Unable to invoke reportLoadInfo due to " + e.getMessage()); - } catch (InvocationTargetException e) { - s_logger.error("Unable to invoke reportLoadInfo due to " + e.getMessage()); - } - } else { - s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and ignore load report"); - } - } - - public static void ensureRoute(String address) { - if(ensureRouteMethod != null) { - try { - ensureRouteMethod.invoke(ConsoleProxy.context, address); - } catch (IllegalAccessException e) { - s_logger.error("Unable to invoke ensureRoute due to " + e.getMessage()); - } catch (InvocationTargetException e) { - s_logger.error("Unable to invoke ensureRoute due to " + e.getMessage()); - } - } else { - s_logger.warn("Unable to find ensureRoute method, console proxy agent is not up to date"); - } - } - - public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword) { - s_logger.info("Start console proxy with context"); - if(conf != null) { - for(Object key : conf.keySet()) { - s_logger.info("Context property " + (String)key + ": " + conf.getProperty((String)key)); - } - } - - configLog4j(); - Logger.setFactory(new ConsoleProxyLoggerFactory()); - - // Using reflection to setup private/secure communication channel towards management server - ConsoleProxy.context = context; - ConsoleProxy.ksBits = ksBits; - ConsoleProxy.ksPassword = ksPassword; - try { - Class contextClazz = Class.forName("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource"); - authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, String.class, String.class, String.class, Boolean.class); - reportMethod = contextClazz.getDeclaredMethod("reportLoadInfo", String.class); - ensureRouteMethod = contextClazz.getDeclaredMethod("ensureRoute", String.class); - } catch (SecurityException e) { - s_logger.error("Unable to setup private channel due to SecurityException", e); - } catch (NoSuchMethodException e) { - s_logger.error("Unable to setup private channel due to NoSuchMethodException", e); - } catch (IllegalArgumentException e) { - s_logger.error("Unable to setup private channel due to IllegalArgumentException", e); - } catch(ClassNotFoundException e) { - s_logger.error("Unable to setup private channel due to ClassNotFoundException", e); - } - - // merge properties from conf file - InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties"); - Properties props = new Properties(); - if (confs == null) { - s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration"); - } else { - try { - props.load(confs); - - for(Object key : props.keySet()) { - // give properties passed via context high priority, treat properties from consoleproxy.properties - // as default values - if(conf.get(key) == null) - conf.put(key, props.get(key)); - } - } catch (Exception e) { - s_logger.error(e.toString(), e); - } - } - - start(conf); - } - - public static void start(Properties conf) { - System.setProperty("java.awt.headless", "true"); - - configProxy(conf); - - ConsoleProxyServerFactory factory = getHttpServerFactory(); - if(factory == null) { - s_logger.error("Unable to load console proxy server factory"); - System.exit(1); - } - - if(httpListenPort != 0) { - startupHttpMain(); - } else { - s_logger.error("A valid HTTP server port is required to be specified, please check your consoleproxy.httpListenPort settings"); - System.exit(1); - } - - if(httpCmdListenPort > 0) { - startupHttpCmdPort(); - } else { - s_logger.info("HTTP command port is disabled"); - } - - ConsoleProxyGCThread cthread = new ConsoleProxyGCThread(connectionMap); - cthread.setName("Console Proxy GC Thread"); - cthread.start(); - } - - private static void startupHttpMain() { - try { - ConsoleProxyServerFactory factory = getHttpServerFactory(); - if(factory == null) { - s_logger.error("Unable to load HTTP server factory"); - System.exit(1); - } - - HttpServer server = factory.createHttpServerInstance(httpListenPort); - server.createContext("/getscreen", new ConsoleProxyThumbnailHandler()); - server.createContext("/resource/", new ConsoleProxyResourceHandler()); - server.createContext("/ajax", new ConsoleProxyAjaxHandler()); - server.createContext("/ajaximg", new ConsoleProxyAjaxImageHandler()); - server.setExecutor(new ThreadExecutor()); // creates a default executor - server.start(); - } catch(Exception e) { - s_logger.error(e.getMessage(), e); - System.exit(1); - } - } - - private static void startupHttpCmdPort() { - try { - s_logger.info("Listening for HTTP CMDs on port " + httpCmdListenPort); - HttpServer cmdServer = HttpServer.create(new InetSocketAddress(httpCmdListenPort), 2); - cmdServer.createContext("/cmd", new ConsoleProxyCmdHandler()); - cmdServer.setExecutor(new ThreadExecutor()); // creates a default executor - cmdServer.start(); - } catch(Exception e) { - s_logger.error(e.getMessage(), e); - System.exit(1); - } - } - - public static void main(String[] argv) { - standaloneStart = true; - configLog4j(); - Logger.setFactory(new ConsoleProxyLoggerFactory()); - - InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties"); - Properties conf = new Properties(); - if (confs == null) { - s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration"); - } else { - try { - conf.load(confs); - } catch (Exception e) { - s_logger.error(e.toString(), e); - } - } - start(conf); - } - - public static ConsoleProxyClient getVncViewer(ConsoleProxyClientParam param) throws Exception { - ConsoleProxyClient viewer = null; - - boolean reportLoadChange = false; - String clientKey = param.getClientMapKey(); - synchronized (connectionMap) { - viewer = connectionMap.get(clientKey); - if (viewer == null) { - viewer = new ConsoleProxyVncClient(); - viewer.initClient(param); - connectionMap.put(clientKey, viewer); - s_logger.info("Added viewer object " + viewer); - - reportLoadChange = true; - } else if (!viewer.isFrontEndAlive()) { - s_logger.info("The rfb thread died, reinitializing the viewer " + viewer); - viewer.initClient(param); - } else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) { - s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: " + viewer.getClientHostPassword() - + ", sid in request: " + param.getClientHostPassword()); - viewer.initClient(param); - } - } - - if(reportLoadChange) { - ConsoleProxyClientStatsCollector statsCollector = getStatsCollector(); - String loadInfo = statsCollector.getStatsReport(); - reportLoadInfo(loadInfo); - if(s_logger.isDebugEnabled()) - s_logger.debug("Report load change : " + loadInfo); - } - - return viewer; - } - - public static ConsoleProxyClient getAjaxVncViewer(ConsoleProxyClientParam param, String ajaxSession) throws Exception { - - boolean reportLoadChange = false; - String clientKey = param.getClientMapKey(); - synchronized (connectionMap) { - ConsoleProxyClient viewer = connectionMap.get(clientKey); - if (viewer == null) { - viewer = new ConsoleProxyVncClient(); - viewer.initClient(param); - - connectionMap.put(clientKey, viewer); - s_logger.info("Added viewer object " + viewer); - reportLoadChange = true; - } else if (!viewer.isFrontEndAlive()) { - s_logger.info("The rfb thread died, reinitializing the viewer " + viewer); - viewer.initClient(param); - } else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) { - s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: " - + viewer.getClientHostPassword() + ", sid in request: " + param.getClientHostPassword()); - viewer.initClient(param); - } else { - if(ajaxSession == null || ajaxSession.isEmpty()) - authenticationExternally(param); - } - - if(reportLoadChange) { - ConsoleProxyClientStatsCollector statsCollector = getStatsCollector(); - String loadInfo = statsCollector.getStatsReport(); - reportLoadInfo(loadInfo); - if(s_logger.isDebugEnabled()) - s_logger.debug("Report load change : " + loadInfo); - } - return viewer; - } - } - - public static void removeViewer(ConsoleProxyClient viewer) { - synchronized (connectionMap) { - for(Map.Entry entry : connectionMap.entrySet()) { - if(entry.getValue() == viewer) { - connectionMap.remove(entry.getKey()); - return; - } - } - } - } - - public static ConsoleProxyClientStatsCollector getStatsCollector() { - return new ConsoleProxyClientStatsCollector(connectionMap); - } - - public static void authenticationExternally(ConsoleProxyClientParam param) throws AuthenticationException { - ConsoleProxyAuthenticationResult authResult = authenticateConsoleAccess(param, false); - - if(authResult == null || !authResult.isSuccess()) { - s_logger.warn("External authenticator failed authencation request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword()); - - throw new AuthenticationException("External authenticator failed request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword()); - } - } - - public static ConsoleProxyAuthenticationResult reAuthenticationExternally(ConsoleProxyClientParam param) { - return authenticateConsoleAccess(param, true); - } - - public static String getEncryptorPassword() { - return encryptorPassword; - } - - public static void setEncryptorPassword(String password) { - encryptorPassword = password; - } - - static class ThreadExecutor implements Executor { - public void execute(Runnable r) { - new Thread(r).start(); - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java deleted file mode 100644 index 6cadeca1f4a..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java +++ /dev/null @@ -1,406 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyAjaxHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class); - - public ConsoleProxyAjaxHandler() { - } - - public void handle(HttpExchange t) throws IOException { - try { - if(s_logger.isTraceEnabled()) - s_logger.trace("AjaxHandler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - - doHandle(t); - - if(s_logger.isTraceEnabled()) - s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IOException e) { - throw e; - } catch (IllegalArgumentException e) { - s_logger.warn("Exception, ", e); - t.sendResponseHeaders(400, -1); // bad request - } catch(Throwable e) { - s_logger.error("Unexpected exception, ", e); - t.sendResponseHeaders(500, -1); // server error - } finally { - t.close(); - } - } - - private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { - String queries = t.getRequestURI().getQuery(); - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle AJAX request: " + queries); - - Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); - - String host = queryMap.get("host"); - String portStr = queryMap.get("port"); - String sid = queryMap.get("sid"); - String tag = queryMap.get("tag"); - String ticket = queryMap.get("ticket"); - String ajaxSessionIdStr = queryMap.get("sess"); - String eventStr = queryMap.get("event"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); - - if(tag == null) - tag = ""; - - long ajaxSessionId = 0; - int event = 0; - - int port; - - if(host == null || portStr == null || sid == null) - throw new IllegalArgumentException(); - - try { - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + portStr); - throw new IllegalArgumentException(e); - } - - if(ajaxSessionIdStr != null) { - try { - ajaxSessionId = Long.parseLong(ajaxSessionIdStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr); - throw new IllegalArgumentException(e); - } - } - - if(eventStr != null) { - try { - event = Integer.parseInt(eventStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + eventStr); - throw new IllegalArgumentException(e); - } - } - - ConsoleProxyClient viewer = null; - try { - ConsoleProxyClientParam param = new ConsoleProxyClientParam(); - param.setClientHostAddress(host); - param.setClientHostPort(port); - param.setClientHostPassword(sid); - param.setClientTag(tag); - param.setTicket(ticket); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); - - viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr); - } catch(Exception e) { - - s_logger.warn("Failed to create viewer due to " + e.getMessage(), e); - - String[] content = new String[] { - "", - "
", - "

Access is denied for the console session. Please close the window and retry again

", - "
" - }; - - StringBuffer sb = new StringBuffer(); - for(int i = 0; i < content.length; i++) - sb.append(content[i]); - - sendResponse(t, "text/html", sb.toString()); - return; - } - - if(event != 0) { - if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) { - if(event == 7) { - // client send over an event bag - InputStream is = t.getRequestBody(); - handleClientEventBag(viewer, convertStreamToString(is, true)); - } else { - handleClientEvent(viewer, event, queryMap); - } - sendResponse(t, "text/html", "OK"); - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); - - sendResponse(t, "text/html", "Invalid ajax client session id"); - } - } else { - if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) { - s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); - handleClientKickoff(t, viewer); - } else if(ajaxSessionId == 0) { - if(s_logger.isDebugEnabled()) - s_logger.debug("Ajax request indicates a fresh client start"); - - String title = queryMap.get("t"); - String guest = queryMap.get("guest"); - handleClientStart(t, viewer, title != null ? title : "", guest); - } else { - - if(s_logger.isTraceEnabled()) - s_logger.trace("Ajax request indicates client update"); - - handleClientUpdate(t, viewer); - } - } - } - - private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) { - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line = null; - try { - while ((line = reader.readLine()) != null) { - sb.append(line + "\n"); - } - } catch (IOException e) { - s_logger.warn("Exception while reading request body: ", e); - } finally { - if(closeStreamAfterRead) { - try { - is.close(); - } catch (IOException e) { - } - } - } - return sb.toString(); - } - - private void sendResponse(HttpExchange t, String contentType, String response) throws IOException { - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", contentType); - - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } - - @SuppressWarnings("deprecation") - private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) { - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle event bag, event bag: " + requestData); - - int start = requestData.indexOf("="); - if(start < 0) - start = 0; - else if(start > 0) - start++; - String data = URLDecoder.decode(requestData.substring(start)); - String[] tokens = data.split("\\|"); - if(tokens != null && tokens.length > 0) { - int count = 0; - try { - count = Integer.parseInt(tokens[0]); - int parsePos = 1; - int type, event, x, y, code, modifiers; - for(int i = 0; i < count; i++) { - type = Integer.parseInt(tokens[parsePos++]); - if(type == 1) { - // mouse event - event = Integer.parseInt(tokens[parsePos++]); - x = Integer.parseInt(tokens[parsePos++]); - y = Integer.parseInt(tokens[parsePos++]); - code = Integer.parseInt(tokens[parsePos++]); - modifiers = Integer.parseInt(tokens[parsePos++]); - - Map queryMap = new HashMap(); - queryMap.put("event", String.valueOf(event)); - queryMap.put("x", String.valueOf(x)); - queryMap.put("y", String.valueOf(y)); - queryMap.put("code", String.valueOf(code)); - queryMap.put("modifier", String.valueOf(modifiers)); - handleClientEvent(viewer, event, queryMap); - } else { - // keyboard event - event = Integer.parseInt(tokens[parsePos++]); - code = Integer.parseInt(tokens[parsePos++]); - modifiers = Integer.parseInt(tokens[parsePos++]); - - Map queryMap = new HashMap(); - queryMap.put("event", String.valueOf(event)); - queryMap.put("code", String.valueOf(code)); - queryMap.put("modifier", String.valueOf(modifiers)); - handleClientEvent(viewer, event, queryMap); - } - } - } catch(NumberFormatException e) { - s_logger.warn("Exception in handle client event bag: " + data + ", ", e); - } catch(Exception e) { - s_logger.warn("Exception in handle client event bag: " + data + ", ", e); - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } - } - } - - private void handleClientEvent(ConsoleProxyClient viewer, int event, Map queryMap) { - int code = 0; - int x = 0, y = 0; - int modifiers = 0; - - String str; - switch(event) { - case 1: // mouse move - case 2: // mouse down - case 3: // mouse up - case 8: // mouse double click - str = queryMap.get("x"); - if(str != null) { - try { - x = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - } - str = queryMap.get("y"); - if(str != null) { - try { - y = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - } - - if(event != 1) { - str = queryMap.get("code"); - try { - code = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - str = queryMap.get("modifier"); - try { - modifiers = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers); - } else { - if(s_logger.isTraceEnabled()) - s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y); - } - viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers); - break; - - case 4: // key press - case 5: // key down - case 6: // key up - str = queryMap.get("code"); - try { - code = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - str = queryMap.get("modifier"); - try { - modifiers = Integer.parseInt(str); - } catch (NumberFormatException e) { - s_logger.warn("Invalid number parameter in query string: " + str); - throw new IllegalArgumentException(e); - } - - if(s_logger.isDebugEnabled()) - s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers); - viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers); - break; - - default : - break; - } - } - - private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException { - String response = viewer.onAjaxClientKickoff(); - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } - - private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException { - List languages = t.getRequestHeaders().get("Accept-Language"); - String response = viewer.onAjaxClientStart(title, languages, guest); - - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "text/html"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, response.length()); - - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } - - private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException { - String response = viewer.onAjaxClientUpdate(); - - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "text/javascript"); - t.sendResponseHeaders(200, response.length()); - - OutputStream os = t.getResponseBody(); - try { - os.write(response.getBytes()); - } finally { - os.close(); - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java deleted file mode 100644 index 5e1014963f3..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java +++ /dev/null @@ -1,159 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyAjaxImageHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class); - - public void handle(HttpExchange t) throws IOException { - try { - if(s_logger.isDebugEnabled()) - s_logger.debug("AjaxImageHandler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - - doHandle(t); - - if(s_logger.isDebugEnabled()) - s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IOException e) { - throw e; - } catch (IllegalArgumentException e) { - s_logger.warn("Exception, ", e); - t.sendResponseHeaders(400, -1); // bad request - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch(Throwable e) { - s_logger.error("Unexpected exception, ", e); - t.sendResponseHeaders(500, -1); // server error - } finally { - t.close(); - } - } - - private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { - String queries = t.getRequestURI().getQuery(); - Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); - - String host = queryMap.get("host"); - String portStr = queryMap.get("port"); - String sid = queryMap.get("sid"); - String tag = queryMap.get("tag"); - String ticket = queryMap.get("ticket"); - String keyStr = queryMap.get("key"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); - String w = queryMap.get("w"); - String h = queryMap.get("h"); - - int key = 0; - int width = 144; - int height = 110; - - if(tag == null) - tag = ""; - - int port; - if(host == null || portStr == null || sid == null) - throw new IllegalArgumentException(); - - try { - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - s_logger.warn("Invalid numeric parameter in query string: " + portStr); - throw new IllegalArgumentException(e); - } - - try { - if (keyStr != null) - key = Integer.parseInt(keyStr); - if(null != w) - width = Integer.parseInt(w); - - if(null != h) - height = Integer.parseInt(h); - - } catch (NumberFormatException e) { - s_logger.warn("Invalid numeric parameter in query string: " + keyStr); - throw new IllegalArgumentException(e); - } - - ConsoleProxyClientParam param = new ConsoleProxyClientParam(); - param.setClientHostAddress(host); - param.setClientHostPort(port); - param.setClientHostPassword(sid); - param.setClientTag(tag); - param.setTicket(ticket); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); - - ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); - - if (key == 0) { - Image scaledImage = viewer.getClientScaledImage(width, height); - BufferedImage bufferedImage = new BufferedImage(width, height, - BufferedImage.TYPE_3BYTE_BGR); - Graphics2D bufImageGraphics = bufferedImage.createGraphics(); - bufImageGraphics.drawImage(scaledImage, 0, 0, null); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - } else { - AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache(); - byte[] img = imageCache.getImage(key); - - if(img != null) { - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - t.sendResponseHeaders(200, img.length); - - OutputStream os = t.getResponseBody(); - try { - os.write(img, 0, img.length); - } finally { - os.close(); - } - } else { - if(s_logger.isInfoEnabled()) - s_logger.info("Image has already been swept out, key: " + key); - t.sendResponseHeaders(404, -1); - } - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java deleted file mode 100644 index 26ee9b33155..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -// duplicated class -public class ConsoleProxyAuthenticationResult { - private boolean success; - private boolean isReauthentication; - private String host; - private int port; - private String tunnelUrl; - private String tunnelSession; - - public ConsoleProxyAuthenticationResult() { - success = false; - isReauthentication = false; - port = 0; - } - - public boolean isSuccess() { - return success; - } - - public void setSuccess(boolean success) { - this.success = success; - } - - public boolean isReauthentication() { - return isReauthentication; - } - - public void setReauthentication(boolean isReauthentication) { - this.isReauthentication = isReauthentication; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getTunnelUrl() { - return tunnelUrl; - } - - public void setTunnelUrl(String tunnelUrl) { - this.tunnelUrl = tunnelUrl; - } - - public String getTunnelSession() { - return tunnelSession; - } - - public void setTunnelSession(String tunnelSession) { - this.tunnelSession = tunnelSession; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java deleted file mode 100644 index c9ad8ab82fa..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.IOException; -import java.net.InetSocketAddress; - -import javax.net.ssl.SSLServerSocket; - -import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.HttpServer; - -public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class); - - @Override - public void init(byte[] ksBits, String ksPassword) { - } - - @Override - public HttpServer createHttpServerInstance(int port) throws IOException { - if(s_logger.isInfoEnabled()) - s_logger.info("create HTTP server instance at port: " + port); - return HttpServer.create(new InetSocketAddress(port), 5); - } - - @Override - public SSLServerSocket createSSLServerSocket(int port) throws IOException { - if(s_logger.isInfoEnabled()) - s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl"); - - return null; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClient.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClient.java deleted file mode 100644 index 8a0be051903..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClient.java +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.awt.Image; -import java.util.List; - -/** - * ConsoleProxyClient defines an standard interface that a console client should implement, - * - * ConsoleProxyClient maintains a session towards the target host, it glues the session - * to a AJAX front-end viewer - */ -public interface ConsoleProxyClient { - int getClientId(); - - // - // Quick status - // - boolean isHostConnected(); - boolean isFrontEndAlive(); - - // - // AJAX viewer - // - long getAjaxSessionId(); - AjaxFIFOImageCache getAjaxImageCache(); - Image getClientScaledImage(int width, int height); // client thumbnail support - - String onAjaxClientStart(String title, List languages, String guest); - String onAjaxClientUpdate(); - String onAjaxClientKickoff(); - - // - // Input handling - // - void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers); - void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers); - - // - // Info/Stats - // - long getClientCreateTime(); - long getClientLastFrontEndActivityTime(); - String getClientHostAddress(); - int getClientHostPort(); - String getClientHostPassword(); - String getClientTag(); - - // - // Setup/house-keeping - // - void initClient(ConsoleProxyClientParam param); - void closeClient(); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java deleted file mode 100644 index 289bdab2f8a..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java +++ /dev/null @@ -1,457 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.awt.Image; -import java.awt.Rectangle; -import java.util.List; - -import org.apache.log4j.Logger; - -import com.cloud.consoleproxy.util.TileInfo; -import com.cloud.consoleproxy.util.TileTracker; -import com.cloud.consoleproxy.vnc.FrameBufferCanvas; - -/** - * - * an instance of specialized console protocol implementation, such as VNC or RDP - * - * It mainly implements the features needed by front-end AJAX viewer - * - */ -public abstract class ConsoleProxyClientBase implements ConsoleProxyClient, ConsoleProxyClientListener { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyClientBase.class); - - private static int s_nextClientId = 0; - protected int clientId = getNextClientId(); - - protected long ajaxSessionId = 0; - - protected boolean dirtyFlag = false; - protected Object tileDirtyEvent = new Object(); - protected TileTracker tracker; - protected AjaxFIFOImageCache ajaxImageCache = new AjaxFIFOImageCache(2); - - protected ConsoleProxyClientParam clientParam; - protected String clientToken; - - protected long createTime = System.currentTimeMillis(); - protected long lastFrontEndActivityTime = System.currentTimeMillis(); - - protected boolean framebufferResized = false; - protected int resizedFramebufferWidth; - protected int resizedFramebufferHeight; - - public ConsoleProxyClientBase() { - tracker = new TileTracker(); - tracker.initTracking(64, 64, 800, 600); - } - - // - // interface ConsoleProxyClient - // - @Override - public int getClientId() { - return clientId; - } - - public abstract boolean isHostConnected(); - public abstract boolean isFrontEndAlive(); - - @Override - public long getAjaxSessionId() { - return this.ajaxSessionId; - } - - @Override - public AjaxFIFOImageCache getAjaxImageCache() { - return ajaxImageCache; - } - - public Image getClientScaledImage(int width, int height) { - FrameBufferCanvas canvas = getFrameBufferCavas(); - if(canvas != null) - return canvas.getFrameBufferScaledImage(width, height); - - return null; - } - - public abstract void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers); - public abstract void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers); - - @Override - public long getClientCreateTime() { - return createTime; - } - - @Override - public long getClientLastFrontEndActivityTime() { - return lastFrontEndActivityTime; - } - - @Override - public String getClientHostAddress() { - return clientParam.getClientHostAddress(); - } - - @Override - public int getClientHostPort() { - return clientParam.getClientHostPort(); - } - - @Override - public String getClientHostPassword() { - return clientParam.getClientHostPassword(); - } - - @Override - public String getClientTag() { - if(clientParam.getClientTag() != null) - return clientParam.getClientTag(); - return ""; - } - - @Override - public abstract void initClient(ConsoleProxyClientParam param); - - @Override - public abstract void closeClient(); - - // - // interface FrameBufferEventListener - // - @Override - public void onFramebufferSizeChange(int w, int h) { - tracker.resize(w, h); - - synchronized(this) { - framebufferResized = true; - resizedFramebufferWidth = w; - resizedFramebufferHeight = h; - } - - signalTileDirtyEvent(); - } - - @Override - public void onFramebufferUpdate(int x, int y, int w, int h) { - if(s_logger.isTraceEnabled()) - s_logger.trace("Frame buffer update {" + x + "," + y + "," + w + "," + h + "}"); - tracker.invalidate(new Rectangle(x, y, w, h)); - - signalTileDirtyEvent(); - } - - // - // AJAX Image manipulation - // - public byte[] getFrameBufferJpeg() { - FrameBufferCanvas canvas = getFrameBufferCavas(); - if(canvas != null) - return canvas.getFrameBufferJpeg(); - - return null; - } - - public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight) { - FrameBufferCanvas canvas = getFrameBufferCavas(); - if(canvas != null) - return canvas.getTilesMergedJpeg(tileList, tileWidth, tileHeight); - return null; - } - - private String prepareAjaxImage(List tiles, boolean init) { - byte[] imgBits; - if(init) - imgBits = getFrameBufferJpeg(); - else - imgBits = getTilesMergedJpeg(tiles, tracker.getTileWidth(), tracker.getTileHeight()); - - if(imgBits == null) { - s_logger.warn("Unable to generate jpeg image"); - } else { - if(s_logger.isTraceEnabled()) - s_logger.trace("Generated jpeg image size: " + imgBits.length); - } - - int key = ajaxImageCache.putImage(imgBits); - StringBuffer sb = new StringBuffer(); - sb.append("/ajaximg?token=").append(clientToken); - sb.append("&key=").append(key); - sb.append("&ts=").append(System.currentTimeMillis()); - - return sb.toString(); - } - - private String prepareAjaxSession(boolean init) { - if(init) { - synchronized(this) { - ajaxSessionId++; - } - } - - StringBuffer sb = new StringBuffer(); - sb.append("/ajax?token=").append(clientToken).append("&sess=").append(ajaxSessionId); - return sb.toString(); - } - - @Override - public String onAjaxClientKickoff() { - return "onKickoff();"; - } - - private boolean waitForViewerReady() { - long startTick = System.currentTimeMillis(); - while(System.currentTimeMillis() - startTick < 5000) { - if(getFrameBufferCavas() != null) - return true; - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - } - } - return false; - } - - private String onAjaxClientConnectFailed() { - return "

" + - "Unable to start console session as connection is refused by the machine you are accessing" + - "

"; - } - - @Override - public String onAjaxClientStart(String title, List languages, String guest) { - updateFrontEndActivityTime(); - - if(!waitForViewerReady()) - return onAjaxClientConnectFailed(); - - synchronized(this) { - ajaxSessionId++; - framebufferResized = false; - } - - int tileWidth = tracker.getTileWidth(); - int tileHeight = tracker.getTileHeight(); - int width = tracker.getTrackWidth(); - int height = tracker.getTrackHeight(); - - if(s_logger.isTraceEnabled()) - s_logger.trace("Ajax client start, frame buffer w: " + width + ", " + height); - - int retry = 0; - tracker.initCoverageTest(); - while(!tracker.hasFullCoverage() && retry < 10) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - retry++; - } - - List tiles = tracker.scan(true); - String imgUrl = prepareAjaxImage(tiles, true); - String updateUrl = prepareAjaxSession(true); - - StringBuffer sbTileSequence = new StringBuffer(); - int i = 0; - for(TileInfo tile : tiles) { - sbTileSequence.append("[").append(tile.getRow()).append(",").append(tile.getCol()).append("]"); - if(i < tiles.size() - 1) - sbTileSequence.append(","); - - i++; - } - - return getAjaxViewerPageContent(sbTileSequence.toString(), imgUrl, - updateUrl, width, height, tileWidth, tileHeight, title, - ConsoleProxy.keyboardType == ConsoleProxy.KEYBOARD_RAW, - languages, guest); - } - - private String getAjaxViewerPageContent(String tileSequence, String imgUrl, String updateUrl, int width, - int height, int tileWidth, int tileHeight, String title, boolean rawKeyboard, List languages, String guest) { - - StringBuffer sbLanguages = new StringBuffer(""); - if(languages != null) { - for(String lang : languages) { - if(sbLanguages.length() > 0) { - sbLanguages.append(","); - } - sbLanguages.append(lang); - } - } - - String[] content = new String[] { - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" + title + "", - "", - "", - "
", - "", - "", - "
", - "
", - "", - "", - "" - }; - - StringBuffer sb = new StringBuffer(); - for(int i = 0; i < content.length; i++) - sb.append(content[i]); - - return sb.toString(); - } - - public String onAjaxClientDisconnected() { - return "onDisconnect();"; - } - - @Override - public String onAjaxClientUpdate() { - updateFrontEndActivityTime(); - if(!waitForViewerReady()) - return onAjaxClientDisconnected(); - - synchronized(tileDirtyEvent) { - if(!dirtyFlag) { - try { - tileDirtyEvent.wait(3000); - } catch(InterruptedException e) { - } - } - } - - boolean doResize = false; - synchronized(this) { - if(framebufferResized) { - framebufferResized = false; - doResize = true; - } - } - - List tiles; - - if(doResize) - tiles = tracker.scan(true); - else - tiles = tracker.scan(false); - dirtyFlag = false; - - String imgUrl = prepareAjaxImage(tiles, false); - StringBuffer sbTileSequence = new StringBuffer(); - int i = 0; - for(TileInfo tile : tiles) { - sbTileSequence.append("[").append(tile.getRow()).append(",").append(tile.getCol()).append("]"); - if(i < tiles.size() - 1) - sbTileSequence.append(","); - - i++; - } - - return getAjaxViewerUpdatePageContent(sbTileSequence.toString(), imgUrl, doResize, - resizedFramebufferWidth, resizedFramebufferHeight, - tracker.getTileWidth(), tracker.getTileHeight()); - } - - private String getAjaxViewerUpdatePageContent(String tileSequence, String imgUrl, boolean resized, int width, - int height, int tileWidth, int tileHeight) { - - String[] content = new String[] { - "tileMap = [ " + tileSequence + " ];", - resized ? "ajaxViewer.resize('main_panel', " + width + ", " + height + " , " + tileWidth + ", " + tileHeight + ");" : "", - "ajaxViewer.refresh('" + imgUrl + "', tileMap, false);" - }; - - StringBuffer sb = new StringBuffer(); - for(int i = 0; i < content.length; i++) - sb.append(content[i]); - - return sb.toString(); - } - - // - // Helpers - // - private synchronized static int getNextClientId() { - return ++s_nextClientId; - } - - private void signalTileDirtyEvent() { - synchronized(tileDirtyEvent) { - dirtyFlag = true; - tileDirtyEvent.notifyAll(); - } - } - - public void updateFrontEndActivityTime() { - lastFrontEndActivityTime = System.currentTimeMillis(); - } - - protected abstract FrameBufferCanvas getFrameBufferCavas(); - - public ConsoleProxyClientParam getClientParam() { - return clientParam; - } - - public void setClientParam(ConsoleProxyClientParam clientParam) { - this.clientParam = clientParam; - ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(ConsoleProxy.getEncryptorPassword()); - this.clientToken = encryptor.encryptObject(ConsoleProxyClientParam.class, clientParam); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java deleted file mode 100644 index 43a0bab8bed..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -public interface ConsoleProxyClientListener { - void onFramebufferSizeChange(int w, int h); - void onFramebufferUpdate(int x, int y, int w, int h); - - void onClientConnected(); - void onClientClose(); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java deleted file mode 100644 index 8de4955d4b7..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -/** - * - * Data object to store parameter info needed by client to connect to its host - */ -public class ConsoleProxyClientParam { - - private String clientHostAddress; - private int clientHostPort; - private String clientHostPassword; - private String clientTag; - private String ticket; - - private String clientTunnelUrl; - private String clientTunnelSession; - - private String ajaxSessionId; - - public ConsoleProxyClientParam() { - clientHostPort = 0; - } - - public String getClientHostAddress() { - return clientHostAddress; - } - - public void setClientHostAddress(String clientHostAddress) { - this.clientHostAddress = clientHostAddress; - } - - public int getClientHostPort() { - return clientHostPort; - } - - public void setClientHostPort(int clientHostPort) { - this.clientHostPort = clientHostPort; - } - - public String getClientHostPassword() { - return clientHostPassword; - } - - public void setClientHostPassword(String clientHostPassword) { - this.clientHostPassword = clientHostPassword; - } - - public String getClientTag() { - return clientTag; - } - - public void setClientTag(String clientTag) { - this.clientTag = clientTag; - } - - public String getTicket() { - return ticket; - } - - public void setTicket(String ticket) { - this.ticket = ticket; - } - - public String getClientTunnelUrl() { - return clientTunnelUrl; - } - - public void setClientTunnelUrl(String clientTunnelUrl) { - this.clientTunnelUrl = clientTunnelUrl; - } - - public String getClientTunnelSession() { - return clientTunnelSession; - } - - public void setClientTunnelSession(String clientTunnelSession) { - this.clientTunnelSession = clientTunnelSession; - } - - public String getAjaxSessionId() { - return this.ajaxSessionId; - } - - public void setAjaxSessionId(String ajaxSessionId) { - this.ajaxSessionId = ajaxSessionId; - } - - public String getClientMapKey() { - if(clientTag != null && !clientTag.isEmpty()) - return clientTag; - - return clientHostAddress + ":" + clientHostPort; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java deleted file mode 100644 index 15cf451ca2c..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java +++ /dev/null @@ -1,88 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Hashtable; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -/** - * - * ConsoleProxyClientStatsCollector collects client stats for console proxy agent to report - */ -public class ConsoleProxyClientStatsCollector { - - ArrayList connections; - - public ConsoleProxyClientStatsCollector() { - } - - public ConsoleProxyClientStatsCollector(Hashtable connMap) { - setConnections(connMap); - } - - public String getStatsReport() { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - return gson.toJson(this); - } - - public void getStatsReport(OutputStreamWriter os) { - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - gson.toJson(this, os); - } - - private void setConnections(Hashtable connMap) { - - ArrayList conns = new ArrayList(); - Enumeration e = connMap.keys(); - while (e.hasMoreElements()) { - synchronized (connMap) { - String key = e.nextElement(); - ConsoleProxyClient client = connMap.get(key); - - ConsoleProxyConnection conn = new ConsoleProxyConnection(); - - conn.id = client.getClientId(); - conn.clientInfo = ""; - conn.host = client.getClientHostAddress(); - conn.port = client.getClientHostPort(); - conn.tag = client.getClientTag(); - conn.createTime = client.getClientCreateTime(); - conn.lastUsedTime = client.getClientLastFrontEndActivityTime(); - conns.add(conn); - } - } - connections = conns; - } - - public static class ConsoleProxyConnection { - public int id; - public String clientInfo; - public String host; - public int port; - public String tag; - public long createTime; - public long lastUsedTime; - - public ConsoleProxyConnection() { - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java deleted file mode 100644 index 408eb0419d7..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; - -import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyCmdHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyCmdHandler.class); - - public void handle(HttpExchange t) throws IOException { - try { - Thread.currentThread().setName("Cmd Thread " + - Thread.currentThread().getId() + " " + t.getRemoteAddress()); - s_logger.info("CmdHandler " + t.getRequestURI()); - doHandle(t); - } catch (Exception e) { - s_logger.error(e.toString(), e); - String response = "Not found"; - t.sendResponseHeaders(404, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch (Throwable e) { - s_logger.error(e.toString(), e); - } finally { - t.close(); - } - } - - public void doHandle(HttpExchange t) throws Exception { - String path = t.getRequestURI().getPath(); - int i = path.indexOf("/", 1); - String cmd = path.substring(i + 1); - s_logger.info("Get CMD request for " + cmd); - if (cmd.equals("getstatus")) { - ConsoleProxyClientStatsCollector statsCollector = ConsoleProxy.getStatsCollector(); - - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "text/plain"); - t.sendResponseHeaders(200, 0); - OutputStreamWriter os = new OutputStreamWriter(t.getResponseBody()); - statsCollector.getStatsReport(os); - os.close(); - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java deleted file mode 100644 index 7f82a965f69..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.File; -import java.util.Enumeration; -import java.util.Hashtable; - -import org.apache.log4j.Logger; - -/** - * - * ConsoleProxyGCThread does house-keeping work for the process, it helps cleanup log files, - * recycle idle client sessions without front-end activities and report client stats to external - * management software - */ -public class ConsoleProxyGCThread extends Thread { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyGCThread.class); - - private final static int MAX_SESSION_IDLE_SECONDS = 180; - - private Hashtable connMap; - private long lastLogScan = 0; - - public ConsoleProxyGCThread(Hashtable connMap) { - this.connMap = connMap; - } - - private void cleanupLogging() { - if(lastLogScan != 0 && System.currentTimeMillis() - lastLogScan < 3600000) - return; - - lastLogScan = System.currentTimeMillis(); - - File logDir = new File("./logs"); - File files[] = logDir.listFiles(); - if(files != null) { - for(File file : files) { - if(System.currentTimeMillis() - file.lastModified() >= 86400000L) { - try { - file.delete(); - } catch(Throwable e) { - } - } - } - } - } - - @Override - public void run() { - - boolean bReportLoad = false; - while (true) { - cleanupLogging(); - bReportLoad = false; - - if(s_logger.isDebugEnabled()) - s_logger.debug("connMap=" + connMap); - Enumeration e = connMap.keys(); - while (e.hasMoreElements()) { - String key; - ConsoleProxyClient client; - - synchronized (connMap) { - key = e.nextElement(); - client = connMap.get(key); - } - - long seconds_unused = (System.currentTimeMillis() - client.getClientLastFrontEndActivityTime()) / 1000; - if (seconds_unused < MAX_SESSION_IDLE_SECONDS) { - continue; - } - - synchronized (connMap) { - connMap.remove(key); - bReportLoad = true; - } - - // close the server connection - s_logger.info("Dropping " + client + " which has not been used for " + seconds_unused + " seconds"); - client.closeClient(); - } - - if(bReportLoad) { - // report load changes - String loadInfo = new ConsoleProxyClientStatsCollector(connMap).getStatsReport(); - ConsoleProxy.reportLoadInfo(loadInfo); - if(s_logger.isDebugEnabled()) - s_logger.debug("Report load change : " + loadInfo); - } - - try { Thread.sleep(5000); } catch (InterruptedException ex) {} - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java deleted file mode 100644 index 7756d01cd7f..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.util.HashMap; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; - -public class ConsoleProxyHttpHandlerHelper { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class); - - public static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap(); - for (String param : params) { - String[] paramTokens = param.split("="); - if(paramTokens != null && paramTokens.length == 2) { - String name = param.split("=")[0]; - String value = param.split("=")[1]; - map.put(name, value); - } else if (paramTokens.length == 3) { - // very ugly, added for Xen tunneling url - String name = paramTokens[0]; - String value = paramTokens[1] + "=" + paramTokens[2]; - map.put(name, value); - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("Invalid paramemter in URL found. param: " + param); - } - } - - // This is a ugly solution for now. We will do encryption/decryption translation - // here to make it transparent to rest of the code. - if(map.get("token") != null) { - ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor( - ConsoleProxy.getEncryptorPassword()); - - ConsoleProxyClientParam param = encryptor.decryptObject(ConsoleProxyClientParam.class, map.get("token")); - if(param != null) { - if(param.getClientHostAddress() != null) - map.put("host", param.getClientHostAddress()); - if(param.getClientHostPort() != 0) - map.put("port", String.valueOf(param.getClientHostPort())); - if(param.getClientTag() != null) - map.put("tag", param.getClientTag()); - if(param.getClientHostPassword() != null) - map.put("sid", param.getClientHostPassword()); - if(param.getClientTunnelUrl() != null) - map.put("consoleurl", param.getClientTunnelUrl()); - if(param.getClientTunnelSession() != null) - map.put("sessionref", param.getClientTunnelSession()); - if(param.getTicket() != null) - map.put("ticket", param.getTicket()); - } - } - - return map; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java deleted file mode 100644 index ff66de3bcc4..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import com.cloud.consoleproxy.util.Logger; -import com.cloud.consoleproxy.util.LoggerFactory; - -public class ConsoleProxyLoggerFactory implements LoggerFactory { - public ConsoleProxyLoggerFactory() { - } - - public Logger getLogger(Class clazz) { - return new Log4jLogger(org.apache.log4j.Logger.getLogger(clazz)); - } - - public static class Log4jLogger extends Logger { - private org.apache.log4j.Logger logger; - - public Log4jLogger(org.apache.log4j.Logger logger) { - this.logger = logger; - } - - public boolean isTraceEnabled() { - return logger.isTraceEnabled(); - } - - public boolean isDebugEnabled() { - return logger.isDebugEnabled(); - } - - public boolean isInfoEnabled() { - return logger.isInfoEnabled(); - } - - public void trace(Object message) { - logger.trace(message); - } - - public void trace(Object message, Throwable exception) { - logger.trace(message, exception); - } - - public void info(Object message) { - logger.info(message); - } - - public void info(Object message, Throwable exception) { - logger.info(message, exception); - } - - public void debug(Object message) { - logger.debug(message); - } - - public void debug(Object message, Throwable exception) { - logger.debug(message, exception); - } - - public void warn(Object message) { - logger.warn(message); - } - - public void warn(Object message, Throwable exception) { - logger.warn(message, exception); - } - - public void error(Object message) { - logger.error(message); - } - - public void error(Object message, Throwable exception) { - logger.error(message, exception); - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java deleted file mode 100644 index 030b2f452eb..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java +++ /dev/null @@ -1,153 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.xml.DOMConfigurator; - -import com.cloud.consoleproxy.util.Logger; - - -// -// -// I switched to a simpler solution to monitor only unrecoverable exceptions, under these cases, console proxy process will exit -// itself and the shell script will re-launch console proxy -// -public class ConsoleProxyMonitor { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyMonitor.class); - - private String[] _argv; - private Map _argMap = new HashMap(); - - private volatile Process _process; - private boolean _quit = false; - - public ConsoleProxyMonitor(String[] argv) { - _argv = argv; - - for(String arg : _argv) { - String[] tokens = arg.split("="); - if(tokens.length == 2) { - s_logger.info("Add argument " + tokens[0] + "=" + tokens[1] + " to the argument map"); - - _argMap.put(tokens[0].trim(), tokens[1].trim()); - } else { - s_logger.warn("unrecognized argument, skip adding it to argument map"); - } - } - } - - private void run() { - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - _quit = true; - onShutdown(); - } - }); - - while(!_quit) { - String cmdLine = getLaunchCommandLine(); - - s_logger.info("Launch console proxy process with command line: " + cmdLine); - - try { - _process = Runtime.getRuntime().exec(cmdLine); - } catch (IOException e) { - s_logger.error("Unexpected exception ", e); - System.exit(1); - } - - boolean waitSucceeded = false; - int exitCode = 0; - while(!waitSucceeded) { - try { - exitCode = _process.waitFor(); - waitSucceeded = true; - - if(s_logger.isInfoEnabled()) - s_logger.info("Console proxy process exits with code: " + exitCode); - } catch (InterruptedException e) { - if(s_logger.isInfoEnabled()) - s_logger.info("InterruptedException while waiting for termination of console proxy, will retry"); - } - } - } - } - - private String getLaunchCommandLine() { - StringBuffer sb = new StringBuffer("java "); - String jvmOptions = _argMap.get("jvmoptions"); - - if(jvmOptions != null) - sb.append(jvmOptions); - - for(Map.Entry entry : _argMap.entrySet()) { - if(!"jvmoptions".equalsIgnoreCase(entry.getKey())) - sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()); - } - - return sb.toString(); - } - - private void onShutdown() { - if(_process != null) { - if(s_logger.isInfoEnabled()) - s_logger.info("Console proxy monitor shuts dwon, terminate console proxy process"); - _process.destroy(); - } - } - - private static void configLog4j() { - URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); - if(configUrl == null) - configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); - - if(configUrl == null) - configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); - - if(configUrl != null) { - try { - System.out.println("Configure log4j using " + configUrl.toURI().toString()); - } catch (URISyntaxException e1) { - e1.printStackTrace(); - } - - try { - File file = new File(configUrl.toURI()); - - System.out.println("Log4j configuration from : " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); - } catch (URISyntaxException e) { - System.out.println("Unable to convert log4j configuration Url to URI"); - } - } else { - System.out.println("Configure log4j with default properties"); - } - } - - public static void main(String[] argv) { - configLog4j(); - (new ConsoleProxyMonitor(argv)).run(); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java deleted file mode 100644 index 29826f0ea92..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java +++ /dev/null @@ -1,142 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.codec.binary.Base64; -import org.apache.log4j.Logger; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -/** - * - * A simple password based encyrptor based on DES. It can serialize simple POJO object into URL safe string - * and deserialize it back. - * - */ -public class ConsoleProxyPasswordBasedEncryptor { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class); - - private String password; - private Gson gson; - - public ConsoleProxyPasswordBasedEncryptor(String password) { - this.password = password; - gson = new GsonBuilder().create(); - } - - public String encryptText(String text) { - if(text == null || text.isEmpty()) - return text; - - assert(password != null); - assert(!password.isEmpty()); - - try { - Cipher cipher = Cipher.getInstance("DES"); - int maxKeySize = 8; - SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES"); - cipher.init(Cipher.ENCRYPT_MODE, keySpec); - byte[] encryptedBytes = cipher.doFinal(text.getBytes()); - return Base64.encodeBase64URLSafeString(encryptedBytes); - } catch (NoSuchAlgorithmException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (NoSuchPaddingException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (IllegalBlockSizeException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (BadPaddingException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (InvalidKeyException e) { - s_logger.error("Unexpected exception ", e); - return null; - } - } - - public String decryptText(String encryptedText) { - if(encryptedText == null || encryptedText.isEmpty()) - return encryptedText; - - assert(password != null); - assert(!password.isEmpty()); - - try { - Cipher cipher = Cipher.getInstance("DES"); - int maxKeySize = 8; - SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES"); - cipher.init(Cipher.DECRYPT_MODE, keySpec); - - byte[] encryptedBytes = Base64.decodeBase64(encryptedText); - return new String(cipher.doFinal(encryptedBytes)); - } catch (NoSuchAlgorithmException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (NoSuchPaddingException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (IllegalBlockSizeException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (BadPaddingException e) { - s_logger.error("Unexpected exception ", e); - return null; - } catch (InvalidKeyException e) { - s_logger.error("Unexpected exception ", e); - return null; - } - } - - public String encryptObject(Class clz, T obj) { - if(obj == null) - return null; - - String json = gson.toJson(obj); - return encryptText(json); - } - - @SuppressWarnings("unchecked") - public T decryptObject(Class clz, String encrypted) { - if(encrypted == null || encrypted.isEmpty()) - return null; - - String json = decryptText(encrypted); - return (T)gson.fromJson(json, clz); - } - - private static byte[] normalizeKey(byte[] keyBytes, int keySize) { - assert(keySize > 0); - byte[] key = new byte[keySize]; - - for(int i = 0; i < keyBytes.length; i++) - key[i%keySize] ^= keyBytes[i]; - - return key; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java deleted file mode 100644 index 7d160472e8a..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java +++ /dev/null @@ -1,181 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyResourceHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyResourceHandler.class); - - static Map s_mimeTypes; - static { - s_mimeTypes = new HashMap(); - s_mimeTypes.put("jar", "application/java-archive"); - s_mimeTypes.put("js", "text/javascript"); - s_mimeTypes.put("css", "text/css"); - s_mimeTypes.put("jpg", "image/jpeg"); - s_mimeTypes.put("html", "text/html"); - s_mimeTypes.put("htm", "text/html"); - s_mimeTypes.put("log", "text/plain"); - } - - static Map s_validResourceFolders; - static { - s_validResourceFolders = new HashMap(); - s_validResourceFolders.put("applet", ""); - s_validResourceFolders.put("logs", ""); - s_validResourceFolders.put("images", ""); - s_validResourceFolders.put("js", ""); - s_validResourceFolders.put("css", ""); - s_validResourceFolders.put("html", ""); - } - - public ConsoleProxyResourceHandler() { - } - - public void handle(HttpExchange t) throws IOException { - try { - if(s_logger.isDebugEnabled()) - s_logger.debug("Resource Handler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - - doHandle(t); - - if(s_logger.isDebugEnabled()) - s_logger.debug(t.getRequestURI() + " Process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IOException e) { - throw e; - } catch(Throwable e) { - s_logger.error("Unexpected exception, ", e); - t.sendResponseHeaders(500, -1); // server error - } finally { - t.close(); - } - } - - @SuppressWarnings("deprecation") - private void doHandle(HttpExchange t) throws Exception { - String path = t.getRequestURI().getPath(); - - if(s_logger.isInfoEnabled()) - s_logger.info("Get resource request for " + path); - - int i = path.indexOf("/", 1); - String filepath = path.substring(i + 1); - i = path.lastIndexOf("."); - String extension = (i == -1) ? "" : path.substring(i + 1); - String contentType = getContentType(extension); - - if(!validatePath(filepath)) { - if(s_logger.isInfoEnabled()) - s_logger.info("Resource access is forbidden, uri: " + path); - - t.sendResponseHeaders(403, -1); // forbidden - return; - } - - File f = new File ("./" + filepath); - if(f.exists()) { - long lastModified = f.lastModified(); - String ifModifiedSince = t.getRequestHeaders().getFirst("If-Modified-Since"); - if (ifModifiedSince != null) { - long d = Date.parse(ifModifiedSince); - if (d + 1000 >= lastModified) { - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", contentType); - t.sendResponseHeaders(304, -1); - - if(s_logger.isInfoEnabled()) - s_logger.info("Sent 304 file has not been " + - "modified since " + ifModifiedSince); - return; - } - } - - long length = f.length(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", contentType); - hds.set("Last-Modified", new Date(lastModified).toGMTString()); - t.sendResponseHeaders(200, length); - responseFileContent(t, f); - - if(s_logger.isInfoEnabled()) - s_logger.info("Sent file " + path + " with content type " + contentType); - } else { - if(s_logger.isInfoEnabled()) - s_logger.info("file does not exist" + path); - t.sendResponseHeaders(404, -1); - } - } - - private static String getContentType(String extension) { - String key = extension.toLowerCase(); - if(s_mimeTypes.containsKey(key)) { - return s_mimeTypes.get(key); - } - return "application/octet-stream"; - } - - private static void responseFileContent(HttpExchange t, File f) throws Exception { - OutputStream os = t.getResponseBody(); - FileInputStream fis = new FileInputStream(f); - while (true) { - byte[] b = new byte[8192]; - int n = fis.read(b); - if (n < 0) { - break; - } - os.write(b, 0, n); - } - fis.close(); - os.close(); - } - - private static boolean validatePath(String path) { - int i = path.indexOf("/"); - if(i == -1) { - if(s_logger.isInfoEnabled()) - s_logger.info("Invalid resource path: can not start at resource root"); - return false; - } - - if(path.contains("..")) { - if(s_logger.isInfoEnabled()) - s_logger.info("Invalid resource path: contains relative up-level navigation"); - - return false; - } - - return isValidResourceFolder(path.substring(0, i)); - } - - private static boolean isValidResourceFolder(String name) { - return s_validResourceFolders.containsKey(name); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java deleted file mode 100644 index ee0ee13fa92..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java +++ /dev/null @@ -1,145 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.ByteArrayInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.security.KeyStore; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.TrustManagerFactory; - -import org.apache.log4j.Logger; - -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsParameters; -import com.sun.net.httpserver.HttpsServer; - -public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFactory { - private static final Logger s_logger = Logger.getLogger(ConsoleProxySecureServerFactoryImpl.class); - - private SSLContext sslContext = null; - - public ConsoleProxySecureServerFactoryImpl() { - } - - @Override - public void init(byte[] ksBits, String ksPassword) { - s_logger.info("Start initializing SSL"); - - if(ksBits == null) { - try { - s_logger.info("Initializing SSL from built-in default certificate"); - - char[] passphrase = "vmops.com".toCharArray(); - KeyStore ks = KeyStore.getInstance("JKS"); - - ks.load(new FileInputStream("certs/realhostip.keystore"), passphrase); - // ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase); - - s_logger.info("SSL certificate loaded"); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - s_logger.info("Key manager factory is initialized"); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ks); - s_logger.info("Trust manager factory is initialized"); - - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - s_logger.info("SSL context is initialized"); - } catch (Exception ioe) { - s_logger.error(ioe.toString(), ioe); - } - - } else { - char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null; - try { - s_logger.info("Initializing SSL from passed-in certificate"); - - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new ByteArrayInputStream(ksBits), passphrase); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - s_logger.info("Key manager factory is initialized"); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ks); - s_logger.info("Trust manager factory is initialized"); - - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - s_logger.info("SSL context is initialized"); - } catch(Exception e) { - s_logger.error("Unable to init factory due to exception ", e); - } - } - - } - - public HttpServer createHttpServerInstance(int port) throws IOException { - try { - HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5); - server.setHttpsConfigurator (new HttpsConfigurator(sslContext) { - @Override - public void configure (HttpsParameters params) { - - // get the remote address if needed - InetSocketAddress remote = params.getClientAddress(); - SSLContext c = getSSLContext(); - - // get the default parameters - SSLParameters sslparams = c.getDefaultSSLParameters(); - - params.setSSLParameters(sslparams); - // statement above could throw IAE if any params invalid. - // eg. if app has a UI and parameters supplied by a user. - } - }); - - s_logger.info("create HTTPS server instance on port: " + port); - return server; - } catch (Exception ioe) { - s_logger.error(ioe.toString(), ioe); - } - return null; - } - - public SSLServerSocket createSSLServerSocket(int port) throws IOException { - try { - SSLServerSocket srvSock = null; - SSLServerSocketFactory ssf = sslContext.getServerSocketFactory(); - srvSock = (SSLServerSocket) ssf.createServerSocket(port); - - s_logger.info("create SSL server socket on port: " + port); - return srvSock; - } catch (Exception ioe) { - s_logger.error(ioe.toString(), ioe); - } - return null; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java deleted file mode 100644 index 7e0e5c77bf4..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.IOException; - -import javax.net.ssl.SSLServerSocket; - -import com.sun.net.httpserver.HttpServer; - -public interface ConsoleProxyServerFactory { - void init(byte[] ksBits, String ksPassword); - HttpServer createHttpServerInstance(int port) throws IOException; - SSLServerSocket createSSLServerSocket(int port) throws IOException; -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java deleted file mode 100644 index 6d34d3b9162..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java +++ /dev/null @@ -1,212 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; - -import com.cloud.consoleproxy.util.Logger; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; - -public class ConsoleProxyThumbnailHandler implements HttpHandler { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyThumbnailHandler.class); - - public ConsoleProxyThumbnailHandler() { - } - - public void handle(HttpExchange t) throws IOException { - try { - Thread.currentThread().setName("JPG Thread " + - Thread.currentThread().getId() + " " + t.getRemoteAddress()); - - if(s_logger.isDebugEnabled()) - s_logger.debug("ScreenHandler " + t.getRequestURI()); - - long startTick = System.currentTimeMillis(); - doHandle(t); - - if(s_logger.isDebugEnabled()) - s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); - } catch (IllegalArgumentException e) { - String response = "Bad query string"; - s_logger.error(response + ", request URI : " + t.getRequestURI()); - t.sendResponseHeaders(200, response.length()); - OutputStream os = t.getResponseBody(); - os.write(response.getBytes()); - os.close(); - } catch(OutOfMemoryError e) { - s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); - System.exit(1); - } catch (Throwable e) { - s_logger.error("Unexpected exception while handing thumbnail request, ", e); - - String queries = t.getRequestURI().getQuery(); - Map queryMap = getQueryMap(queries); - int width = 0; - int height = 0; - String ws = queryMap.get("w"); - String hs = queryMap.get("h"); - try { - width = Integer.parseInt(ws); - height = Integer.parseInt(hs); - } catch (NumberFormatException ex) { - } - width = Math.min(width, 800); - height = Math.min(height, 600); - - BufferedImage img = generateTextImage(width, height, "Cannot Connect"); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(img, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - s_logger.error("Cannot get console, sent error JPG response for " + t.getRequestURI()); - return; - } finally { - t.close(); - } - } - - private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { - String queries = t.getRequestURI().getQuery(); - Map queryMap = getQueryMap(queries); - int width = 0; - int height = 0; - int port = 0; - String ws = queryMap.get("w"); - String hs = queryMap.get("h"); - String host = queryMap.get("host"); - String portStr = queryMap.get("port"); - String sid = queryMap.get("sid"); - String tag = queryMap.get("tag"); - String ticket = queryMap.get("ticket"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); - - if(tag == null) - tag = ""; - - if (ws == null || hs == null || host == null || portStr == null || sid == null ) { - throw new IllegalArgumentException(); - } - try { - width = Integer.parseInt(ws); - height = Integer.parseInt(hs); - port = Integer.parseInt(portStr); - } catch (NumberFormatException e) { - throw new IllegalArgumentException(e); - } - - ConsoleProxyClientParam param = new ConsoleProxyClientParam(); - param.setClientHostAddress(host); - param.setClientHostPort(port); - param.setClientHostPassword(sid); - param.setClientTag(tag); - param.setTicket(ticket); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); - - ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); - - if (!viewer.isHostConnected()) { - // use generated image instead of static - BufferedImage img = generateTextImage(width, height, "Connecting"); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(img, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - - if(s_logger.isInfoEnabled()) - s_logger.info("Console not ready, sent dummy JPG response"); - return; - } - - { - Image scaledImage = viewer.getClientScaledImage(width, height); - BufferedImage bufferedImage = new BufferedImage(width, height, - BufferedImage.TYPE_3BYTE_BGR); - Graphics2D bufImageGraphics = bufferedImage.createGraphics(); - bufImageGraphics.drawImage(scaledImage, 0, 0, null); - ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); - javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); - byte[] bs = bos.toByteArray(); - Headers hds = t.getResponseHeaders(); - hds.set("Content-Type", "image/jpeg"); - hds.set("Cache-Control", "no-cache"); - hds.set("Cache-Control", "no-store"); - t.sendResponseHeaders(200, bs.length); - OutputStream os = t.getResponseBody(); - os.write(bs); - os.close(); - } - } - - public static BufferedImage generateTextImage(int w, int h, String text) { - BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = img.createGraphics(); - g.setColor(Color.BLACK); - g.fillRect(0, 0, w, h); - g.setColor(Color.WHITE); - try { - g.setFont(new Font(null, Font.PLAIN, 12)); - FontMetrics fm = g.getFontMetrics(); - int textWidth = fm.stringWidth(text); - int startx = (w-textWidth) / 2; - if(startx < 0) - startx = 0; - g.drawString(text, startx, h/2); - } catch (Throwable e) { - s_logger.warn("Problem in generating text to thumnail image, return blank image"); - } - return img; - } - - public static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap(); - for (String param : params) { - String name = param.split("=")[0]; - String value = param.split("=")[1]; - map.put(name, value); - } - return map; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java b/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java deleted file mode 100644 index 6a473b5f007..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java +++ /dev/null @@ -1,235 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -import java.io.IOException; -import java.net.URI; -import java.net.UnknownHostException; - -import org.apache.log4j.Logger; - -import com.cloud.consoleproxy.vnc.FrameBufferCanvas; -import com.cloud.consoleproxy.vnc.RfbConstants; -import com.cloud.consoleproxy.vnc.VncClient; - -/** - * - * ConsoleProxyVncClient bridges a VNC engine with the front-end AJAX viewer - * - */ -public class ConsoleProxyVncClient extends ConsoleProxyClientBase { - private static final Logger s_logger = Logger.getLogger(ConsoleProxyVncClient.class); - - private static final int SHIFT_KEY_MASK = 64; - private static final int CTRL_KEY_MASK = 128; - private static final int META_KEY_MASK = 256; - private static final int ALT_KEY_MASK = 512; - - private static final int X11_KEY_SHIFT = 0xffe1; - private static final int X11_KEY_CTRL = 0xffe3; - private static final int X11_KEY_ALT = 0xffe9; - private static final int X11_KEY_META = 0xffe7; - - private VncClient client; - private Thread worker; - private boolean workerDone = false; - - private int lastModifierStates = 0; - private int lastPointerMask = 0; - - public ConsoleProxyVncClient() { - } - - public boolean isHostConnected() { - if(client != null) - return client.isHostConnected(); - - return false; - } - - @Override - public boolean isFrontEndAlive() { - if(workerDone || System.currentTimeMillis() - getClientLastFrontEndActivityTime() > ConsoleProxy.VIEWER_LINGER_SECONDS*1000) { - s_logger.info("Front end has been idle for too long"); - return false; - } - return true; - } - - @Override - public void initClient(ConsoleProxyClientParam param) { - setClientParam(param); - - client = new VncClient(this); - worker = new Thread(new Runnable() { - public void run() { - String tunnelUrl = getClientParam().getClientTunnelUrl(); - String tunnelSession = getClientParam().getClientTunnelSession(); - - for(int i = 0; i < 15; i++) { - try { - if(tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null && !tunnelSession.isEmpty()) { - URI uri = new URI(tunnelUrl); - s_logger.info("Connect to VNC server via tunnel. url: " + tunnelUrl + ", session: " + tunnelSession); - - ConsoleProxy.ensureRoute(uri.getHost()); - client.connectTo( - uri.getHost(), uri.getPort(), - uri.getPath() + "?" + uri.getQuery(), - tunnelSession, "https".equalsIgnoreCase(uri.getScheme()), - getClientHostPassword()); - } else { - s_logger.info("Connect to VNC server directly. host: " + getClientHostAddress() + ", port: " + getClientHostPort()); - ConsoleProxy.ensureRoute(getClientHostAddress()); - client.connectTo(getClientHostAddress(), getClientHostPort(), getClientHostPassword()); - } - } catch (UnknownHostException e) { - s_logger.error("Unexpected exception (will retry until timeout)", e); - } catch (IOException e) { - s_logger.error("Unexpected exception (will retry until timeout) ", e); - } catch (Throwable e) { - s_logger.error("Unexpected exception (will retry until timeout) ", e); - } - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - - if(tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null && !tunnelSession.isEmpty()) { - ConsoleProxyAuthenticationResult authResult = ConsoleProxy.reAuthenticationExternally(getClientParam()); - if(authResult != null && authResult.isSuccess()) { - if(authResult.getTunnelUrl() != null && !authResult.getTunnelUrl().isEmpty() && - authResult.getTunnelSession() != null && !authResult.getTunnelSession().isEmpty()) { - tunnelUrl = authResult.getTunnelUrl(); - tunnelSession = authResult.getTunnelSession(); - - s_logger.info("Reset XAPI session. url: " + tunnelUrl + ", session: " + tunnelSession); - } - } - } - } - - s_logger.info("Receiver thread stopped."); - workerDone = true; - client.getClientListener().onClientClose(); - } - }); - - worker.setDaemon(true); - worker.start(); - } - - @Override - public void closeClient() { - if(client != null) - client.shutdown(); - } - - @Override - public void onClientConnected() { - } - - public void onClientClose() { - s_logger.info("Received client close indication. remove viewer from map."); - - ConsoleProxy.removeViewer(this); - } - - @Override - public void onFramebufferUpdate(int x, int y, int w, int h) { - super.onFramebufferUpdate(x, y, w, h); - client.requestUpdate(false); - } - - public void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers) { - if(client == null) - return; - - updateFrontEndActivityTime(); - - switch(event) { - case KEY_DOWN : - sendModifierEvents(modifiers); - client.sendClientKeyboardEvent(RfbConstants.KEY_DOWN, code, 0); - break; - - case KEY_UP : - client.sendClientKeyboardEvent(RfbConstants.KEY_UP, code, 0); - sendModifierEvents(0); - break; - - case KEY_PRESS : - break; - - default : - assert(false); - break; - } - } - - public void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers) { - if(client == null) - return; - - updateFrontEndActivityTime(); - - if (event == InputEventType.MOUSE_DOWN) { - if (code == 2) { - lastPointerMask |= 4; - } else if (code == 0) { - lastPointerMask |= 1; - } - } - - if (event == InputEventType.MOUSE_UP) { - if (code == 2) { - lastPointerMask ^= 4; - } else if (code == 0) { - lastPointerMask ^= 1; - } - } - - sendModifierEvents(modifiers); - client.sendClientMouseEvent(lastPointerMask, x, y, code, modifiers); - if(lastPointerMask == 0) - sendModifierEvents(0); - } - - @Override - protected FrameBufferCanvas getFrameBufferCavas() { - if(client != null) - return client.getFrameBufferCanvas(); - return null; - } - - private void sendModifierEvents(int modifiers) { - if((modifiers & SHIFT_KEY_MASK) != (lastModifierStates & SHIFT_KEY_MASK)) - client.sendClientKeyboardEvent((modifiers & SHIFT_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_SHIFT, 0); - - if((modifiers & CTRL_KEY_MASK) != (lastModifierStates & CTRL_KEY_MASK)) - client.sendClientKeyboardEvent((modifiers & CTRL_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_CTRL, 0); - - if((modifiers & META_KEY_MASK) != (lastModifierStates & META_KEY_MASK)) - client.sendClientKeyboardEvent((modifiers & META_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_META, 0); - - if((modifiers & ALT_KEY_MASK) != (lastModifierStates & ALT_KEY_MASK)) - client.sendClientKeyboardEvent((modifiers & ALT_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_ALT, 0); - - lastModifierStates = modifiers; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/InputEventType.java b/console-proxy/src/com/cloud/consoleproxy/InputEventType.java deleted file mode 100644 index 4a5aff16bcf..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/InputEventType.java +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy; - -public enum InputEventType { - MOUSE_MOVE(1), - MOUSE_DOWN(2), - MOUSE_UP(3), - KEY_PRESS(4), - KEY_DOWN(5), - KEY_UP(6), - MOUSE_DBLCLICK(8); - - int eventCode; - private InputEventType(int eventCode) { - this.eventCode = eventCode; - } - - public int getEventCode() { - return eventCode; - } - - public static InputEventType fromEventCode(int eventCode) { - switch(eventCode) { - case 1 : - return MOUSE_MOVE; - case 2 : - return MOUSE_DOWN; - case 3 : - return MOUSE_UP; - case 4 : - return KEY_PRESS; - case 5 : - return KEY_DOWN; - case 6 : - return KEY_UP; - case 8 : - return MOUSE_DBLCLICK; - default : - break; - } - throw new IllegalArgumentException("Unsupport event code: " + eventCode); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java b/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java deleted file mode 100644 index 2ff82d7242f..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/ITileScanListener.java +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.List; - -public interface ITileScanListener { - boolean onTileChange(Rectangle rowMergedRect, int row, int col); - void onRegionChange(List regionList); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java b/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java deleted file mode 100644 index bb7373e78b2..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/ImageHelper.java +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -public class ImageHelper { - public static byte[] jpegFromImage(BufferedImage image) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(128000); - javax.imageio.ImageIO.write(image, "jpg", bos); - - byte[] jpegBits = bos.toByteArray(); - bos.close(); - return jpegBits; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/Logger.java b/console-proxy/src/com/cloud/consoleproxy/util/Logger.java deleted file mode 100644 index f4357bd4bc4..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/Logger.java +++ /dev/null @@ -1,223 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -// logger facility for dynamic switch between console logger used in Applet and log4j based logger -public class Logger { - private static LoggerFactory factory = null; - - public static final int LEVEL_TRACE = 1; - public static final int LEVEL_DEBUG = 2; - public static final int LEVEL_INFO = 3; - public static final int LEVEL_WARN = 4; - public static final int LEVEL_ERROR = 5; - - private Class clazz; - private Logger logger; - - private static int level = LEVEL_INFO; - - public static Logger getLogger(Class clazz) { - return new Logger(clazz); - } - - public static void setFactory(LoggerFactory f) { - factory = f; - } - - public static void setLevel(int l) { - level = l; - } - - public Logger(Class clazz) { - this.clazz = clazz; - } - - protected Logger() { - } - - public boolean isTraceEnabled() { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - return logger.isTraceEnabled(); - } - return level <= LEVEL_TRACE; - } - - public boolean isDebugEnabled() { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - return logger.isDebugEnabled(); - } - return level <= LEVEL_DEBUG; - } - - public boolean isInfoEnabled() { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - return logger.isInfoEnabled(); - } - return level <= LEVEL_INFO; - } - - public void trace(Object message) { - - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.trace(message); - } else { - if(level <= LEVEL_TRACE) - System.out.println(message); - } - } - - public void trace(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.trace(message, exception); - } else { - if(level <= LEVEL_TRACE) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void info(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.info(message); - } else { - if(level <= LEVEL_INFO) - System.out.println(message); - } - } - - public void info(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.info(message, exception); - } else { - if(level <= LEVEL_INFO) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void debug(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.debug(message); - } else { - if(level <= LEVEL_DEBUG) - System.out.println(message); - } - } - - public void debug(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.debug(message, exception); - } else { - if(level <= LEVEL_DEBUG) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void warn(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.warn(message); - } else { - if(level <= LEVEL_WARN) - System.out.println(message); - } - } - - public void warn(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.warn(message, exception); - } else { - if(level <= LEVEL_WARN) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void error(Object message) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.error(message); - } else { - if(level <= LEVEL_ERROR) - System.out.println(message); - } - } - - public void error(Object message, Throwable exception) { - if(factory != null) { - if(logger == null) - logger = factory.getLogger(clazz); - - logger.error(message, exception); - } else { - if(level <= LEVEL_ERROR) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java b/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java deleted file mode 100644 index 121411adf16..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/LoggerFactory.java +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -public interface LoggerFactory { - Logger getLogger(Class clazz); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java b/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java deleted file mode 100644 index c77b5511270..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/RawHTTP.java +++ /dev/null @@ -1,249 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -// -// This file is originally from XenConsole with modifications -// - -/** - * Send an HTTP CONNECT or PUT request to a XenAPI host with a Session ID, - * return the connected socket and the Task ID. Used for tunnelling VNC - * connections and import/export operations. - */ -public final class RawHTTP { - private static final Logger s_logger = Logger.getLogger(RawHTTP.class); - - private static final Pattern END_PATTERN = Pattern.compile("^\r\n$"); - private static final Pattern HEADER_PATTERN = Pattern - .compile("^([A-Z_a-z0-9-]+):\\s*(.*)\r\n$"); - private static final Pattern HTTP_PATTERN = Pattern - .compile("^HTTP/\\d+\\.\\d+ (\\d*) (.*)\r\n$"); - - /** - * @uml.property name="command" - */ - private final String command; - /** - * @uml.property name="host" - */ - private final String host; - /** - * @uml.property name="port" - */ - private final int port; - /** - * @uml.property name="path" - */ - private final String path; - /** - * @uml.property name="session" - */ - private final String session; - /** - * @uml.property name="useSSL" - */ - private final boolean useSSL; - - /** - * @uml.property name="responseHeaders" - * @uml.associationEnd qualifier="group:java.lang.String java.lang.String" - */ - private final Map responseHeaders = new HashMap(); - - /** - * @uml.property name="ic" - */ - private InputStream ic; - /** - * @uml.property name="oc" - */ - private OutputStream oc; - /** - * @uml.property name="s" - */ - private Socket s; - - public InputStream getInputStream() { - return ic; - } - - public OutputStream getOutputStream() { - return oc; - } - - public Socket getSocket() { - return s; - } - - public RawHTTP(String command, String host, int port, String path, - String session, boolean useSSL) { - this.command = command; - this.host = host; - this.port = port; - this.path = path; - this.session = session; - this.useSSL = useSSL; - } - - private static final TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } - }; - - private Socket _getSocket() throws IOException { - if (useSSL) { - SSLContext context = getClientSSLContext(); - if(context == null) - throw new IOException("Unable to setup SSL context"); - - SSLSocket ssl = null; - try { - context.init(null, trustAllCerts, new SecureRandom()); - SocketFactory factory = context.getSocketFactory(); - ssl = (SSLSocket) factory.createSocket(host, port); - /* ssl.setSSLParameters(context.getDefaultSSLParameters()); */ - } catch (IOException e) { - s_logger.error("IOException: " + e.getMessage(), e); - throw e; - } catch (KeyManagementException e) { - s_logger.error("KeyManagementException: " + e.getMessage(), e); - } - return ssl; - } else { - return new Socket(host, port); - } - } - - public Socket connect() throws IOException { - String[] headers = makeHeaders(); - s = _getSocket(); - try { - oc = s.getOutputStream(); - for (String header : headers) { - oc.write(header.getBytes()); - oc.write("\r\n".getBytes()); - } - oc.flush(); - ic = s.getInputStream(); - while (true) { - String line = readline(ic); - - Matcher m = END_PATTERN.matcher(line); - if (m.matches()) { - return s; - } - - m = HEADER_PATTERN.matcher(line); - if (m.matches()) { - responseHeaders.put(m.group(1), m.group(2)); - continue; - } - - m = HTTP_PATTERN.matcher(line); - if (m.matches()) { - String status_code = m.group(1); - String reason_phrase = m.group(2); - if (!"200".equals(status_code)) { - throw new IOException("HTTP status " + status_code - + " " + reason_phrase); - } - } else { - throw new IOException("Unknown HTTP line " + line); - } - } - } catch (IOException exn) { - s.close(); - throw exn; - } catch (RuntimeException exn) { - s.close(); - throw exn; - } - } - - public Map getResponseHeaders() { - return responseHeaders; - } - - private String[] makeHeaders() { - String[] headers = { String.format("%s %s HTTP/1.0", command, path), - String.format("Host: %s", host), - String.format("Cookie: session_id=%s", session), "" }; - return headers; - } - - private static String readline(InputStream ic) throws IOException { - String result = ""; - while (true) { - try { - int c = ic.read(); - - if (c == -1) { - return result; - } - result = result + (char) c; - if (c == 0x0a /* LF */) { - return result; - } - } catch (IOException e) { - ic.close(); - throw e; - } - } - } - - private SSLContext getClientSSLContext() { - SSLContext sslContext = null; - try { - sslContext = SSLContext.getInstance("SSL", "SunJSSE"); - } catch (NoSuchAlgorithmException e) { - s_logger.error("Unexpected exception ", e); - } catch (NoSuchProviderException e) { - s_logger.error("Unexpected exception ", e); - } - return sslContext; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/Region.java b/console-proxy/src/com/cloud/consoleproxy/util/Region.java deleted file mode 100644 index a5d1751d63b..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/Region.java +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - -public class Region { - private Rectangle bound; - private List rectList; - - public Region() { - bound = new Rectangle(0, 0, 0, 0); - rectList = new ArrayList(); - } - - public Region(Rectangle rect) { - bound = new Rectangle(rect.x, rect.y, rect.width, rect.height); - rectList = new ArrayList(); - rectList.add(rect); - } - - public Rectangle getBound() { - return bound; - } - - public void clearBound() { - assert(rectList.size() == 0); - bound.x = bound.y = bound.width = bound.height = 0; - } - - public List getRectangles() { - return rectList; - } - - public boolean add(Rectangle rect) { - if(bound.isEmpty()) { - assert(rectList.size() == 0); - bound.x = rect.x; - bound.y = rect.y; - bound.width = rect.width; - bound.height = rect.height; - - rectList.add(rect); - return true; - } - - Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y- 1, rect.width + 2, rect.height + 2); - if(!bound.intersects(rcInflated)) - return false; - - for(Rectangle r : rectList) { - if(r.intersects(rcInflated)) { - if(!r.contains(rect)) { - enlargeBound(rect); - rectList.add(rect); - return true; - } - } - } - return false; - } - - private void enlargeBound(Rectangle rect) { - int boundLeft = Math.min(bound.x, rect.x); - int boundTop = Math.min(bound.y, rect.y); - int boundRight = Math.max(bound.x + bound.width, rect.x + rect.width); - int boundBottom = Math.max(bound.y + bound.height, rect.y + rect.height); - - bound.x = boundLeft; - bound.y = boundTop; - bound.width = boundRight - boundLeft; - bound.height = boundBottom - boundTop; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java b/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java deleted file mode 100644 index 9faa04ea9eb..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/RegionClassifier.java +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - -public class RegionClassifier { - private List regionList; - - public RegionClassifier() { - regionList = new ArrayList(); - } - - public void add(Rectangle rect) { - boolean newRegion = true; - Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2); - for(Region region : regionList) { - if(region.getBound().intersects(rcInflated)) { - newRegion = false; - break; - } - } - - if(newRegion) { - regionList.add(new Region(rect)); - } else { - for(Region region : regionList) { - if(region.add(rect)) - return; - } - regionList.add(new Region(rect)); - } - } - - public List getRegionList() { - return regionList; - } - - public void clear() { - regionList.clear(); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java b/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java deleted file mode 100644 index 933a55a55b0..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/TileInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; - -public class TileInfo { - private int row; - private int col; - private Rectangle tileRect; - - public TileInfo(int row, int col, Rectangle tileRect) { - this.row = row; - this.col = col; - this.tileRect = tileRect; - } - - public int getRow() { - return row; - } - - public void setRow(int row) { - this.row = row; - } - - public int getCol() { - return col; - } - - public void setCol(int col) { - this.col = col; - } - - public Rectangle getTileRect() { - return tileRect; - } - - public void setTileRect(Rectangle tileRect) { - this.tileRect = tileRect; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java b/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java deleted file mode 100644 index b3facb2f88f..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/util/TileTracker.java +++ /dev/null @@ -1,269 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.util; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - -public class TileTracker { - - // 2 dimension tile status snapshot, a true value means the corresponding tile has been invalidated - private boolean[][] snapshot; - - private int tileWidth = 0; - private int tileHeight = 0; - private int trackWidth = 0; - private int trackHeight = 0; - - public TileTracker() { - } - - public int getTileWidth() { - return tileWidth; - } - - public void setTileWidth(int tileWidth) { - this.tileWidth = tileWidth; - } - - public int getTileHeight() { - return tileHeight; - } - - public void setTileHeight(int tileHeight) { - this.tileHeight = tileHeight; - } - - public int getTrackWidth() { - return trackWidth; - } - - public void setTrackWidth(int trackWidth) { - this.trackWidth = trackWidth; - } - - public int getTrackHeight() { - return trackHeight; - } - - public void setTrackHeight(int trackHeight) { - this.trackHeight = trackHeight; - } - - public void initTracking(int tileWidth, int tileHeight, int trackWidth, int trackHeight) { - assert(tileWidth > 0); - assert(tileHeight > 0); - assert(trackWidth > 0); - assert(trackHeight > 0); - assert(tileWidth <= trackWidth); - assert(tileHeight <= trackHeight); - - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.trackWidth = trackWidth; - this.trackHeight = trackHeight; - - int cols = getTileCols(); - int rows = getTileRows(); - snapshot = new boolean[rows][cols]; - for(int i = 0; i < rows; i++) - for(int j = 0; j < cols; j++) - snapshot[i][j] = false; - } - - public synchronized void resize(int trackWidth, int trackHeight) { - assert(tileWidth > 0); - assert(tileHeight > 0); - assert(trackWidth > 0); - assert(trackHeight > 0); - - this.trackWidth = trackWidth; - this.trackHeight = trackHeight; - - int cols = getTileCols(); - int rows = getTileRows(); - snapshot = new boolean[rows][cols]; - for(int i = 0; i < rows; i++) - for(int j = 0; j < cols; j++) - snapshot[i][j] = true; - } - - public void invalidate(Rectangle rect) { - setTileFlag(rect, true); - } - - public void validate(Rectangle rect) { - setTileFlag(rect, false); - } - - public List scan(boolean init) { - List l = new ArrayList(); - - synchronized(this) { - for(int i = 0; i < getTileRows(); i++) { - for(int j = 0; j < getTileCols(); j++) { - if(init || snapshot[i][j]) { - Rectangle rect = new Rectangle(); - rect.y = i*tileHeight; - rect.x = j*tileWidth; - rect.width = Math.min(trackWidth - rect.x, tileWidth); - rect.height = Math.min(trackHeight - rect.y, tileHeight); - - l.add(new TileInfo(i, j, rect)); - snapshot[i][j] = false; - } - } - } - - return l; - } - } - - public boolean hasFullCoverage() { - synchronized(this) { - for(int i = 0; i < getTileRows(); i++) { - for(int j = 0; j < getTileCols(); j++) { - if(!snapshot[i][j]) - return false; - } - } - } - return true; - } - - - - public void initCoverageTest() { - synchronized(this) { - for(int i = 0; i < getTileRows(); i++) { - for(int j = 0; j < getTileCols(); j++) { - snapshot[i][j] = false; - } - } - } - } - - // listener will be called while holding the object lock, use it - // with care to avoid deadlock condition being formed - public synchronized void scan(int nStartRow, int nStartCol, ITileScanListener listener) { - assert(listener != null); - - int cols = getTileCols(); - int rows = getTileRows(); - - nStartRow = nStartRow % rows; - nStartCol = nStartCol % cols; - - int nPos = nStartRow*cols + nStartCol; - int nUnits = rows*cols; - int nStartPos = nPos; - int nRow; - int nCol; - do { - nRow = nPos / cols; - nCol = nPos % cols; - - if(snapshot[nRow][nCol]) { - int nEndCol = nCol; - for(; nEndCol < cols && snapshot[nRow][nEndCol]; nEndCol++) { - snapshot[nRow][nEndCol] = false; - } - - Rectangle rect = new Rectangle(); - rect.y = nRow*tileHeight; - rect.height = tileHeight; - rect.x = nCol*tileWidth; - rect.width = (nEndCol - nCol)*tileWidth; - - if(!listener.onTileChange(rect, nRow, nEndCol)) - break; - } - - nPos = (nPos + 1) % nUnits; - } while(nPos != nStartPos); - } - - public void capture(ITileScanListener listener) { - assert(listener != null); - - int cols = getTileCols(); - int rows = getTileRows(); - - RegionClassifier classifier = new RegionClassifier(); - int left, top, right, bottom; - - synchronized(this) { - for(int i = 0; i < rows; i++) { - top = i*tileHeight; - bottom = Math.min(top + tileHeight, trackHeight); - for(int j = 0; j < cols; j++) { - left = j*tileWidth; - right = Math.min(left + tileWidth, trackWidth); - - if(snapshot[i][j]) { - snapshot[i][j] = false; - classifier.add(new Rectangle(left, top, right - left, bottom - top)); - } - } - } - } - listener.onRegionChange(classifier.getRegionList()); - } - - private synchronized void setTileFlag(Rectangle rect, boolean flag) { - int nStartTileRow; - int nStartTileCol; - int nEndTileRow; - int nEndTileCol; - - int cols = getTileCols(); - int rows = getTileRows(); - - if(rect != null) { - nStartTileRow = Math.min(getTileYPos(rect.y), rows - 1); - nStartTileCol = Math.min(getTileXPos(rect.x), cols - 1); - nEndTileRow = Math.min(getTileYPos(rect.y + rect.height - 1), rows -1); - nEndTileCol = Math.min(getTileXPos(rect.x + rect.width - 1), cols -1); - } else { - nStartTileRow = 0; - nStartTileCol = 0; - nEndTileRow = rows - 1; - nEndTileCol = cols - 1; - } - - for(int i = nStartTileRow; i <= nEndTileRow; i++) - for(int j = nStartTileCol; j <= nEndTileCol; j++) - snapshot[i][j] = flag; - } - - private int getTileRows() { - return (trackHeight + tileHeight - 1) / tileHeight; - } - - private int getTileCols() { - return (trackWidth + tileWidth - 1) / tileWidth; - } - - private int getTileXPos(int x) { - return x / tileWidth; - } - - public int getTileYPos(int y) { - return y / tileHeight; - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java b/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java deleted file mode 100644 index f2fb4bb6b69..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -import java.awt.Canvas; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.util.List; - -import com.cloud.consoleproxy.util.ImageHelper; -import com.cloud.consoleproxy.util.TileInfo; - -/** - * A BuffereImageCanvas component represents frame buffer image on - * the screen. It also notifies its subscribers when screen is repainted. - */ -public class BufferedImageCanvas extends Canvas implements FrameBufferCanvas { - private static final long serialVersionUID = 1L; - - // Offline screen buffer - private BufferedImage offlineImage; - - // Cached Graphics2D object for offline screen buffer - private Graphics2D graphics; - - private PaintNotificationListener listener; - - public BufferedImageCanvas(PaintNotificationListener listener, int width, int height) { - super(); - this.listener = listener; - - setBackground(Color.black); - - setFocusable(true); - - // Don't intercept TAB key - setFocusTraversalKeysEnabled(false); - - setCanvasSize(width, height); - } - - public void setCanvasSize(int width, int height) { - this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - graphics = offlineImage.createGraphics(); - - setSize(offlineImage.getWidth(), offlineImage.getHeight()); - } - - @Override - public void update(Graphics g) { - // Call paint() directly, without clearing screen first - paint(g); - } - - @Override - public void paint(Graphics g) { - // Only part of image, requested with repaint(Rectangle), will be - // painted on screen. - synchronized (offlineImage) { - g.drawImage(offlineImage, 0, 0, this); - } - // Notify server that update is painted on screen - listener.imagePaintedOnScreen(); - } - - public BufferedImage getOfflineImage() { - return offlineImage; - } - - public Graphics2D getOfflineGraphics() { - return graphics; - } - - public void copyTile(Graphics2D g, int x, int y, Rectangle rc) { - synchronized (offlineImage) { - g.drawImage(offlineImage, x, y, x + rc.width, y + rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); - } - } - - @Override - public Image getFrameBufferScaledImage(int width, int height) { - if (offlineImage != null) - return offlineImage.getScaledInstance(width, height, Image.SCALE_DEFAULT); - return null; - } - - @Override - public byte[] getFrameBufferJpeg() { - int width = 800; - int height = 600; - - width = offlineImage.getWidth(); - height = offlineImage.getHeight(); - - BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = bufferedImage.createGraphics(); - synchronized (offlineImage) { - g.drawImage(offlineImage, 0, 0, width, height, 0, 0, width, height, null); - } - - byte[] imgBits = null; - try { - imgBits = ImageHelper.jpegFromImage(bufferedImage); - } catch (IOException e) { - } - return imgBits; - } - - @Override - public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight) { - int width = Math.max(tileWidth, tileWidth * tileList.size()); - BufferedImage bufferedImage = new BufferedImage(width, tileHeight, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = bufferedImage.createGraphics(); - - synchronized (offlineImage) { - int i = 0; - for (TileInfo tile : tileList) { - Rectangle rc = tile.getTileRect(); - g.drawImage(offlineImage, i * tileWidth, 0, i * tileWidth + rc.width, rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); - i++; - } - } - - byte[] imgBits = null; - try { - imgBits = ImageHelper.jpegFromImage(bufferedImage); - } catch (IOException e) { - } - return imgBits; - } -} \ No newline at end of file diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java b/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java deleted file mode 100644 index dcf11461360..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -import java.awt.Image; -import java.util.List; - -import com.cloud.consoleproxy.util.TileInfo; - -public interface FrameBufferCanvas { - Image getFrameBufferScaledImage(int width, int height); - - public byte[] getFrameBufferJpeg(); - - public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java b/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java deleted file mode 100644 index b8527c5261a..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -public interface FrameBufferUpdateListener { - - /** - * Notify listener, that frame buffer update packet is received, so client - * is permitted (but not obligated) to ask server to send another update. - */ - void frameBufferPacketReceived(); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java b/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java deleted file mode 100644 index aaefacb99f2..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -public interface PaintNotificationListener { - - /** - * Notify subscriber that screen is updated, so client can send another - * frame buffer update request to server. - */ - void imagePaintedOnScreen(); - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java b/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java deleted file mode 100644 index 18bf47ec1d4..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/RfbConstants.java +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -import java.nio.charset.Charset; - -public interface RfbConstants { - - public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003."; - // public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; - public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; - public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR; - - /** - * Server message types. - */ - final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3; - - /** - * Client message types. - */ - public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3, CLIENT_KEYBOARD_EVENT = 4, - CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6; - - /** - * Server authorization type - */ - public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2; - - /** - * Server authorization reply. - */ - public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2; - - /** - * Encodings. - */ - public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16; - - /** - * Pseudo-encodings. - */ - public final static int ENCODING_CURSOR = -239 /* 0xFFFFFF11 */, ENCODING_DESKTOP_SIZE = -223 /* 0xFFFFFF21 */; - - /** - * Encodings, which we support. - */ - public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE }; - - /** - * Frame buffer update request type: update of whole screen or partial - * update. - */ - public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1; - - public static final int KEY_UP = 0, KEY_DOWN = 1; - - public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1; - - public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1; - - public static final int PALETTE = 0, TRUE_COLOR = 1; - - /** - * Default charset to use when communicating with server. - */ - public static final Charset CHARSET = Charset.availableCharsets().get("US-ASCII"); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java deleted file mode 100644 index 194eec17414..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClient.java +++ /dev/null @@ -1,451 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -import java.awt.Frame; -import java.awt.ScrollPane; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.spec.KeySpec; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESKeySpec; - -import com.cloud.consoleproxy.ConsoleProxyClientListener; -import com.cloud.consoleproxy.util.Logger; -import com.cloud.consoleproxy.util.RawHTTP; -import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; -import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; - -public class VncClient { - private static final Logger s_logger = Logger.getLogger(VncClient.class); - - private Socket socket; - private DataInputStream is; - private DataOutputStream os; - - private VncScreenDescription screen = new VncScreenDescription(); - - private VncClientPacketSender sender; - private VncServerPacketReceiver receiver; - - private boolean noUI = false; - private ConsoleProxyClientListener clientListener = null; - - public static void main(String args[]) { - if (args.length < 3) { - printHelpMessage(); - System.exit(1); - } - - String host = args[0]; - String port = args[1]; - String password = args[2]; - - try { - new VncClient(host, Integer.parseInt(port), password, false, null); - } catch (NumberFormatException e) { - s_logger.error("Incorrect VNC server port number: " + port + "."); - System.exit(1); - } catch (UnknownHostException e) { - s_logger.error("Incorrect VNC server host name: " + host + "."); - System.exit(1); - } catch (IOException e) { - s_logger.error("Cannot communicate with VNC server: " + e.getMessage()); - System.exit(1); - } catch (Throwable e) { - s_logger.error("An error happened: " + e.getMessage()); - System.exit(1); - } - System.exit(0); - } - - private static void printHelpMessage() { - /* LOG */s_logger.info("Usage: HOST PORT PASSWORD."); - } - - public VncClient(ConsoleProxyClientListener clientListener) { - this.noUI = true; - this.clientListener = clientListener; - } - - public VncClient(String host, int port, String password, boolean noUI, ConsoleProxyClientListener clientListener) throws UnknownHostException, IOException { - - this.noUI = noUI; - this.clientListener = clientListener; - connectTo(host, port, password); - } - - public void shutdown() { - if (sender != null) - sender.closeConnection(); - - if (receiver != null) - receiver.closeConnection(); - - if (is != null) { - try { - is.close(); - } catch (Throwable e) { - } - } - - if (os != null) { - try { - os.close(); - } catch (Throwable e) { - } - } - - if (socket != null) { - try { - socket.close(); - } catch (Throwable e) { - } - } - } - - public ConsoleProxyClientListener getClientListener() { - return clientListener; - } - - public void connectTo(String host, int port, String path, String session, boolean useSSL, String sid) throws UnknownHostException, IOException { - if (port < 0) { - if (useSSL) - port = 443; - else - port = 80; - } - - RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL); - this.socket = tunnel.connect(); - doConnect(sid); - } - - public void connectTo(String host, int port, String password) throws UnknownHostException, IOException { - // Connect to server - s_logger.info("Connecting to VNC server " + host + ":" + port + "..."); - this.socket = new Socket(host, port); - doConnect(password); - } - - private void doConnect(String password) throws IOException { - is = new DataInputStream(socket.getInputStream()); - os = new DataOutputStream(socket.getOutputStream()); - - // Initialize connection - handshake(); - authenticate(password); - initialize(); - - s_logger.info("Connecting to VNC server succeeded, start session"); - - // Run client-to-server packet sender - sender = new VncClientPacketSender(os, screen, this); - - // Create buffered image canvas - BufferedImageCanvas canvas = new BufferedImageCanvas(sender, screen.getFramebufferWidth(), screen.getFramebufferHeight()); - - // Subscribe packet sender to various events - canvas.addMouseListener(sender); - canvas.addMouseMotionListener(sender); - canvas.addKeyListener(sender); - - Frame frame = null; - if (!noUI) - frame = createVncClientMainWindow(canvas, screen.getDesktopName()); - - new Thread(sender).start(); - - // Run server-to-client packet receiver - receiver = new VncServerPacketReceiver(is, canvas, screen, this, sender, clientListener); - try { - receiver.run(); - } finally { - if (frame != null) { - frame.setVisible(false); - frame.dispose(); - } - this.shutdown(); - } - } - - private Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title) { - // Create AWT windows - final Frame frame = new Frame(title + " - VNCle"); - - // Use scrolling pane to support screens, which are larger than ours - ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); - scroller.add(canvas); - scroller.setSize(screen.getFramebufferWidth(), screen.getFramebufferHeight()); - - frame.add(scroller); - frame.pack(); - frame.setVisible(true); - - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent evt) { - frame.setVisible(false); - shutdown(); - } - }); - - return frame; - } - - /** - * Handshake with VNC server. - */ - private void handshake() throws IOException { - - // Read protocol version - byte[] buf = new byte[12]; - is.readFully(buf); - String rfbProtocol = new String(buf); - - // Server should use RFB protocol 3.x - if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) { - s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); - throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); - } - - // Send response: we support RFB 3.3 only - String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n"; - os.write(ourProtocolString.getBytes()); - os.flush(); - } - - /** - * VNC authentication. - */ - private void authenticate(String password) throws IOException { - // Read security type - int authType = is.readInt(); - - switch (authType) { - case RfbConstants.CONNECTION_FAILED: { - // Server forbids to connect. Read reason and throw exception - - int length = is.readInt(); - byte[] buf = new byte[length]; - is.readFully(buf); - String reason = new String(buf, RfbConstants.CHARSET); - - s_logger.error("Authentication to VNC server is failed. Reason: " + reason); - throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason); - } - - case RfbConstants.NO_AUTH: { - // Client can connect without authorization. Nothing to do. - break; - } - - case RfbConstants.VNC_AUTH: { - s_logger.info("VNC server requires password authentication"); - doVncAuth(password); - break; - } - - default: - s_logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); - throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); - } - } - - /** - * Encode client password and send it to server. - */ - private void doVncAuth(String password) throws IOException { - - // Read challenge - byte[] challenge = new byte[16]; - is.readFully(challenge); - - // Encode challenge with password - byte[] response; - try { - response = encodePassword(challenge, password); - } catch (Exception e) { - s_logger.error("Cannot encrypt client password to send to server: " + e.getMessage()); - throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage()); - } - - // Send encoded challenge - os.write(response); - os.flush(); - - // Read security result - int authResult = is.readInt(); - - switch (authResult) { - case RfbConstants.VNC_AUTH_OK: { - // Nothing to do - break; - } - - case RfbConstants.VNC_AUTH_TOO_MANY: - s_logger.error("Connection to VNC server failed: too many wrong attempts."); - throw new RuntimeException("Connection to VNC server failed: too many wrong attempts."); - - case RfbConstants.VNC_AUTH_FAILED: - s_logger.error("Connection to VNC server failed: wrong password."); - throw new RuntimeException("Connection to VNC server failed: wrong password."); - - default: - s_logger.error("Connection to VNC server failed, reason code: " + authResult); - throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult); - } - } - - /** - * Encode password using DES encryption with given challenge. - * - * @param challenge - * a random set of bytes. - * @param password - * a password - * @return DES hash of password and challenge - */ - public byte[] encodePassword(byte[] challenge, String password) throws Exception { - // VNC password consist of up to eight ASCII characters. - byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding - byte[] passwordAsciiBytes = password.getBytes(RfbConstants.CHARSET); - System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8)); - - // Flip bytes (reverse bits) in key - for (int i = 0; i < key.length; i++) { - key[i] = flipByte(key[i]); - } - - KeySpec desKeySpec = new DESKeySpec(key); - SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); - SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec); - Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - - byte[] response = cipher.doFinal(challenge); - return response; - } - - /** - * Reverse bits in byte, so least significant bit will be most significant - * bit. E.g. 01001100 will become 00110010. - * - * See also: http://www.vidarholen.net/contents/junk/vnc.html , - * http://bytecrafter - * .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html - * - * @param b - * a byte - * @return byte in reverse order - */ - private static byte flipByte(byte b) { - int b1_8 = (b & 0x1) << 7; - int b2_7 = (b & 0x2) << 5; - int b3_6 = (b & 0x4) << 3; - int b4_5 = (b & 0x8) << 1; - int b5_4 = (b & 0x10) >>> 1; - int b6_3 = (b & 0x20) >>> 3; - int b7_2 = (b & 0x40) >>> 5; - int b8_1 = (b & 0x80) >>> 7; - byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1); - return c; - } - - private void initialize() throws IOException { - // Send client initialization message - { - // Send shared flag - os.writeByte(RfbConstants.EXCLUSIVE_ACCESS); - os.flush(); - } - - // Read server initialization message - { - // Read frame buffer size - int framebufferWidth = is.readUnsignedShort(); - int framebufferHeight = is.readUnsignedShort(); - screen.setFramebufferSize(framebufferWidth, framebufferHeight); - if (clientListener != null) - clientListener.onFramebufferSizeChange(framebufferWidth, framebufferHeight); - } - - // Read pixel format - { - int bitsPerPixel = is.readUnsignedByte(); - int depth = is.readUnsignedByte(); - - int bigEndianFlag = is.readUnsignedByte(); - int trueColorFlag = is.readUnsignedByte(); - - int redMax = is.readUnsignedShort(); - int greenMax = is.readUnsignedShort(); - int blueMax = is.readUnsignedShort(); - - int redShift = is.readUnsignedByte(); - int greenShift = is.readUnsignedByte(); - int blueShift = is.readUnsignedByte(); - - // Skip padding - is.skipBytes(3); - - screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColorFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift); - } - - // Read desktop name - { - int length = is.readInt(); - byte buf[] = new byte[length]; - is.readFully(buf); - String desktopName = new String(buf, RfbConstants.CHARSET); - screen.setDesktopName(desktopName); - } - } - - public FrameBufferCanvas getFrameBufferCanvas() { - if (receiver != null) - return receiver.getCanvas(); - - return null; - } - - public void requestUpdate(boolean fullUpdate) { - if (fullUpdate) - sender.requestFullScreenUpdate(); - else - sender.imagePaintedOnScreen(); - } - - public void sendClientKeyboardEvent(int event, int code, int modifiers) { - sender.sendClientPacket(new KeyboardEventPacket(event, code)); - } - - public void sendClientMouseEvent(int event, int x, int y, int code, int modifiers) { - sender.sendClientPacket(new MouseEventPacket(event, x, y)); - } - - public boolean isHostConnected() { - return receiver != null && receiver.isConnectionAlive(); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java deleted file mode 100644 index d27b76d3468..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java +++ /dev/null @@ -1,258 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.io.DataOutputStream; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -import com.cloud.consoleproxy.util.Logger; -import com.cloud.consoleproxy.vnc.packet.client.ClientPacket; -import com.cloud.consoleproxy.vnc.packet.client.FramebufferUpdateRequestPacket; -import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; -import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; -import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket; -import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket; - -public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener { - private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class); - - // Queue for outgoing packets - private final BlockingQueue queue = new ArrayBlockingQueue(30); - - private final DataOutputStream os; - private final VncScreenDescription screen; - private final VncClient vncConnection; - - private boolean connectionAlive = true; - - // Don't send update request again until we receive next frame buffer update - private boolean updateRequestSent = false; - - public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) { - this.os = os; - this.screen = screen; - this.vncConnection = vncConnection; - - sendSetPixelFormat(); - sendSetEncodings(); - requestFullScreenUpdate(); - } - - public void sendClientPacket(ClientPacket packet) { - queue.add(packet); - } - - @Override - public void run() { - try { - while (connectionAlive) { - ClientPacket packet = queue.poll(1, TimeUnit.SECONDS); - if (packet != null) { - packet.write(os); - os.flush(); - } - } - } catch (Throwable e) { - s_logger.error("Unexpected exception: ", e); - if (connectionAlive) { - closeConnection(); - vncConnection.shutdown(); - } - } - } - - private void sendSetEncodings() { - queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY)); - } - - private void sendSetPixelFormat() { - if (!screen.isRGB888_32_LE()) { - queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0)); - } - } - - public void closeConnection() { - connectionAlive = false; - } - - public void requestFullScreenUpdate() { - queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight())); - updateRequestSent = true; - } - - @Override - public void imagePaintedOnScreen() { - if (!updateRequestSent) { - queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight())); - updateRequestSent = true; - } - } - - @Override - public void frameBufferPacketReceived() { - updateRequestSent = false; - } - - @Override - public void mouseDragged(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseMoved(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseClicked(MouseEvent e) { - // Nothing to do - } - - @Override - public void mousePressed(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseReleased(MouseEvent e) { - queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); - } - - @Override - public void mouseEntered(MouseEvent e) { - // Nothing to do - } - - @Override - public void mouseExited(MouseEvent e) { - // Nothing to do - } - - /** - * Current state of buttons 1 to 8 are represented by bits 0 to 7 of - * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a - * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and - * right buttons on the mouse. On a wheel mouse, each step of the wheel - * upwards is represented by a press and release of button 4, and each step - * downwards is represented by a press and release of button 5. - * - * @param modifiers - * extended modifiers from AWT mouse event - * @return VNC mouse button mask - */ - public static int mapAwtModifiersToVncButtonMask(int modifiers) { - int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) - | (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0); - return mask; - } - - @Override - public void keyTyped(KeyEvent e) { - // Do nothing - } - - @Override - public void keyPressed(KeyEvent e) { - ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode())); - queue.add(request); - } - - @Override - public void keyReleased(KeyEvent e) { - ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode())); - queue.add(request); - } - - private int mapAwtKeyToVncKey(int key) { - switch (key) { - case KeyEvent.VK_BACK_SPACE: - return 0xff08; - case KeyEvent.VK_TAB: - return 0xff09; - case KeyEvent.VK_ENTER: - return 0xff0d; - case KeyEvent.VK_ESCAPE: - return 0xff1b; - case KeyEvent.VK_INSERT: - return 0xff63; - case KeyEvent.VK_DELETE: - return 0xffff; - case KeyEvent.VK_HOME: - return 0xff50; - case KeyEvent.VK_END: - return 0xff57; - case KeyEvent.VK_PAGE_UP: - return 0xff55; - case KeyEvent.VK_PAGE_DOWN: - return 0xff56; - case KeyEvent.VK_LEFT: - return 0xff51; - case KeyEvent.VK_UP: - return 0xff52; - case KeyEvent.VK_RIGHT: - return 0xff53; - case KeyEvent.VK_DOWN: - return 0xff54; - case KeyEvent.VK_F1: - return 0xffbe; - case KeyEvent.VK_F2: - return 0xffbf; - case KeyEvent.VK_F3: - return 0xffc0; - case KeyEvent.VK_F4: - return 0xffc1; - case KeyEvent.VK_F5: - return 0xffc2; - case KeyEvent.VK_F6: - return 0xffc3; - case KeyEvent.VK_F7: - return 0xffc4; - case KeyEvent.VK_F8: - return 0xffc5; - case KeyEvent.VK_F9: - return 0xffc6; - case KeyEvent.VK_F10: - return 0xffc7; - case KeyEvent.VK_F11: - return 0xffc8; - case KeyEvent.VK_F12: - return 0xffc9; - case KeyEvent.VK_SHIFT: - return 0xffe1; - case KeyEvent.VK_CONTROL: - return 0xffe3; - case KeyEvent.VK_META: - return 0xffe7; - case KeyEvent.VK_ALT: - return 0xffe9; - case KeyEvent.VK_ALT_GRAPH: - return 0xffea; - case KeyEvent.VK_BACK_QUOTE: - return 0x0060; - } - - return key; - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java deleted file mode 100644 index 44b2a34769b..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -/** - * VncScreenDescription - contains information about remote VNC screen. - */ -public class VncScreenDescription { - - // Frame buffer size - private int framebufferWidth = -1; - private int framebufferHeight = -1; - - // Desktop name - private String desktopName; - - // Bytes per pixel - private int bytesPerPixel; - - // Indicates that screen uses format which we want to use: - // RGB 24bit packed into 32bit little-endian int. - private boolean rgb888_32_le = false; - - public VncScreenDescription() { - } - - /** - * Store information about server pixel format. - */ - public void setPixelFormat(int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift, int blueShift) { - - bytesPerPixel = (bitsPerPixel + 7) / 8; - - rgb888_32_le = (depth == 24 && bitsPerPixel == 32 && redShift == 16 && greenShift == 8 && blueShift == 0 && redMax == 255 && greenMax == 255 && blueMax == 255 - && bigEndianFlag == RfbConstants.LITTLE_ENDIAN && trueColorFlag == RfbConstants.TRUE_COLOR); - } - - /** - * Store information about server screen size. - */ - public void setFramebufferSize(int framebufferWidth, int framebufferHeight) { - this.framebufferWidth = framebufferWidth; - this.framebufferHeight = framebufferHeight; - } - - /** - * Store server desktop name. - */ - public void setDesktopName(String desktopName) { - this.desktopName = desktopName; - } - - // Getters for variables, as usual - - public String getDesktopName() { - return desktopName; - } - - public int getBytesPerPixel() { - return bytesPerPixel; - } - - public int getFramebufferHeight() { - return framebufferHeight; - } - - public int getFramebufferWidth() { - return framebufferWidth; - } - - public boolean isRGB888_32_LE() { - return rgb888_32_le; - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java b/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java deleted file mode 100644 index 57c8ff8efe2..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc; - -import java.awt.Toolkit; -import java.awt.datatransfer.StringSelection; -import java.io.DataInputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.ConsoleProxyClientListener; -import com.cloud.consoleproxy.util.Logger; -import com.cloud.consoleproxy.vnc.packet.server.FramebufferUpdatePacket; -import com.cloud.consoleproxy.vnc.packet.server.ServerCutText; - -public class VncServerPacketReceiver implements Runnable { - private static final Logger s_logger = Logger.getLogger(VncServerPacketReceiver.class); - - private final VncScreenDescription screen; - private BufferedImageCanvas canvas; - private DataInputStream is; - - private boolean connectionAlive = true; - private VncClient vncConnection; - private final FrameBufferUpdateListener fburListener; - private final ConsoleProxyClientListener clientListener; - - public VncServerPacketReceiver(DataInputStream is, BufferedImageCanvas canvas, VncScreenDescription screen, VncClient vncConnection, FrameBufferUpdateListener fburListener, - ConsoleProxyClientListener clientListener) { - this.screen = screen; - this.canvas = canvas; - this.is = is; - this.vncConnection = vncConnection; - this.fburListener = fburListener; - this.clientListener = clientListener; - } - - public BufferedImageCanvas getCanvas() { - return canvas; - } - - @Override - public void run() { - try { - while (connectionAlive) { - - // Read server message type - int messageType = is.readUnsignedByte(); - - // Invoke packet handler by packet type. - switch (messageType) { - - case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: { - // Notify sender that frame buffer update is received, - // so it can send another frame buffer update request - fburListener.frameBufferPacketReceived(); - // Handle frame buffer update - new FramebufferUpdatePacket(canvas, screen, is, clientListener); - break; - } - - case RfbConstants.SERVER_BELL: { - serverBell(); - break; - } - - case RfbConstants.SERVER_CUT_TEXT: { - serverCutText(is); - break; - } - - default: - throw new RuntimeException("Unknown server packet type: " + messageType + "."); - } - } - } catch (Throwable e) { - s_logger.error("Unexpected exception: ", e); - if (connectionAlive) { - closeConnection(); - vncConnection.shutdown(); - } - } - } - - public void closeConnection() { - connectionAlive = false; - } - - public boolean isConnectionAlive() { - return connectionAlive; - } - - /** - * Handle server bell packet. - */ - private void serverBell() { - Toolkit.getDefaultToolkit().beep(); - } - - /** - * Handle packet with server clip-board. - */ - private void serverCutText(DataInputStream is) throws IOException { - ServerCutText clipboardContent = new ServerCutText(is); - StringSelection contents = new StringSelection(clipboardContent.getContent()); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null); - - s_logger.info("Server clipboard buffer: " + clipboardContent.getContent()); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java deleted file mode 100644 index 873b8c0825e..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.client; - -import java.io.DataOutputStream; -import java.io.IOException; - -public interface ClientPacket { - - void write(DataOutputStream os) throws IOException; - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java deleted file mode 100644 index d3a6e40e961..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.client; - -import java.io.DataOutputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.vnc.RfbConstants; - -/** - * FramebufferUpdateRequestPacket - * - * @author Volodymyr M. Lisivka - */ -public class FramebufferUpdateRequestPacket implements ClientPacket { - - private final int incremental; - private final int x, y, width, height; - - public FramebufferUpdateRequestPacket(int incremental, int x, int y, int width, int height) { - this.incremental = incremental; - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST); - - os.writeByte(incremental); - os.writeShort(x); - os.writeShort(y); - os.writeShort(width); - os.writeShort(height); - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java deleted file mode 100644 index 8efbab123a5..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.client; - -import java.io.DataOutputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.vnc.RfbConstants; - -public class KeyboardEventPacket implements ClientPacket { - - private final int downFlag, key; - - public KeyboardEventPacket(int downFlag, int key) { - this.downFlag = downFlag; - this.key = key; - } - - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT); - - os.writeByte(downFlag); - os.writeShort(0); // padding - os.writeInt(key); - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java deleted file mode 100644 index b42191cbadc..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.client; - -import java.io.DataOutputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.vnc.RfbConstants; - -public class MouseEventPacket implements ClientPacket { - - private final int buttonMask, x, y; - - public MouseEventPacket(int buttonMask, int x, int y) { - this.buttonMask = buttonMask; - this.x = x; - this.y = y; - } - - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_POINTER_EVENT); - - os.writeByte(buttonMask); - os.writeShort(x); - os.writeShort(y); - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java deleted file mode 100644 index 3d8cfcb09a6..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.client; - -import java.io.DataOutputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.vnc.RfbConstants; - -public class SetEncodingsPacket implements ClientPacket { - - private final int[] encodings; - - public SetEncodingsPacket(int[] encodings) { - this.encodings = encodings; - } - - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_SET_ENCODINGS); - - os.writeByte(0);// padding - - os.writeShort(encodings.length); - - for (int i = 0; i < encodings.length; i++) { - os.writeInt(encodings[i]); - } - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java deleted file mode 100644 index 7a25bfab9f3..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.client; - -import java.io.DataOutputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.vnc.RfbConstants; -import com.cloud.consoleproxy.vnc.VncScreenDescription; - -public class SetPixelFormatPacket implements ClientPacket { - - private final int bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift; - - private final VncScreenDescription screen; - - public SetPixelFormatPacket(VncScreenDescription screen, int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift, - int blueShift) { - this.screen = screen; - this.bitsPerPixel = bitsPerPixel; - this.depth = depth; - this.bigEndianFlag = bigEndianFlag; - this.trueColourFlag = trueColorFlag; - this.redMax = redMax; - this.greenMax = greenMax; - this.blueMax = blueMax; - this.redShift = redShift; - this.greenShift = greenShift; - this.blueShift = blueShift; - } - - @Override - public void write(DataOutputStream os) throws IOException { - os.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT); - - // Padding - os.writeByte(0); - os.writeByte(0); - os.writeByte(0); - - // Send pixel format - os.writeByte(bitsPerPixel); - os.writeByte(depth); - os.writeByte(bigEndianFlag); - os.writeByte(trueColourFlag); - os.writeShort(redMax); - os.writeShort(greenMax); - os.writeShort(blueMax); - os.writeByte(redShift); - os.writeByte(greenShift); - os.writeByte(blueShift); - - // Padding - os.writeByte(0); - os.writeByte(0); - os.writeByte(0); - - screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift); - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java deleted file mode 100644 index a380e9cd62a..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -public abstract class AbstractRect implements Rect { - - protected final int x; - protected final int y; - protected final int width; - protected final int height; - - public AbstractRect(int x, int y, int width, int height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - @Override - public int getX() { - return x; - } - - @Override - public int getY() { - return y; - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getHeight() { - return height; - } - -} \ No newline at end of file diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java deleted file mode 100644 index caaecb58864..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.io.DataInputStream; -import java.io.IOException; - -public class CopyRect extends AbstractRect { - - private final int srcX, srcY; - - public CopyRect(int x, int y, int width, int height, DataInputStream is) throws IOException { - super(x, y, width, height); - - srcX = is.readUnsignedShort(); - srcY = is.readUnsignedShort(); - } - - @Override - public void paint(BufferedImage image, Graphics2D graphics) { - graphics.copyArea(srcX, srcY, width, height, x - srcX, y - srcY); - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java deleted file mode 100644 index 18f6987eab4..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - -import com.cloud.consoleproxy.vnc.BufferedImageCanvas; - -public class FrameBufferSizeChangeRequest extends AbstractRect { - - private final BufferedImageCanvas canvas; - - public FrameBufferSizeChangeRequest(BufferedImageCanvas canvas, int width, int height) { - super(0, 0, width, height); - this.canvas = canvas; - canvas.setCanvasSize(width, height); - } - - @Override - public void paint(BufferedImage offlineImage, Graphics2D graphics) { - canvas.setCanvasSize(width, height); - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java deleted file mode 100644 index 3bc43fdded1..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -import java.io.DataInputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.ConsoleProxyClientListener; -import com.cloud.consoleproxy.vnc.BufferedImageCanvas; -import com.cloud.consoleproxy.vnc.RfbConstants; -import com.cloud.consoleproxy.vnc.VncScreenDescription; -import com.cloud.consoleproxy.vnc.packet.server.CopyRect; -import com.cloud.consoleproxy.vnc.packet.server.RawRect; -import com.cloud.consoleproxy.vnc.packet.server.Rect; - -public class FramebufferUpdatePacket { - - private final VncScreenDescription screen; - private final BufferedImageCanvas canvas; - private final ConsoleProxyClientListener clientListener; - - public FramebufferUpdatePacket(BufferedImageCanvas canvas, VncScreenDescription screen, DataInputStream is, ConsoleProxyClientListener clientListener) throws IOException { - - this.screen = screen; - this.canvas = canvas; - this.clientListener = clientListener; - readPacketData(is); - } - - private void readPacketData(DataInputStream is) throws IOException { - is.skipBytes(1);// Skip padding - - // Read number of rectangles - int numberOfRectangles = is.readUnsignedShort(); - - // For all rectangles - for (int i = 0; i < numberOfRectangles; i++) { - - // Read coordinate of rectangle - int x = is.readUnsignedShort(); - int y = is.readUnsignedShort(); - int width = is.readUnsignedShort(); - int height = is.readUnsignedShort(); - - int encodingType = is.readInt(); - - // Process rectangle - Rect rect; - switch (encodingType) { - - case RfbConstants.ENCODING_RAW: { - rect = new RawRect(screen, x, y, width, height, is); - break; - } - - case RfbConstants.ENCODING_COPY_RECT: { - rect = new CopyRect(x, y, width, height, is); - break; - } - - case RfbConstants.ENCODING_DESKTOP_SIZE: { - rect = new FrameBufferSizeChangeRequest(canvas, width, height); - if (this.clientListener != null) - this.clientListener.onFramebufferSizeChange(width, height); - break; - } - - default: - throw new RuntimeException("Unsupported ecnoding: " + encodingType); - } - - paint(rect, canvas); - - if (this.clientListener != null) - this.clientListener.onFramebufferUpdate(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); - } - - } - - public void paint(Rect rect, BufferedImageCanvas canvas) { - // Draw rectangle on offline buffer - rect.paint(canvas.getOfflineImage(), canvas.getOfflineGraphics()); - - // Request update of repainted area - canvas.repaint(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); - } - -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java deleted file mode 100644 index 978a4c2755d..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferInt; -import java.io.DataInputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.vnc.VncScreenDescription; - -public class RawRect extends AbstractRect { - private final int[] buf; - - public RawRect(VncScreenDescription screen, int x, int y, int width, int height, DataInputStream is) throws IOException { - super(x, y, width, height); - - byte[] bbuf = new byte[width * height * screen.getBytesPerPixel()]; - is.readFully(bbuf); - - // Convert array of bytes to array of int - int size = width * height; - buf = new int[size]; - for (int i = 0, j = 0; i < size; i++, j += 4) { - buf[i] = (bbuf[j + 0] & 0xFF) | ((bbuf[j + 1] & 0xFF) << 8) | ((bbuf[j + 2] & 0xFF) << 16) | ((bbuf[j + 3] & 0xFF) << 24); - } - - } - - @Override - public void paint(BufferedImage image, Graphics2D graphics) { - - DataBuffer dataBuf = image.getRaster().getDataBuffer(); - - switch (dataBuf.getDataType()) { - - case DataBuffer.TYPE_INT: { - // We chose RGB888 model, so Raster will use DataBufferInt type - DataBufferInt dataBuffer = (DataBufferInt) dataBuf; - - int imageWidth = image.getWidth(); - int imageHeight = image.getHeight(); - - // Paint rectangle directly on buffer, line by line - int[] imageBuffer = dataBuffer.getData(); - for (int srcLine = 0, dstLine = y; srcLine < height && dstLine < imageHeight; srcLine++, dstLine++) { - try { - System.arraycopy(buf, srcLine * width, imageBuffer, x + dstLine * imageWidth, width); - } catch (IndexOutOfBoundsException e) { - } - } - break; - } - - default: - throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: " + dataBuf.getClass().getSimpleName()); - } - } -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java deleted file mode 100644 index 51edf12d0d6..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - -public interface Rect { - - void paint(BufferedImage offlineImage, Graphics2D graphics); - - int getX(); - - int getY(); - - int getWidth(); - - int getHeight(); -} diff --git a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java b/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java deleted file mode 100644 index 044f9589b23..00000000000 --- a/console-proxy/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.consoleproxy.vnc.packet.server; - -import java.io.DataInputStream; -import java.io.IOException; - -import com.cloud.consoleproxy.util.Logger; -import com.cloud.consoleproxy.vnc.RfbConstants; - -public class ServerCutText { - private static final Logger s_logger = Logger.getLogger(ServerCutText.class); - - private String content; - - public String getContent() { - return content; - } - - public ServerCutText(DataInputStream is) throws IOException { - readPacketData(is); - } - - private void readPacketData(DataInputStream is) throws IOException { - is.skipBytes(3);// Skip padding - int length = is.readInt(); - byte buf[] = new byte[length]; - is.readFully(buf); - - content = new String(buf, RfbConstants.CHARSET); - - /* LOG */s_logger.info("Clippboard content: " + content); - } - -} diff --git a/console-proxy/systemvm-descriptor.xml b/console-proxy/systemvm-descriptor.xml deleted file mode 100644 index 7efe7fdfcb0..00000000000 --- a/console-proxy/systemvm-descriptor.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - systemvm - - zip - - false - - - - - - - - ../scripts/storage/secondary/ - scripts/storage/secondary - 555 - 555 - - - ../scripts/storage/secondary/ - scripts/storage/secondary - 555 - 555 - - - scripts - - 555 - 555 - - - conf - conf - 555 - 555 - - log4j-cloud.xml - consoleproxy.properties - agent.properties - - - - ../console-proxy/images - images - 555 - 555 - - *.jpg - *.gif - *.png - *.cur - - - - ../console-proxy/js - js - 555 - 555 - - *.js - - - - ../console-proxy/ui - ui - 555 - 555 - - *.ftl - - - - ../console-proxy/css - css - 555 - 555 - - *.css - - - - ../console-proxy/certs - certs - 555 - 555 - - *.keystore - *.crt - *.key - - - - diff --git a/console-proxy/ui/viewer-bad-sid.ftl b/console-proxy/ui/viewer-bad-sid.ftl deleted file mode 100644 index 2f30ec36015..00000000000 --- a/console-proxy/ui/viewer-bad-sid.ftl +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - -
-

Unable to start console session as access is denied because of bad sid

-
- - - diff --git a/console-proxy/ui/viewer-connect-failed.ftl b/console-proxy/ui/viewer-connect-failed.ftl deleted file mode 100644 index 9d907cacb43..00000000000 --- a/console-proxy/ui/viewer-connect-failed.ftl +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - -
-

Unable to start console session as connection is refused by the machine you are accessing

-
- - - diff --git a/console-proxy/ui/viewer-update.ftl b/console-proxy/ui/viewer-update.ftl deleted file mode 100644 index 6bf9ab35ce7..00000000000 --- a/console-proxy/ui/viewer-update.ftl +++ /dev/null @@ -1,24 +0,0 @@ -<#-- -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. ---> -tileMap = [ ${tileSequence} ]; -<#if resized == true> - ajaxViewer.resize('main_panel', ${width}, ${height}, ${tileWidth}, ${tileHeight}); - -ajaxViewer.refresh('${imgUrl}', tileMap, false); - diff --git a/console-proxy/ui/viewer.ftl b/console-proxy/ui/viewer.ftl deleted file mode 100644 index 62de193cf7b..00000000000 --- a/console-proxy/ui/viewer.ftl +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - -${title} - - - - -
- - - - - diff --git a/console-proxy/vm-script/vmops b/console-proxy/vm-script/vmops deleted file mode 100644 index a9f70c83925..00000000000 --- a/console-proxy/vm-script/vmops +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash -# -# vmops Script to start and stop the VMOps Agent. -# -# Author: Chiradeep Vittal -# chkconfig: 2345 99 01 -# description: Start up the VMOps agent - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -# Source function library. -if [ -f /etc/init.d/functions ] -then - . /etc/init.d/functions -fi - -_success() { - if [ -f /etc/init.d/functions ] - then - success - else - echo "Success" - fi -} - -_failure() { - if [ -f /etc/init.d/functions ] - then - failure - else - echo "Failed" - fi -} -RETVAL=$? -VMOPS_HOME="/usr/local/vmops" - -mkdir -p /var/log/vmops - -get_pids() { - local i - for i in $(ps -ef| grep java | grep -v grep | awk '{print $2}'); - do - echo $(pwdx $i) | grep "$VMOPS_HOME" | grep -i console | awk -F: '{print $1}'; - done -} - -start() { - local pid=$(get_pids) - echo -n "Starting VMOps Console Proxy: " - if [ -f $VMOPS_HOME/consoleproxy/run.sh ]; - then - if [ "$pid" == "" ] - then - (cd $VMOPS_HOME/consoleproxy; nohup ./run.sh > /var/log/vmops/vmops.out 2>&1 & ) - pid=$(get_pids) - echo $pid > /var/run/vmops.pid - fi - _success - else - _failure - fi - echo -} - -stop() { - local pid - echo -n "Stopping VMOps agent: " - for pid in $(get_pids) - do - kill $pid - done - _success - echo -} - -status() { - local pids=$(get_pids) - if [ "$pids" == "" ] - then - echo "VMOps agent is not running" - return 1 - fi - echo "VMOps agent is running: process id: $pids" - return 0 -} - - -case "$1" in - start) start - ;; - stop) stop - ;; - status) status - ;; - restart) stop - start - ;; - *) echo $"Usage: $0 {start|stop|status|restart}" - exit 1 - ;; -esac - -exit $RETVAL diff --git a/pom.xml b/pom.xml index 9a077692d4f..f7aaf95f5d1 100644 --- a/pom.xml +++ b/pom.xml @@ -156,7 +156,6 @@ api agent - console-proxy core server usage @@ -168,6 +167,7 @@ test engine framework + services From 4869f0cacfc1319ac725e35ac4ebd1b59da8e5e1 Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Tue, 12 Feb 2013 15:59:02 -0800 Subject: [PATCH 22/42] Moved console-proxy into services --- services/console-proxy/plugin/pom.xml | 35 + services/console-proxy/pom.xml | 37 + .../bindir/cloud-setup-console-proxy.in | 220 +++ .../console-proxy/server/certs/localhost.crt | 22 + .../console-proxy/server/certs/localhost.key | 27 + .../console-proxy/server/certs/realhostip.crt | 31 + .../console-proxy/server/certs/realhostip.csr | 15 + .../console-proxy/server/certs/realhostip.key | 24 + .../server/certs/realhostip.keystore | Bin 0 -> 8690 bytes .../server/conf.dom0/agent.properties.in | 46 + .../conf.dom0/consoleproxy.properties.in | 23 + .../server/conf.dom0/log4j-cloud.xml.in | 101 ++ .../server/conf/agent.properties | 19 + .../server/conf/consoleproxy.properties | 23 + .../console-proxy/server/conf/log4j-cloud.xml | 102 ++ .../console-proxy/server/css/ajaxviewer.css | 144 ++ services/console-proxy/server/css/logger.css | 139 ++ .../rc.d/init.d/cloud-console-proxy.in | 98 ++ .../rc.d/init.d/cloud-console-proxy.in | 98 ++ .../rc.d/init.d/cloud-console-proxy.in | 98 ++ .../SYSCONFDIR/init.d/cloud-console-proxy.in | 112 ++ services/console-proxy/server/images/back.gif | Bin 0 -> 149 bytes .../server/images/bright-green.png | Bin 0 -> 3903 bytes services/console-proxy/server/images/cad.gif | Bin 0 -> 918 bytes .../server/images/cannotconnect.jpg | Bin 0 -> 1810 bytes .../server/images/clr_button.gif | Bin 0 -> 1274 bytes .../server/images/clr_button_hover.gif | Bin 0 -> 437 bytes services/console-proxy/server/images/dot.cur | Bin 0 -> 326 bytes .../server/images/gray-green.png | Bin 0 -> 3833 bytes .../server/images/grid_headerbg.gif | Bin 0 -> 196 bytes services/console-proxy/server/images/left.png | Bin 0 -> 3024 bytes .../server/images/minimize_button.gif | Bin 0 -> 634 bytes .../server/images/minimize_button_hover.gif | Bin 0 -> 227 bytes .../console-proxy/server/images/notready.jpg | Bin 0 -> 1827 bytes .../server/images/play_button.gif | Bin 0 -> 657 bytes .../server/images/play_button_hover.gif | Bin 0 -> 243 bytes .../console-proxy/server/images/right.png | Bin 0 -> 3131 bytes .../console-proxy/server/images/right2.png | Bin 0 -> 3156 bytes .../server/images/shrink_button.gif | Bin 0 -> 655 bytes .../server/images/shrink_button_hover.gif | Bin 0 -> 243 bytes .../server/images/stop_button.gif | Bin 0 -> 649 bytes .../server/images/stop_button_hover.gif | Bin 0 -> 231 bytes .../console-proxy/server/images/winlog.png | Bin 0 -> 2629 bytes services/console-proxy/server/js/ajaxkeys.js | 77 + .../console-proxy/server/js/ajaxviewer.js | 1444 +++++++++++++++++ .../console-proxy/server/js/cloud.logger.js | 338 ++++ services/console-proxy/server/js/handler.js | 72 + services/console-proxy/server/js/jquery.js | 19 + .../server/libexec/console-proxy-runner.in | 90 + services/console-proxy/server/pom.xml | 247 +++ services/console-proxy/server/scripts/_run.sh | 63 + .../server/scripts/config_auth.sh | 69 + .../server/scripts/config_ssl.sh | 174 ++ .../server/scripts/ipfirewall.sh | 50 + .../console-proxy/server/scripts/run-proxy.sh | 48 + services/console-proxy/server/scripts/run.bat | 18 + services/console-proxy/server/scripts/run.sh | 45 + .../server/scripts/ssvm-check.sh | 136 ++ .../consoleproxy/AjaxFIFOImageCache.java | 83 + .../consoleproxy/AuthenticationException.java | 33 + .../com/cloud/consoleproxy/ConsoleProxy.java | 499 ++++++ .../consoleproxy/ConsoleProxyAjaxHandler.java | 406 +++++ .../ConsoleProxyAjaxImageHandler.java | 159 ++ .../ConsoleProxyAuthenticationResult.java | 81 + .../ConsoleProxyBaseServerFactoryImpl.java | 48 + .../consoleproxy/ConsoleProxyClient.java | 69 + .../consoleproxy/ConsoleProxyClientBase.java | 457 ++++++ .../ConsoleProxyClientListener.java | 25 + .../consoleproxy/ConsoleProxyClientParam.java | 110 ++ .../ConsoleProxyClientStatsCollector.java | 88 + .../consoleproxy/ConsoleProxyCmdHandler.java | 70 + .../consoleproxy/ConsoleProxyGCThread.java | 109 ++ .../ConsoleProxyHttpHandlerHelper.java | 74 + .../ConsoleProxyLoggerFactory.java | 89 + .../consoleproxy/ConsoleProxyMonitor.java | 153 ++ .../ConsoleProxyPasswordBasedEncryptor.java | 142 ++ .../ConsoleProxyResourceHandler.java | 181 +++ .../ConsoleProxySecureServerFactoryImpl.java | 145 ++ .../ConsoleProxyServerFactory.java | 29 + .../ConsoleProxyThumbnailHandler.java | 212 +++ .../consoleproxy/ConsoleProxyVncClient.java | 235 +++ .../cloud/consoleproxy/InputEventType.java | 58 + .../consoleproxy/util/ITileScanListener.java | 25 + .../cloud/consoleproxy/util/ImageHelper.java | 32 + .../com/cloud/consoleproxy/util/Logger.java | 223 +++ .../consoleproxy/util/LoggerFactory.java | 21 + .../com/cloud/consoleproxy/util/RawHTTP.java | 249 +++ .../com/cloud/consoleproxy/util/Region.java | 90 + .../consoleproxy/util/RegionClassifier.java | 58 + .../com/cloud/consoleproxy/util/TileInfo.java | 55 + .../cloud/consoleproxy/util/TileTracker.java | 269 +++ .../consoleproxy/vnc/BufferedImageCanvas.java | 150 ++ .../consoleproxy/vnc/FrameBufferCanvas.java | 30 + .../vnc/FrameBufferUpdateListener.java | 26 + .../vnc/PaintNotificationListener.java | 27 + .../cloud/consoleproxy/vnc/RfbConstants.java | 82 + .../com/cloud/consoleproxy/vnc/VncClient.java | 451 +++++ .../vnc/VncClientPacketSender.java | 258 +++ .../vnc/VncScreenDescription.java | 89 + .../vnc/VncServerPacketReceiver.java | 123 ++ .../vnc/packet/client/ClientPacket.java | 26 + .../FramebufferUpdateRequestPacket.java | 53 + .../packet/client/KeyboardEventPacket.java | 42 + .../vnc/packet/client/MouseEventPacket.java | 43 + .../vnc/packet/client/SetEncodingsPacket.java | 45 + .../packet/client/SetPixelFormatPacket.java | 75 + .../vnc/packet/server/AbstractRect.java | 53 + .../vnc/packet/server/CopyRect.java | 39 + .../server/FrameBufferSizeChangeRequest.java | 39 + .../server/FramebufferUpdatePacket.java | 102 ++ .../vnc/packet/server/RawRect.java | 75 + .../consoleproxy/vnc/packet/server/Rect.java | 33 + .../vnc/packet/server/ServerCutText.java | 49 + .../server/systemvm-descriptor.xml | 113 ++ .../server/ui/viewer-bad-sid.ftl | 29 + .../server/ui/viewer-connect-failed.ftl | 29 + .../console-proxy/server/ui/viewer-update.ftl | 24 + services/console-proxy/server/ui/viewer.ftl | 60 + services/console-proxy/server/vm-script/vmops | 119 ++ services/pom.xml | 36 + 120 files changed, 11201 insertions(+) create mode 100644 services/console-proxy/plugin/pom.xml create mode 100644 services/console-proxy/pom.xml create mode 100755 services/console-proxy/server/bindir/cloud-setup-console-proxy.in create mode 100644 services/console-proxy/server/certs/localhost.crt create mode 100644 services/console-proxy/server/certs/localhost.key create mode 100644 services/console-proxy/server/certs/realhostip.crt create mode 100644 services/console-proxy/server/certs/realhostip.csr create mode 100644 services/console-proxy/server/certs/realhostip.key create mode 100644 services/console-proxy/server/certs/realhostip.keystore create mode 100644 services/console-proxy/server/conf.dom0/agent.properties.in create mode 100644 services/console-proxy/server/conf.dom0/consoleproxy.properties.in create mode 100644 services/console-proxy/server/conf.dom0/log4j-cloud.xml.in create mode 100644 services/console-proxy/server/conf/agent.properties create mode 100644 services/console-proxy/server/conf/consoleproxy.properties create mode 100644 services/console-proxy/server/conf/log4j-cloud.xml create mode 100644 services/console-proxy/server/css/ajaxviewer.css create mode 100644 services/console-proxy/server/css/logger.css create mode 100644 services/console-proxy/server/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in create mode 100644 services/console-proxy/server/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in create mode 100644 services/console-proxy/server/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in create mode 100755 services/console-proxy/server/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in create mode 100644 services/console-proxy/server/images/back.gif create mode 100644 services/console-proxy/server/images/bright-green.png create mode 100644 services/console-proxy/server/images/cad.gif create mode 100644 services/console-proxy/server/images/cannotconnect.jpg create mode 100644 services/console-proxy/server/images/clr_button.gif create mode 100644 services/console-proxy/server/images/clr_button_hover.gif create mode 100644 services/console-proxy/server/images/dot.cur create mode 100644 services/console-proxy/server/images/gray-green.png create mode 100644 services/console-proxy/server/images/grid_headerbg.gif create mode 100644 services/console-proxy/server/images/left.png create mode 100644 services/console-proxy/server/images/minimize_button.gif create mode 100644 services/console-proxy/server/images/minimize_button_hover.gif create mode 100644 services/console-proxy/server/images/notready.jpg create mode 100644 services/console-proxy/server/images/play_button.gif create mode 100644 services/console-proxy/server/images/play_button_hover.gif create mode 100644 services/console-proxy/server/images/right.png create mode 100644 services/console-proxy/server/images/right2.png create mode 100644 services/console-proxy/server/images/shrink_button.gif create mode 100644 services/console-proxy/server/images/shrink_button_hover.gif create mode 100644 services/console-proxy/server/images/stop_button.gif create mode 100644 services/console-proxy/server/images/stop_button_hover.gif create mode 100644 services/console-proxy/server/images/winlog.png create mode 100644 services/console-proxy/server/js/ajaxkeys.js create mode 100644 services/console-proxy/server/js/ajaxviewer.js create mode 100644 services/console-proxy/server/js/cloud.logger.js create mode 100644 services/console-proxy/server/js/handler.js create mode 100644 services/console-proxy/server/js/jquery.js create mode 100755 services/console-proxy/server/libexec/console-proxy-runner.in create mode 100644 services/console-proxy/server/pom.xml create mode 100755 services/console-proxy/server/scripts/_run.sh create mode 100755 services/console-proxy/server/scripts/config_auth.sh create mode 100755 services/console-proxy/server/scripts/config_ssl.sh create mode 100755 services/console-proxy/server/scripts/ipfirewall.sh create mode 100644 services/console-proxy/server/scripts/run-proxy.sh create mode 100644 services/console-proxy/server/scripts/run.bat create mode 100755 services/console-proxy/server/scripts/run.sh create mode 100644 services/console-proxy/server/scripts/ssvm-check.sh create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/InputEventType.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/ITileScanListener.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/ImageHelper.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/Logger.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/LoggerFactory.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/RawHTTP.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/Region.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/RegionClassifier.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/TileInfo.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/util/TileTracker.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/RfbConstants.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClient.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java create mode 100644 services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java create mode 100644 services/console-proxy/server/systemvm-descriptor.xml create mode 100644 services/console-proxy/server/ui/viewer-bad-sid.ftl create mode 100644 services/console-proxy/server/ui/viewer-connect-failed.ftl create mode 100644 services/console-proxy/server/ui/viewer-update.ftl create mode 100644 services/console-proxy/server/ui/viewer.ftl create mode 100644 services/console-proxy/server/vm-script/vmops create mode 100644 services/pom.xml diff --git a/services/console-proxy/plugin/pom.xml b/services/console-proxy/plugin/pom.xml new file mode 100644 index 00000000000..8cf3d76aa00 --- /dev/null +++ b/services/console-proxy/plugin/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + cloud-plugin-console-proxy + Apache CloudStack Console Proxy Plugin + pom + + org.apache.cloudstack + cloud-service-console-proxy + 4.1.0-SNAPSHOT + ../pom.xml + + + install + src + test + + diff --git a/services/console-proxy/pom.xml b/services/console-proxy/pom.xml new file mode 100644 index 00000000000..cd57526273d --- /dev/null +++ b/services/console-proxy/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + cloud-service-console-proxy + Apache CloudStack Console Proxy Service + pom + + org.apache.cloudstack + cloud-services + 4.1.0-SNAPSHOT + ../pom.xml + + + install + + + plugin + server + + diff --git a/services/console-proxy/server/bindir/cloud-setup-console-proxy.in b/services/console-proxy/server/bindir/cloud-setup-console-proxy.in new file mode 100755 index 00000000000..6439c0fc329 --- /dev/null +++ b/services/console-proxy/server/bindir/cloud-setup-console-proxy.in @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +import sys, os, subprocess, errno, re, getopt + +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- +import cloud_utils +from cloud_utils import stderr + +E_GENERIC= 1 +E_NOKVM = 2 +E_NODEFROUTE = 3 +E_DHCP = 4 +E_NOPERSISTENTNET = 5 +E_NETRECONFIGFAILED = 6 +E_VIRTRECONFIGFAILED = 7 +E_FWRECONFIGFAILED = 8 +E_CPRECONFIGFAILED = 9 +E_CPFAILEDTOSTART = 10 +E_NOFQDN = 11 + +def bail(errno=E_GENERIC,message=None,*args): + if message: stderr(message,*args) + stderr("Cloud Console Proxy setup aborted") + sys.exit(errno) + + +#---------------- boilerplate for python 2.4 support + + +# CENTOS does not have this -- we have to put this here +try: + from subprocess import check_call + from subprocess import CalledProcessError +except ImportError: + def check_call(*popenargs, **kwargs): + import subprocess + retcode = subprocess.call(*popenargs, **kwargs) + cmd = kwargs.get("args") + if cmd is None: cmd = popenargs[0] + if retcode: raise CalledProcessError(retcode, cmd) + return retcode + + class CalledProcessError(Exception): + def __init__(self, returncode, cmd): + self.returncode = returncode ; self.cmd = cmd + def __str__(self): return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + +# ------------ end boilerplate ------------------------- + +def check_hostname(): return check_call(["hostname",'--fqdn']) + +class Command: + def __init__(self,name,parent=None): + self.__name = name + self.__parent = parent + def __getattr__(self,name): + if name == "_print": name = "print" + return Command(name,self) + def __call__(self,*args): + cmd = self.__get_recursive_name() + list(args) + #print " ",cmd + popen = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + m = popen.communicate() + ret = popen.wait() + if ret: + e = CalledProcessError(ret,cmd) + e.stdout,e.stderr = m + raise e + class CommandOutput: + def __init__(self,stdout,stderr): + self.stdout = stdout + self.stderr = stderr + return CommandOutput(*m) + def __lt__(self,other): + cmd = self.__get_recursive_name() + #print " ",cmd,"<",other + popen = subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + m = popen.communicate(other) + ret = popen.wait() + if ret: + e = CalledProcessError(ret,cmd) + e.stdout,e.stderr = m + raise e + class CommandOutput: + def __init__(self,stdout,stderr): + self.stdout = stdout + self.stderr = stderr + return CommandOutput(*m) + + def __get_recursive_name(self,sep=None): + m = self + l = [] + while m is not None: + l.append(m.__name) + m = m.__parent + l.reverse() + if sep: return sep.join(l) + else: return l + def __str__(self): + return ''%self.__get_recursive_name(sep=" ") + + def __repr__(self): return self.__str__() + +ip = Command("ip") +service = Command("service") +chkconfig = Command("chkconfig") +ufw = Command("ufw") +iptables = Command("iptables") +augtool = Command("augtool") +ifconfig = Command("ifconfig") +uuidgen = Command("uuidgen") + +Fedora = os.path.exists("/etc/fedora-release") +CentOS = os.path.exists("/etc/centos-release") or ( os.path.exists("/etc/redhat-release") and not os.path.exists("/etc/fedora-release") ) + +#--------------- procedure starts here ------------ + +def main(): + # parse cmd line + opts, args = getopt.getopt(sys.argv[1:], "a", ["host=", "zone=", "pod="]) + host=None + zone=None + pod=None + autoMode=False + do_check_kvm = True + for opt, arg in opts: + if opt == "--host": + if arg != "": + host = arg + elif opt == "--zone": + if arg != "": + zone = arg + elif opt == "--pod": + if arg != "": + pod = arg + elif opt == "-a": + autoMode=True + servicename = "@PACKAGE@-console-proxy" + + if autoMode: + cloud_utils.setLogFile("/var/log/cloud/setupConsoleProxy.log") + + stderr("Welcome to the Cloud Console Proxy setup") + stderr("") + + try: + check_hostname() + stderr("The hostname of this machine is properly set up") + except CalledProcessError,e: + bail(E_NOFQDN,"This machine does not have an FQDN (fully-qualified domain name) for a hostname") + + stderr("Stopping the Cloud Console Proxy") + cloud_utils.stop_service(servicename) + stderr("Cloud Console Proxy stopped") + + ports = "8002".split() + if Fedora or CentOS: + try: + o = chkconfig("--list","iptables") + if ":on" in o.stdout and os.path.exists("/etc/sysconfig/iptables"): + stderr("Setting up firewall rules to permit traffic to Cloud services") + service.iptables.start() ; print o.stdout + o.stderr + for p in ports: iptables("-I","INPUT","1","-p","tcp","--dport",p,'-j','ACCEPT') + o = service.iptables.save() ; print o.stdout + o.stderr + except CalledProcessError,e: + print e.stdout+e.stderr + bail(E_FWRECONFIGFAILED,"Firewall rules could not be set") + else: + stderr("Setting up firewall rules to permit traffic to Cloud services") + try: + for p in ports: ufw.allow(p) + stderr("Rules set") + except CalledProcessError,e: + print e.stdout+e.stderr + bail(E_FWRECONFIGFAILED,"Firewall rules could not be set") + + stderr("We are going to enable ufw now. This may disrupt network connectivity and service availability. See the ufw documentation for information on how to manage ufw firewall policies.") + try: + o = ufw.enable < "y\n" ; print o.stdout + o.stderr + except CalledProcessError,e: + print e.stdout+e.stderr + bail(E_FWRECONFIGFAILED,"Firewall could not be enabled") + + cloud_utils.setup_consoleproxy_config("@CPSYSCONFDIR@/agent.properties", host, zone, pod) + stderr("Enabling and starting the Cloud Console Proxy") + cloud_utils.enable_service(servicename) + stderr("Cloud Console Proxy restarted") + +if __name__ == "__main__": + main() + +# FIXMES: 1) nullify networkmanager on ubuntu (asking the user first) and enable the networking service permanently diff --git a/services/console-proxy/server/certs/localhost.crt b/services/console-proxy/server/certs/localhost.crt new file mode 100644 index 00000000000..005d98b8c72 --- /dev/null +++ b/services/console-proxy/server/certs/localhost.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrTCCApWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzESMBAGA1UECgwJQ2xvdWQu +Y29tMRAwDgYDVQQLDAdEZWZhdWx0MRswGQYDVQQDDBJTZWNvbmRhcnlTdG9yYWdl +Vk0wHhcNMTAwNTI3MTgzNjI1WhcNMTMwMjIwMTgzNjI1WjBxMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzESMBAGA1UECgwJQ2xv +dWQuY29tMRAwDgYDVQQLDAdEZWZhdWx0MRswGQYDVQQDDBJTZWNvbmRhcnlTdG9y +YWdlVk0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbhstQ5Gn2gzrk +ZX1es+tuz4rnrcONRHUzyY/UdoT5jiVfmmS9CML/GgSpKzmnEMNZcCSh7G/GKPwD +gBZywpTVD56nYT4ZzK0GjPxcg0a+BvxxA2esQ2/kFBvtdcZ1TNExtjdOqysjK0It +M6U2891wbn+Y9oHqooTA0uaZELTpe/MCg2eBx7A4+u26novHHfOaKEEqtBscpDP8 +0+nQduNQf61haV25Lv2CDqrCIuv/FrNmgQhcUg8e1dFkk4VCsflEDuSYh9PpaD7J +t+oqmNTVw8k6u3JAYJFkcu457uYz0wrED7Cai7Y6gUy7xwmY2SSY/r2mJJHEpSpZ +NhiH47kZAgMBAAGjUDBOMB0GA1UdDgQWBBQ2hUX5Jdhn277SBisACnEABqg52zAf +BgNVHSMEGDAWgBQ2hUX5Jdhn277SBisACnEABqg52zAMBgNVHRMEBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQBAVrkGGDPHDPQdZRgI1+1L87sX5xdNoet9sJUVRtz9 +ZwhGWAmca30cJGlhSFNx5y01E6T7lHDLrF9HCf9nVC10t0FwQwTVYijE8VyM7fAA +4Hv/whSKg1kkQQSTis2ZW0wMU6TnanhJy6MrxwdhRampeXjNOpNtoLrF/5LbhWxO +Gm0S5u+4q7eYnUPD4o3sb17idG62kkejHeToPnJwXtDwyq0XYNlL/OoqRaaY5f0b +IKdeqqEkdtkzfU4N1dG7bJA29gBl48gPn+CSrh9u3D0s1OYM7MWi1/TjpwCR18ir +CslVzO6kVNyQoNEYhZ9+2Sz0ceZVrYDFFzp8qAF8qbZ7 +-----END CERTIFICATE----- diff --git a/services/console-proxy/server/certs/localhost.key b/services/console-proxy/server/certs/localhost.key new file mode 100644 index 00000000000..6d95765e4f0 --- /dev/null +++ b/services/console-proxy/server/certs/localhost.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAm4bLUORp9oM65GV9XrPrbs+K563DjUR1M8mP1HaE+Y4lX5pk +vQjC/xoEqSs5pxDDWXAkoexvxij8A4AWcsKU1Q+ep2E+GcytBoz8XINGvgb8cQNn +rENv5BQb7XXGdUzRMbY3TqsrIytCLTOlNvPdcG5/mPaB6qKEwNLmmRC06XvzAoNn +gcewOPrtup6Lxx3zmihBKrQbHKQz/NPp0HbjUH+tYWlduS79gg6qwiLr/xazZoEI +XFIPHtXRZJOFQrH5RA7kmIfT6Wg+ybfqKpjU1cPJOrtyQGCRZHLuOe7mM9MKxA+w +mou2OoFMu8cJmNkkmP69piSRxKUqWTYYh+O5GQIDAQABAoIBAQCI5S8VNtimaYBv +BX5C26+BzCECKIsWT4myWdrGMsR9PUdcTXQaiBnLncU4epm2miS5FuLHvGvuSqj5 +E8eun+ONXsBRqGCXKPer6nE/pTWhklilyU9566oTYjfq3l4fZcxFK5SnJDdGL4+C +ZhEou6LQkhKyO1jDhOXBLGJZnMEBOf+zXhgZGLDbQwCcCQ5PAZUiSf0cnVRk3rI9 +GwdsbCDNJk6awy8ANlFATemDvHwHZ7ZwmvVzsjsYlYJbY/vJYlOyxa7tzYROVAlm +m8oiPfDvGjnXcGxVQwT0pgDvwtUkUFijZORpkJOPavxqSkpOzDFrOe9UW6HJYGzH +ujhmZBVhAoGBAMnbfssRwYI5egfsxNbA62NkxRcGe5HIlnyZ1Ln0BCE8ae60frcf +4IluZzT53Ly3jUQP2uGdp6nJVq/5ymRYSySNr46DXek2qcJ2TMbtRBhjebPwi+Rb +qTNjccNSgqs0j+1qP78PoTUO5fUWGL9XqIlfPU6Vji4+SmdNMvz6z84lAoGBAMU9 +/3l2WVXykD6FNrRbtvpzU1C9LDixOaVVpGnZHO8krW440LnhIZJbkXnXDVZXc7eI +D/doecfL8rtFteqeBr0LiXGQXU5mIMjXAOAPSvDqIciYzhL8KOK5CWEgVZo6RQlU +G3ne0mk93I+w8N0SE2VKeuxepz4yw0oiKUpAgWrlAoGAS06qNRSAvxa2YjKBFSWQ +K9qydO6kNzVAf2fcpytURxoE41tPUv5/hIY91tPI+Fb6SwQnQrjQjlVhE/H7Agi2 +sAJ0FpUH+jO8jaIY7rYiC39BLlJ1vlI8A8H79UTZHwpTD93tvlgUankObas6vFf1 +tppjgufkzXfLxlJUzXC9CkkCgYA7gy9YOKtP0XZQRuVwmdOl0bIrxEhZeq/IAQUw +or+mMEzb2uyviQwWGubT+l0d1hkmITmgDrff3tuIQcpX1gJ2e8qmp0Zf51SxBJ5Q +/IxCEILNAb374HV9oxL/aUAq3rYB0IzRwrd95ZptCJhEO7X6c/SO6ShRDgP6lEAd +FUV3OQKBgQCFC0Xx/fCX1yquARgoe5pbK7IpXWaTvjBu//gbHsfR2lk3dZbESdeg +OquPDdfp+jFPGISsDhPLzcfkZIPbz5ZVs8KdmpB/YLwyJwFqjDyjwVaDnRnuycb1 +/4PlVWKp7j5SDDNCfleYvmiRn8k6P4mxVJOHKzwb/IwQcKghyqAF1w== +-----END RSA PRIVATE KEY----- diff --git a/services/console-proxy/server/certs/realhostip.crt b/services/console-proxy/server/certs/realhostip.crt new file mode 100644 index 00000000000..7520b0ca6c4 --- /dev/null +++ b/services/console-proxy/server/certs/realhostip.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFZTCCBE2gAwIBAgIHKBCduBUoKDANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY +BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydGlm +aWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkxMDAuBgNVBAMTJ0dvIERhZGR5 +IFNlY3VyZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTERMA8GA1UEBRMIMDc5Njky +ODcwHhcNMTIwMjAzMDMzMDQwWhcNMTcwMjA3MDUxMTIzWjBZMRkwFwYDVQQKDBAq +LnJlYWxob3N0aXAuY29tMSEwHwYDVQQLDBhEb21haW4gQ29udHJvbCBWYWxpZGF0 +ZWQxGTAXBgNVBAMMECoucmVhbGhvc3RpcC5jb20wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCDT9AtEfs+s/I8QXp6rrCw0iNJ0+GgsybNHheU+JpL39LM +TZykCrZhZnyDvwdxCoOfE38Sa32baHKNds+y2SHnMNsOkw8OcNucHEBX1FIpOBGp +h9D6xC+umx9od6xMWETUv7j6h2u+WC3OhBM8fHCBqIiAol31/IkcqDxxsHlQ8S/o +CfTlXJUY6Yn628OA1XijKdRnadV0hZ829cv/PZKljjwQUTyrd0KHQeksBH+YAYSo +2JUl8ekNLsOi8/cPtfojnltzRI1GXi0ZONs8VnDzJ0a2gqZY+uxlz+CGbLnGnlN4 +j9cBpE+MfUE+35Dq121sTpsSgF85Mz+pVhn2S633AgMBAAGjggG+MIIBujAPBgNV +HRMBAf8EBTADAQEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNV +HQ8BAf8EBAMCBaAwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nb2RhZGR5 +LmNvbS9nZHMxLTY0LmNybDBTBgNVHSAETDBKMEgGC2CGSAGG/W0BBxcBMDkwNwYI +KwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3Np +dG9yeS8wgYAGCCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au +Z29kYWRkeS5jb20vMEoGCCsGAQUFBzAChj5odHRwOi8vY2VydGlmaWNhdGVzLmdv +ZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RfaW50ZXJtZWRpYXRlLmNydDAfBgNVHSME +GDAWgBT9rGEyk2xF1uLuhV+auud2mWjM5zArBgNVHREEJDAighAqLnJlYWxob3N0 +aXAuY29tgg5yZWFsaG9zdGlwLmNvbTAdBgNVHQ4EFgQUZyJz9/QLy5TWIIscTXID +E8Xk47YwDQYJKoZIhvcNAQEFBQADggEBAKiUV3KK16mP0NpS92fmQkCLqm+qUWyN +BfBVgf9/M5pcT8EiTZlS5nAtzAE/eRpBeR3ubLlaAogj4rdH7YYVJcDDLLoB2qM3 +qeCHu8LFoblkb93UuFDWqRaVPmMlJRnhsRkL1oa2gM2hwQTkBDkP7w5FG1BELCgl +gZI2ij2yxjge6pOEwSyZCzzbCcg9pN+dNrYyGEtB4k+BBnPA3N4r14CWbk+uxjrQ +6j2Ip+b7wOc5IuMEMl8xwTyjuX3lsLbAZyFI9RCyofwA9NqIZ1GeB6Zd196rubQp +93cmBqGGjZUs3wMrGlm7xdjlX6GQ9UvmvkMub9+lL99A5W50QgCmFeI= +-----END CERTIFICATE----- diff --git a/services/console-proxy/server/certs/realhostip.csr b/services/console-proxy/server/certs/realhostip.csr new file mode 100644 index 00000000000..61395c9f8d9 --- /dev/null +++ b/services/console-proxy/server/certs/realhostip.csr @@ -0,0 +1,15 @@ +-----BEGIN NEW CERTIFICATE REQUEST----- +MIICsDCCAZgCAQAwazELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlDdXBlcnRp +bm8xDjAMBgNVBAoTBVZNT3BzMRAwDgYDVQQLEwdVbmtub3duMRkwFwYDVQQDDBAqLnJlYWxob3N0 +aXAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg0/QLRH7PrPyPEF6eq6wsNIj +SdPhoLMmzR4XlPiaS9/SzE2cpAq2YWZ8g78HcQqDnxN/Emt9m2hyjXbPstkh5zDbDpMPDnDbnBxA +V9RSKTgRqYfQ+sQvrpsfaHesTFhE1L+4+odrvlgtzoQTPHxwgaiIgKJd9fyJHKg8cbB5UPEv6An0 +5VyVGOmJ+tvDgNV4oynUZ2nVdIWfNvXL/z2SpY48EFE8q3dCh0HpLAR/mAGEqNiVJfHpDS7DovP3 +D7X6I55bc0SNRl4tGTjbPFZw8ydGtoKmWPrsZc/ghmy5xp5TeI/XAaRPjH1BPt+Q6tdtbE6bEoBf +OTM/qVYZ9kut9wIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAF5lhhni9dW9MqSL2ixNbViPWpFS +ecOggshYChJfZKrhsuZaDpumJ/+ebICS4zv/oxDwNLSmeAmydiaUQC9LFQEEwvPBYDTtTzwCrtwH +yyFJQSm6pyeIBP/Bih/5hLW8JPm0bDbp5ldrHCDEgKQeeyQhyYOKFODkTuMLw+FLD+V86IVHxElL +/urCRWyZEPwyMsgfsU6ywNX9XNShyk1uDHjFDE67sPhfw52ooxXrYQnBdTk+g0UXPbULzrCK/1kU +fjRq347V9Fwi5NFyGADOaA+q6mtnlb1i3uH1n1YVUzevvpnIr3/RxPSYHB47Kj9iYKeDlYdTRHJy +NpuvTfmQO2Y= +-----END NEW CERTIFICATE REQUEST----- diff --git a/services/console-proxy/server/certs/realhostip.key b/services/console-proxy/server/certs/realhostip.key new file mode 100644 index 00000000000..53bdc86e273 --- /dev/null +++ b/services/console-proxy/server/certs/realhostip.key @@ -0,0 +1,24 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCDT9AtEfs+s/I8QXp6rrCw0iNJ +0+GgsybNHheU+JpL39LMTZykCrZhZnyDvwdxCoOfE38Sa32baHKNds+y2SHnMNsOkw8OcNucHEBX +1FIpOBGph9D6xC+umx9od6xMWETUv7j6h2u+WC3OhBM8fHCBqIiAol31/IkcqDxxsHlQ8S/oCfTl +XJUY6Yn628OA1XijKdRnadV0hZ829cv/PZKljjwQUTyrd0KHQeksBH+YAYSo2JUl8ekNLsOi8/cP +tfojnltzRI1GXi0ZONs8VnDzJ0a2gqZY+uxlz+CGbLnGnlN4j9cBpE+MfUE+35Dq121sTpsSgF85 +Mz+pVhn2S633AgMBAAECggEAH/Szd9RxbVADenCA6wxKSa3KErRyq1YN8ksJeCKMAj0FIt0caruE +qO11DebWW8cwQu1Otl/cYI6pmg24/BBldMrp9IELX/tNJo+lhPpRyGAxxC0eSXinFfoASb8d+jJd +Bd1mmemM6fSxqRlxSP4LrzIhjhR1g2CiyYuTsiM9UtoVKGyHwe7KfFwirUOJo3Mr18zUVNm7YqY4 +IVhOSq59zkH3ULBlYq4bG50jpxa5mNSCZ7IpafPY/kE/CbR+FWNt30+rk69T+qb5abg6+XGm+OAm +bnQ18yZEqX6nJLk7Ch0cfA5orGgrTMOrM71wK7tBBDQ308kOxDGebx6j0qD36QKBgQDTRDr8kuhA +9sUyKr9vk2DQCMpNvEeiwI3JRMqmmxpNAtg01aJ3Ya57vX5Fc+zcuV87kP6FM1xgpHQvnw5LWo2J +s7ANwQcP8ricEW5zkZhSjI4ssMeAubmsHOloGxmLFYZqwx0JI7CWViGTLMcUlqKblmHcjeQDeDfP +P1TaCItFmwKBgQCfHZwVvIcaDs5vxVpZ4ftvflIrW8qq0uOVK6QIf9A/YTGhCXl2qxxTg2A6+0rg +ZqI7zKzUDxIbVv0KlgCbpHDC9d5+sdtDB3wW2pimuJ3p1z4/RHb4n/lDwXCACZl1S5l24yXX2pFZ +wdPCXmy5PYkHMssFLNhI24pprUIQs66M1QKBgQDQwjAjWisD3pRXESSfZRsaFkWJcM28hdbVFhPF +c6gWhwQLmTp0CuL2RPXcPUPFi6sN2iWWi3zxxi9Eyz+9uBn6AsOpo56N5MME/LiOnETO9TKb+Ib6 +rQtKhjshcv3XkIqFPo2XdVvOAgglPO7vajX91iiXXuH7h7RmJud6l0y/lwKBgE+bi90gLuPtpoEr +VzIDKz40ED5bNYHT80NNy0rpT7J2GVN9nwStRYXPBBVeZq7xCpgqpgmO5LtDAWULeZBlbHlOdBwl +NhNKKl5wzdEUKwW0yBL1WSS5PQgWPwgARYP25/ggW22sj+49WIo1neXsEKPGWObk8e050f1fTt92 +Vo1lAoGAb1gCoyBCzvi7sqFxm4V5oapnJeiQQJFjhoYWqGa26rQ+AvXXNuBcigIeDXNJPctSF0Uc +p11KbbCgiruBbckvM1vGsk6Sx4leRk+IFHRpJktFUek4o0eUg0shOsyyvyet48Dfg0a8FvcxROs0 +gD+IYds5doiob/hcm1hnNB/3vk4= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/services/console-proxy/server/certs/realhostip.keystore b/services/console-proxy/server/certs/realhostip.keystore new file mode 100644 index 0000000000000000000000000000000000000000..c8d54d47ebabe889d3d9bf6ed5b5e50ccd9c1c4c GIT binary patch literal 8690 zcmeI1c|4Ts-^b^kjqHpq>qPeLo?(U&N>M~f)?%u6D%rP)l!&O4 zB})sXB1F-Gick`f<+(>kr*nSi_j+FE`QtoWf6Q{twaj&2*Z2P1@6Y#I99tZN!C-Lc zg{evA#O{G%h#C z9%;HRSJu#Du0ku;cb~r4!(4ly-7o)9) zk6()9Vzhb^F1d>sXLF;tt6U2x21xT%xxAl#Vm6x=&^@F*n&oNkQX6aGwv?iaijw>0 z4}MVm4^{VT zrd)Q;?ZQV4n9ZM;Dyd0{k<5|P(wE=>B9C1UvTuO0(EiE_AjOfQkDAIJo#?a{4yqux z8s{uNE85rj3S`G~>J^dfD3Bc<_LpsPyz?pl$gYM%5zWV$4hD$=5dg6C{T4&<4Q(+1 z!&`kUziV=0UZf?5>v#eyTNf?rnj|MV!+p+}XZ37Mm~a4f@RS6&q<6xTZZO^^1LVn` zRL#dtA8fMko*u$EBXZmBd-R<&XH?di-&tGvI2>N87($J&uG)6L-TJPs;v1RR;*4Xa z0pfSCLvfk+*7XL?97$EiirOoc1Tdn8)7E70_=O7;eZI*zCnV>0Y9fZeNsxT3bUZ>M z<#R%EB%L&h9f=r=xOX~$``6L!%2f=F!H#L`MdhHEWMSskB`2vE!C(vRc3Y}MHe7_9 z^7!F~av|&SrVB!w&J|?xJWlaA%ikUw-i+rP)W|X)=Fd`1zg(m4;#8_Hcu6+2??x6R z5CDvaP9gjG&=^c=;Id4d5}ky>PynP43!ng+M*|HY0d=@AqzW)(IP&eY8xeKKpDS1; z9X;LX)0>txYhF@dyp@*%ekM&g3AVmJR_O75$maCDeAi-LZ_O*r={Y(W6noQ6nQPoy5WQ;)i#prd zk~Oo$rBjr0QAAC?%_LE z)AzL(*tN4R^O3O^rQAw6%&hC?iG(LM)yWygs?Bp(M?kFP z^F#Q%=^yAj9X|y44oBQY`mA*yhGAwR$P1UvGBiFAU8mu@3lc9_bqTb;*6^h}q}{jZ zTF2^9IW{J2=uA{0P%)oAa1H0&KpoII?W=p+Y>cw^WV+dh8i9v4xng$iWs$Qy*?ji{ z3)*I{3SXUPwM#TD<)w~R*Cda!h59_k2ugKV1x}~> z=6V9ot^6+ROl?e6@dMwEmImeAj89j{{%dd_pE5Q&l@5F{HBbpE^3}*+Qmjy24JXSu zD5Kn$f{Q(a8dV6hrMR=vt{%VVcT~UA1oc20GnkQX92#xB0aXlWXr3U0iZcJ93o=*j*~Slt?V3Bg<7) zukA>Zd|f8(%aqw4H|R!eKfK?^V5vv#`L@r^-Gz~l=ENhbh4#7L(8-p0-Dc`Kx!`~G zsw$d-u74nwmS~H7Rozwkbm(@6ZfDz!xAgU&gwi|RG>f&kV)=aQ z-nA~aq#NC`Wij-QxmHaGlo9f8xW!OJUx^>|u|4OaFVo=tL0P8|-Iif~EhB2J(Jey{ z#g{R%S{37!1)A}~lMKNNn^ecIPek@#6cNvj8^-&XH!(wKsGgJz?ojm)+FT`Xw{u0r z>0g#Svb*fnZ9TGVvQlS$q5Je!ht3urfoS2m#V;+9U9EMSz4N+`jfU;=ZnYpaZV2S= zQ&%3jh9eVFdRjJG;!NkUgA;}%BU7oRsGXb1!Uj-BQ^;^ z5(+U9H-^iUPCZBqAmcEgFy|L843C{Bjmczqk^Lw*DNvI0BM(M+3vC&!v^{D5zhRXw zh~w1pV$^+@%s@jO9nWttMPX=r)4Vt!#%ZrZrv%a%R3?ocf&)QqPA3FLjLh=tp+8Q z=glXiuKoLCgfp2HVC3bKG^6>GsR7tcv;ZcZ=7)78`%%5fOp4b^Z^Xa$hS*Et`=$NL zWP%(f<5_012%865!e2}(CXGxF9=upq_DIpP<3)Cb@;y0;lb_F84LoYwmY&a3Mc#WT zqK+$wCn5_ID&l)M&4(VpzrFm)n%7{Tz%POVfqm&R8=N|Ksu>8Mk9jcHq;oM%-iLL; z+S#nLu6i!Ux7Jzfek8`|P$0V~HZ0fe(|nvvkx@`t$c_&>Z&pnWxu!_IjhpLh410Va zPp#9N`j{D&Mf}vdWSm@(U?gN~RKnUAWBOJT8F~hYEb2*7`tX)tyD|6Uj9}%QV&-m! zS^VZbTB{BEj2r_$s%)+bKj%F6j?(@-+OMW1)9yfGH;`|0{IIFXK+;IJzu)#WkuZ0X z?)vkNtEa6>XW$3`2IPeUwa|)OTi!WY3^HmD12qO3ep6I2IPvz4>gMk}m@8r|;?5$5g1z-bZnv_0v5pdF_Z7HJJ` z1uf9LKSf&r(F^_nmjnQkK>d|Jg9G9kD`WlVVXFhO!&Z6%0FnvP!L=)85QvuiZ6ul} zBk&K?0=Ir^1&=oQ8~^9+DwzP!3+ELu#dzESfU(3_`!{&~#xo6R{jUbJVO)jPV*i>H`)Dk-ggad|aw zS9Dd_y_^Q*Ad)0FAz&`O!%S0EiJeS5YFyr8AUE<$WP@fZuTkHs+s63=8N@2Qq?PGQ z8#bCz|FmDDJM2_|&BYeO2P4L@g~MO!Uy~GGA@T0G2BW;1!$W0N_1O&ciC8Q3rsz}yHs zC-(B2<|1E0dfN|%BU-*{E)?^BTCab^Ni3UGA`XWmf;fVnu0AA#`0tDVw>1Ah6W%7R z$709Rm95|538U`Sy8xGSWcIFgfu1F=o*Lg$ufC((5i)2vW&|omCRBi;Rpl{_#wqs5 z%z@Nryh?Iw`YCEh2u0`ZN2h1-q)ugrl+uYWi_X#AnNzU%;l1*#&iv54P$QR*2!AivbvZD)+)}vm4xPuOA@%xyouwb zY$7U-`%JsxC8d~p%aOtb?-}!i(`}-pm&B;bBU1Vc;?qIGXPKb*BIab?`bd7872(B0 z!mIt=-TjwJ!AY5{NXcJ1W)V(J;Q{_Frmp2z}h}@ANM!&F?nPS(f-)d$#!Ik8z`{dO7la2)#5M>mF zU-R>wTRcT;^OO2FcPJ@}BD`-3bZi)$FX^(3H8R$mQdO{LLRT2R&PLlJeEm zfG2cKwE=xV{)`@U@#Z`Zn0GAU~<2kyUHD6}{sYA#?% zr1hFtnCF__>FhS!Zc@YC|2P-ppxF|WSDAVN9T^iEjGnI(wH9a#Xc{oSDiSpH=7a49 z1y#%EvQ{xgdTr?3XCf{SqlsP!0*%HB7@dsTab`~m!mjj$qNlug`n{TRl&9f~DIaMG z6d}cK{CBbYk&yeA75tHq`;n0Qf18kl!%&`d8iTQ%i!=9j`duNGARPJvy}W+IA%Oo+ ziVCu?;~*-9BpkAr(j1?~$-A!@E>7lsJB7)j(S30epctnW4@O|6)o*5#u_;4!fQ|$1g6vy4?1p@H?f>owg^mpVY_*sE zYq(~{Ss8gG1ZyA!YuSWo-M@ir|1Ge_X2U8s%ofT{k1Xa(ZFDi45$lZF99LhP`RdhZ z^DF1L#N;feq99F+hwSnY`3q>m>ay(u&eEb4XV?#sa<{qeL}VQ> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/console-proxy/server/conf/agent.properties b/services/console-proxy/server/conf/agent.properties new file mode 100644 index 00000000000..4e217f21100 --- /dev/null +++ b/services/console-proxy/server/conf/agent.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +instance=ConsoleProxy +resource=com.cloud.agent.resource.consoleproxy.ConsoleProxyResource diff --git a/services/console-proxy/server/conf/consoleproxy.properties b/services/console-proxy/server/conf/consoleproxy.properties new file mode 100644 index 00000000000..bb452f5823c --- /dev/null +++ b/services/console-proxy/server/conf/consoleproxy.properties @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +consoleproxy.tcpListenPort=0 +consoleproxy.httpListenPort=8088 +consoleproxy.httpCmdListenPort=8001 +consoleproxy.jarDir=./applet/ +consoleproxy.viewerLinger=180 +consoleproxy.reconnectMaxRetry=5 diff --git a/services/console-proxy/server/conf/log4j-cloud.xml b/services/console-proxy/server/conf/log4j-cloud.xml new file mode 100644 index 00000000000..5b31c9db967 --- /dev/null +++ b/services/console-proxy/server/conf/log4j-cloud.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/console-proxy/server/css/ajaxviewer.css b/services/console-proxy/server/css/ajaxviewer.css new file mode 100644 index 00000000000..5ea552b176f --- /dev/null +++ b/services/console-proxy/server/css/ajaxviewer.css @@ -0,0 +1,144 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +body { + margin:0 0; + text-align: center; +} + +#main_panel { + clear:both; + margin: 0 auto; + text-align: left; +} + +.canvas_tile { + cursor:crosshair; +} + +#toolbar { + font:normal 12px 'Trebuchet MS','Arial'; + margin:0 auto; + text-align: left; + padding:0 0; + height:32px; + background-image:url(/resource/images/back.gif); + background-repeat:repeat-x; +} + +#toolbar ul { + margin:0 0; + padding:0 10px 0 10px; + float:left; + display:block; + line-height:32px; + list-style:none; +} + +#toolbar li { + float:left; + display:inline; + padding:0; + height:32px; +} + +#toolbar a { + color:white; + float:left; + display:block; + padding:0 3px 0 3px; + text-decoration:none; + line-height:32px; +} + +#toolbar a span { + display:block; + float:none; + padding:0 10px 0 7px; +} + +#toolbar a span img { + border:none; + margin:8px 4px 0 0; +} + +#toolbar a:hover { + background: url(/resource/images/left.png) no-repeat left center; +} + +#toolbar a:hover span { + background:url(/resource/images/right.png) no-repeat right center; +} + + +#toolbar ul li ul { + position: absolute; + top:32; + width: 260; + height: 65; + display: block; + display: none; + border-top: 1px solid black; + background-image:url(/resource/images/back.gif); + background-repeat:repeat-x repeat-y; +} + +#toolbar ul li ul li { + display: list-item; + float:none; + padding-left: 20; +} + +#toolbar ul li ul li.current { + background: url(/resource/images/cad.gif) no-repeat left center; +} + +#toolbar ul li ul li a { + display:block; + padding:0 3px 0 3px; + text-decoration:none; + line-height:32px; + vertical-align: bottom; /* this is to fix the list gap in IE */ +} + +#toolbar ul li ul li a:hover { + background: url(/resource/images/left.png) no-repeat left center; +} + +#toolbar ul li ul li a:hover span { + background: url(/resource/images/right2.png) no-repeat right center; +} + +span.dark { + margin-right:20px; + float:right; + display:block; + width:32px; + height:30px; + background:url(/resource/images/gray-green.png) no-repeat center center; +} + +span.bright { + margin-right:20px; + float:right; + display:block; + width:32px; + height:30px; + background:url(/resource/images/bright-green.png) no-repeat center center; +} diff --git a/services/console-proxy/server/css/logger.css b/services/console-proxy/server/css/logger.css new file mode 100644 index 00000000000..42ac8071ed5 --- /dev/null +++ b/services/console-proxy/server/css/logger.css @@ -0,0 +1,139 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +@charset "UTF-8"; +.logwin { + position: absolute; + + z-index:2147483648; + width: 800px; + border: 1px solid gray; + background: white; + text-align: left; +} + +.logwin_title{ + width:auto; + height: 23px; + background:url(../images/grid_headerbg.gif) repeat-x top left; + border: 1px sold #737373; +} + +.logwin_title_actionbox{ + width:175px; + height:16px; + float:left; + margin:4px 0 0 7px; + display:inline; +} + + +.logwin_title_actionbox .select { + background: #424242; + font: normal 10px Arial, Helvetica, sans-serif; + float:left; + border: 1px solid #6e6e6e; + height: 16px; + width: 100px; + margin-left:3px; + padding:0 0 0 3px; + color:#CCC; +} + +.logwin_title_rgtactionbox{ + width:49px; + height:15px; + float:right; + margin:4px 0 0 7px; + display:inline; +} + + +a.logwin_playbutton { + width:18px; + height:15px; + float:left; + background:url(../images/play_button.gif) no-repeat top left; + margin-right:2px; + padding:0; +} + +a:hover.logwin_playbutton { + background:url(../images/play_button_hover.gif) no-repeat top left; +} + +a.logwin_stopbutton { + width:18px; + height:15px; + float:left; + background:url(../images/stop_button.gif) no-repeat top left; + margin-right:2px; + padding:0; +} + +a:hover.logwin_stopbutton { + background:url(../images/stop_button_hover.gif) no-repeat top left; +} + +a.logwin_clrbutton { + width:28px; + height:15px; + float:left; + background:url(../images/clr_button.gif) no-repeat top left; + margin:0; + padding:0; +} + +a:hover.logwin_clrbutton { + background:url(../images/clr_button_hover.gif) no-repeat top left; +} + +a.logwin_shrinkbutton { + width:18px; + height:15px; + float:right; + background:url(../images/shrink_button.gif) no-repeat top left; + margin-right:7px; + margin-top:4px; + padding:0; +} + +a:hover.logwin_shrinkbutton { + background:url(../images/shrink_button_hover.gif) no-repeat top left; +} + +a.logwin_minimizebutton { + width:18px; + height:15px; + float:left; + background:url(../images/minimize_button.gif) no-repeat top left; + margin-right:2px; + padding:0; +} + +a:hover.logwin_minimizebutton { + background:url(../images/minimize_button_hover.gif) no-repeat top left; +} + +.logwin_content { + overflow:scroll; + height: 477px; + background: white; +} + diff --git a/services/console-proxy/server/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/services/console-proxy/server/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in new file mode 100644 index 00000000000..47fdaef1d96 --- /dev/null +++ b/services/console-proxy/server/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in @@ -0,0 +1,98 @@ +#!/bin/bash + +# chkconfig: 35 99 10 +# description: Cloud Console Proxy + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well + +. /etc/rc.d/init.d/functions + +whatami=cloud-console-proxy + +# set environment variables + +SHORTNAME="$whatami" +PIDFILE=@PIDDIR@/"$whatami".pid +LOCKFILE=@LOCKDIR@/"$SHORTNAME" +LOGFILE=@CPLOG@ +PROGNAME="Cloud Console Proxy" + +unset OPTIONS +[ -r @SYSCONFDIR@/sysconfig/"$SHORTNAME" ] && source @SYSCONFDIR@/sysconfig/"$SHORTNAME" +DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize +PROG=@LIBEXECDIR@/console-proxy-runner + +start() { + echo -n $"Starting $PROGNAME: " + if hostname --fqdn >/dev/null 2>&1 ; then + daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ + -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS + RETVAL=$? + echo + else + failure + echo + echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr + RETVAL=9 + fi + [ $RETVAL = 0 ] && touch ${LOCKFILE} + return $RETVAL +} + +stop() { + echo -n $"Stopping $PROGNAME: " + killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} +} + + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p ${PIDFILE} $SHORTNAME + RETVAL=$? + ;; + restart) + stop + sleep 3 + start + ;; + condrestart) + if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then + stop + sleep 3 + start + fi + ;; + *) + echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/services/console-proxy/server/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/services/console-proxy/server/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in new file mode 100644 index 00000000000..47fdaef1d96 --- /dev/null +++ b/services/console-proxy/server/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in @@ -0,0 +1,98 @@ +#!/bin/bash + +# chkconfig: 35 99 10 +# description: Cloud Console Proxy + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well + +. /etc/rc.d/init.d/functions + +whatami=cloud-console-proxy + +# set environment variables + +SHORTNAME="$whatami" +PIDFILE=@PIDDIR@/"$whatami".pid +LOCKFILE=@LOCKDIR@/"$SHORTNAME" +LOGFILE=@CPLOG@ +PROGNAME="Cloud Console Proxy" + +unset OPTIONS +[ -r @SYSCONFDIR@/sysconfig/"$SHORTNAME" ] && source @SYSCONFDIR@/sysconfig/"$SHORTNAME" +DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize +PROG=@LIBEXECDIR@/console-proxy-runner + +start() { + echo -n $"Starting $PROGNAME: " + if hostname --fqdn >/dev/null 2>&1 ; then + daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ + -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS + RETVAL=$? + echo + else + failure + echo + echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr + RETVAL=9 + fi + [ $RETVAL = 0 ] && touch ${LOCKFILE} + return $RETVAL +} + +stop() { + echo -n $"Stopping $PROGNAME: " + killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} +} + + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p ${PIDFILE} $SHORTNAME + RETVAL=$? + ;; + restart) + stop + sleep 3 + start + ;; + condrestart) + if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then + stop + sleep 3 + start + fi + ;; + *) + echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/services/console-proxy/server/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/services/console-proxy/server/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in new file mode 100644 index 00000000000..47fdaef1d96 --- /dev/null +++ b/services/console-proxy/server/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in @@ -0,0 +1,98 @@ +#!/bin/bash + +# chkconfig: 35 99 10 +# description: Cloud Console Proxy + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well + +. /etc/rc.d/init.d/functions + +whatami=cloud-console-proxy + +# set environment variables + +SHORTNAME="$whatami" +PIDFILE=@PIDDIR@/"$whatami".pid +LOCKFILE=@LOCKDIR@/"$SHORTNAME" +LOGFILE=@CPLOG@ +PROGNAME="Cloud Console Proxy" + +unset OPTIONS +[ -r @SYSCONFDIR@/sysconfig/"$SHORTNAME" ] && source @SYSCONFDIR@/sysconfig/"$SHORTNAME" +DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize +PROG=@LIBEXECDIR@/console-proxy-runner + +start() { + echo -n $"Starting $PROGNAME: " + if hostname --fqdn >/dev/null 2>&1 ; then + daemon --check=$SHORTNAME --pidfile=${PIDFILE} "$DAEMONIZE" \ + -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS + RETVAL=$? + echo + else + failure + echo + echo The host name does not resolve properly to an IP address. Cannot start "$PROGNAME". > /dev/stderr + RETVAL=9 + fi + [ $RETVAL = 0 ] && touch ${LOCKFILE} + return $RETVAL +} + +stop() { + echo -n $"Stopping $PROGNAME: " + killproc -p ${PIDFILE} $SHORTNAME # -d 10 $SHORTNAME + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE} +} + + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p ${PIDFILE} $SHORTNAME + RETVAL=$? + ;; + restart) + stop + sleep 3 + start + ;; + condrestart) + if status -p ${PIDFILE} $SHORTNAME >&/dev/null; then + stop + sleep 3 + start + fi + ;; + *) + echo $"Usage: $whatami {start|stop|restart|condrestart|status|help}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/services/console-proxy/server/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in b/services/console-proxy/server/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in new file mode 100755 index 00000000000..550f2fbedfa --- /dev/null +++ b/services/console-proxy/server/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in @@ -0,0 +1,112 @@ +#!/bin/bash + +# chkconfig: 35 99 10 +# description: Cloud Console Proxy + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# WARNING: if this script is changed, then all other initscripts MUST BE changed to match it as well + +. /lib/lsb/init-functions +. /etc/default/rcS + +whatami=cloud-console-proxy + +# set environment variables + +SHORTNAME="$whatami" +PIDFILE=@PIDDIR@/"$whatami".pid +LOCKFILE=@LOCKDIR@/"$SHORTNAME" +LOGFILE=@CPLOG@ +PROGNAME="Cloud Console Proxy" + +unset OPTIONS +[ -r @SYSCONFDIR@/default/"$SHORTNAME" ] && source @SYSCONFDIR@/default/"$SHORTNAME" +DAEMONIZE=@BINDIR@/@PACKAGE@-daemonize +PROG=@LIBEXECDIR@/console-proxy-runner + +start() { + log_daemon_msg $"Starting $PROGNAME" "$SHORTNAME" + if [ -s "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then + log_progress_msg "apparently already running" + log_end_msg 0 + exit 0 + fi + if hostname --fqdn >/dev/null 2>&1 ; then + true + else + log_failure_msg "The host name does not resolve properly to an IP address. Cannot start $PROGNAME" + log_end_msg 1 + exit 1 + fi + + if start-stop-daemon --start --quiet \ + --pidfile "$PIDFILE" \ + --exec "$DAEMONIZE" -- -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS + RETVAL=$? + then + rc=0 + sleep 1 + if ! kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then + log_failure_msg "$PROG failed to start" + rc=1 + fi + else + rc=1 + fi + + if [ $rc -eq 0 ]; then + log_end_msg 0 + else + log_end_msg 1 + rm -f "$PIDFILE" + fi +} + +stop() { + echo -n $"Stopping $PROGNAME" "$SHORTNAME" + start-stop-daemon --stop --quiet --oknodo --pidfile "$PIDFILE" + log_end_msg $? + rm -f "$PIDFILE" +} + + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status_of_proc -p "$PIDFILE" "$PROG" "$SHORTNAME" + RETVAL=$? + ;; + restart) + stop + sleep 3 + start + ;; + *) + echo $"Usage: $whatami {start|stop|restart|status|help}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/services/console-proxy/server/images/back.gif b/services/console-proxy/server/images/back.gif new file mode 100644 index 0000000000000000000000000000000000000000..5c61ae27719fd2cccd372b6c95a19328812feb46 GIT binary patch literal 149 zcmZ?wbhEHbWMoiaIKsfNa{o1U&r*K>Dv{t?r|N|s^-B^aY}jz(29t&(i$^JoM+vJ( z35RzXw@|$V*QYgrYYKxr~7#Qs65x7W)@iIsAXNQ>QwH*Qs)&M$I+{&1Z`2aHjfX&pzz!shdsShJ9 z{51TikOvPYKNIIG0I+aT9~zMUh#vrEYi|XwDFiP2)a8Lps5Z>=@en z_bGRIY-CyQFb^`!h(5U=@igX6=hkG%(+i*LHokT5y!@g)SF)N})yvSp$eLn;*r-rg z3-yKfeT`^uX`7sp*9~VD_XC`aRp&&nY>Ux=%`i>PBZnFo-U6EIA6XbdgJnj;(Sy+h z$AUg~iiUEVrXe8VZ*xWpH_#2fub&5Wjc6#DX_9szn+~{kdALu5<8r`N>gKnPfRee1 zh0p+(BvBrk>;xdf;xV-?dtaR9`>@H9@YER>}TZ`-q90>+})>lW_yeM zbOD$Q2Pk=l}?8n(`|lj!q+Tv5B5G}y6rytE#COg zD;DC9(8oh9rtURPGThTP`~B}5q~Bdt$~_dT|L}0DL_cwEYC?94R(nDPRGinQ2}zJE zIPF3E(_D(vDWG9$1AvuU-^NeUjI>x;KE>2vBcpQ9YS?0UH5mcDdUcZHCQtHLK; zA>{@euc$;ncKIfU*X&w;9KYQdvl^xA!kOswIvBa!M2BN&-0wJv-Dl*(A~!`FP3ZQ= ziHp3C;kG$so}gl)XnV(A#KA;pMeFh&_2^SZZxW7m0Hs@hqUwwllfo-)v_7%EB-IqC z9k^}ugyqAz0LAop-t3GImtP#vx~rNq_o3K}V>n)$)PA(#J3j-`a`C8qTeJT2zh#*c zJ-VJBDq$^dJ5X_=gtj(z-J|z;Al?3O!v-c*xVis_Lx3@X5YWR z->CnRt=PP5VTQ8F0kOpQ!{;tkGN>oXiX7>PoXMPNo_a5^3`)<3)Epvk>p_4h;ELk#HP)5PNuqdx+yokkN)uGEizVM7)Xqk}% zVmDpL^eLs-p(ykCqMe`}x$sy)bP>70*IwsYnDcfly>qigvo-0NP4@G%({*hkEFJA-l1B=6qgDEsgau`JxNCf;qJYf$uKmFQ;X_JwwT|@Xa3&s-OFPFjQgN ze%t4F{<<-%6MYq{IQkV5;&zc!@P&AOyqw`8}}$=eAveVR`PjP4n6 z-1TldH7!2v)8^CmRFYm&RMOmX!E!m>*0Lt=aNaSCV;1e*c=wa;&zMSvmf{zsxs17t zFBucBTu)v9F=%#5mxrlo?B@@3U!@8#&x$GGz2!&%PAl@HAwq^~x! zeL5_UlaF4QC<(JBS%;Hf&WR~_C?q(ReyMns8(6gA_JdFRr}pK?)7sOTYU4`d&S!D8 zhF2Z0x~!Xhv(83NkZy_*=ZIjU59CuMXvnm&;d7n9OY2GK@n(-a9`^n72L`uqZgI_FtWuHDk&aP1_x)m= z4)iIess#3plCDe`{5D827|$RpTC11KnW@NV7s;R399Day{6?ou9(BeCoHCtEFPa}z zmzOD1y>#*I#Gj?r=&8&pmgx|_G=+}SenB}sO>2?)yoK`nWi?WCEn=+xPWq1?r%VCw zU5OR0K#BXzK}|wUe5pJ)2c<`^&k9EScD}<#lU@`Sx73{L zMf=hDZ{u=VF6=2qsozrBFR={`zhIj<@nUKHr{Rb~YPa-AZA3Mx=IJ}l*L9xx?|ZtB z>DWKBU$g&hue#hn_R{Uk=B2)6!H~M2%Kr?ExYTWIYrlUx;y~kIIe-p$ZkcXXZRnK6 z7R_&_Z26onwes;_YNzd|-DVJDul`c=X+Wr7%icEr^N~ek_V&Nt?qvQ5lw(t6bGY}Y zZA#3~AZTWOgK1(zm`!8&+TD}~W+rEbhROdLbsc$99Wv+9GjX27_s1K~fXRMC zB5v+j-LaliE3X4qDo19y9A7)8musB$SqNCr+)4as-Cp~sHodIo)PzwFDqo0su+` z0KW)$&I54uC;$tt0BEEEz~}q$Y^xCfwBWdfy&^_v;g91GY)fyTaU@ag3v3tN1 zeuD;o`~MjN015yG0DMIw0Q3+9ASggEfZzZjs6i9~C<;&vpg2GYYA%KV3AHjshG5I1X?sQ$QeqKmmaP0*ADzz*K061&d)eLG42|6hSZq!4ZU@wuK&wA}EHS zID!&X7BGmR2!eR3N>Jy6ZJ~!@D2AgLK^+&efI%EXaU8`7I6Wi zG(lYvH5-PZ4~>8ugF?djU|Z+Zg(!QB;7W!~KiHFsBNs_MJiQA>B_pe|u| zLMqZOY3fdPc~F6Oq2YCaTY}=jnP4je_lo22LhlQ(wX&n$@BcL*QHr?(K%-}Bpl5%5 zxX!lGj8>5|)Wh7U)!llkaId#K(!jd>)SlYLQU>PON{um zK9#&Ju#y(kTxaa@Crti7QeVGTnjbmII(IgXL9mPCDy^Jgr)*M?uOm5`=iO>}BuCg5 z|J)*%Rc*Jpk<;m=CvjccPGiy$1I^R1DKlyg9LjqX-Opzf{#=(G;muW~EZiF?uwf`K zkx$btw^aM^vf}vE(%ABu&~6o#!Q|>QHM6)?Xuy=K4GsSuT^uNVpit7p(y`cFTa&vF z`1Kl)J$v>%quj2RD`wKRn@(y?^_F^y_n*1t?IE4~$kf@{nLqDd|I*LtYr3)f&4XkY zr=yaT_|8;RT5UbFG{TgGFw)uN?kuJ7$mGa@C_62ka%na$3A3a;g*oA279o2$QvPV9%*hz#<1s47ViixttDKBiKA5I-Hc9zhs@ll}wX><3 zr&Bb}rfOfv)V-Xedo^F@dV%)MBCT5`ns>{r&gL3i$TGQHV1BK{@Lsj)ohr*4jp0SMv&=&C7eUu#z#jU9qln{acO)Mc#MI8iLs?cDWH?5OPz5+k5aIkhb{$79D8q3eb+5p?P`X&6ymR LSE)(}FjxZsXaKX; literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/cannotconnect.jpg b/services/console-proxy/server/images/cannotconnect.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0599f17beb25987e999230a1429047408e85284a GIT binary patch literal 1810 zcmbW%c{tR07y$6^Z|<4IxJCxK8b+yg4MS~`qb6iiIg>P2GflCq5S4Z27{qL)Hp!JM z%9SkZN^Uxckeh^cWOCQcj@^CQ=h?q@d%w^7{_}mG@AH11@AJI;H~b-hKV(KT0}up( zQ-TBd!(h9qAJYv08f`nk001BW!I6Rr08x+|aW5PKaRq|jK41(`2qX%LK%tN*G#Z7$ z;c+-D7AGMrB7~Qc*s?`RfrUo%CZE4g1UmTimKXnHEH>s8av4vB(fTL;}Hmr zM&mFzaU4#ZOe7G=|2z2AKokSafh!zR0x(esE(-DMfV^OMq(HWT`#T^QTre*hgT-wU zG?d^041&X92sjdnKnS`o3+@3z6e*^tX@n9#;(}K4l+X%_Pr)efFQ}2UemTEQ`?Oau z7DtfUB26TzsFKyT>riy{sQL!RCZ=Ww4$=-CJ$Bs2*3RC6?)tsk83yz0kKPx2eElx^ zhlGZOUyX>2O5pr_{l?A2Tenlw(lhR7W@YCT78RE~E`3t=YwgpzXY~z@P0g=5I$w8n zzv<~69vK}Qe>d@da_YkZ_v7Ny^2+Mkh6@7lzgS;n|8j{6TrdOzjzDj?AegTp;i3qn zq9#hr=m^@yQ(Q?a2qUpSKBb@rtE_E3FL~PQC62I7XPES1gZ8KFzrljPO7;czkE<65 z!6Cut!9{@~ShbEK--CTc3Ug}w zRO&Oz1074}qes%6k_gXEaaGT|jkYf&>T2W(1*EszhQ%qXD&rh(4a?(qg;74)+9bPo zP-N)Li)TriQkFGAwl%#|%@42$&qjrKN1p75$DH9dvsp_S)6NHZ6UFO#$*8=-&oZsT zPVRg1aVtyZ=f5C8Vj)?my(_F)=xp20PeSk@D3 zqzCHTr!s2ITC2m_b%P;k_~vLEKEP3I$`S^aZC|RZ@H#JVC6(Q_c>Y5Q23x4RYG*JU zi_X{5Mn_jFB#X+6-RQVY=yF%j$Fewz(Z7EvJe5%9)mOw})2kdxaxYo%UcCO@qpI*y zRS_%Gp=asNY;$d;jgKcUb1tx?Bi7Vyae)u|Joun=^Xf(3;lUVopU74Jq)x&#X*P+K z-Jf1)Kgp>`W|Y~rpmvntb{(QZ{pONGvh30kkudMM%}-M*lOFU9mMn(qS=R?UHMe5b z^-Mxs8sMZS`jXl5b*!VF^z0K1k6q>Wu?e-KGl@Zx{r#63D=Nwr;~yE{qn-!}xy!0s z`&?O``99)YMlc^(6`8B4tnXut$7nx_Sc2t+G`+1}f~DEq4B!#@pt@@4Tm6c>l}SE8 z4MyfZWDLm8&lD4V?o(JX?kvpgz{mjO)g~s1O_McK+5hU4mb;76sAu)=D-73p;gqev zsMqs>r2$*lJgX%G&6&~cyMvz?Ti{LaxGrp`)+CFU%Ua2$b*az1>nc{uFKG#&6gg&8 zY1WWUdSsdiwXIVrW;Ukivtt+9%9FSDwjq{TX$C9d6P8Z90_w~Ap$MgVCzm(4M*4T@ zb30n_Y05;VZ{T#`iqtf>(>!x@#d)#d^!o7L-1FITdv`jUlvl(Z39%@P8em%Wq$kG9 z=xhFTY&0n&;{Coo$L;q@PpPns&g8uBe!S4;iCDJb(#GwJF&-<7jEv2I!$uMqE`##T^~wjC!|bEh=pd`Hgf zdI(J!Y;(}*S}Hsl-6t+LYat}=BIa)VZrseThwJ*WHfZpA-&*&l@KWb)E3Uo)IW;l( NE7r!wsuAJ$`~kw83zz@^ literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/clr_button.gif b/services/console-proxy/server/images/clr_button.gif new file mode 100644 index 0000000000000000000000000000000000000000..f5c385829da0dda9ec5a86372aa2fb7a57c85a69 GIT binary patch literal 1274 zcmVkP*Z=AOLmD$XmxLUfJjwZ zYI%Qhlc86Dl52H&KSD!kdxL9hYfOfodaJ)ndW~*!zU`S6=Q-G6ScY$wuepFjtQ+``WiK)21V}FZeWMoQcbcC3o)!EuWMoDF5Wx31JTX=939w!FV>euPwNb4^Z8RCk7JZf{g!XI*G*ReFkNYi)+3uV!y^ zNnK^P!pBQ>he%FQf}pIs#mSAOuWERHUxbv^*xGDzbV_f2Yjt{HZgj)X)Oe7YM@UGy z#>-r8c1dJ!LPSJsba+xyQd@U}Sd^`TpQ~wlfUUT_dzYiPz{JDO(}9zlRe6Y4e2Z*v zaDAMpewCd@M@Mpzoob1d)YsT@m858XhmWSPS95-Hi>>KU2Ab*Y;j6!c}i`2l(D^2e2&D<({6l%Szu#$n5BQ4 zrf7hQYlM$nbbe1zQD1d_Uv_^`P*G=WZ@I+Dn5(l`ldM;Qlxuc+y2Z*=WNVL~rEiOt zSAmjPXm4eAep6FZW^iqOdyG|okyn9}NK{x!Vr)Q2OHNKsPESwD z(9%nEhRxB_A^8LV00000EC2ui02}}h000R80A0Wc*I)nw007Vcn1Ue5fB_&5c4?un z1povCQuz`Xz*`y#D0FCeqCv+Q3N<9upm64i2@}^eP>`Tz&ptE!OgZ|%Aw;|;V?uCP zbnXKrWS(4Yld-J{hfHMp1R(N49K0VutZWg3r5TWUd49Na#!Xo#M0QrOnKdX-fB-LO zMfw)zAB4NLj1e0JE=MqJTzZtT)@a?XXv~}hNcP84oqw-busCrASRz!L!2CMZgA<){ zxyW^c=0-pOUBaHly2CEWJ#!vBpaa#bRFF54NRU_~N)fDf*i7&6qi52%3p_61xX`9j zwrV_Zz=`pNR}5mVm@fXG3i515>V8F1VRh1DVstAgc&%q(n2M8=# z7K#cfKr_q`g%Ro*GK4Bdlu!aA{m4_m1{MgALIp)gfrJX5D4+lpeBe@uh6)HFix7`Y zBaJ$E*g${*0uUj`0TVb-fDe~M07V25WT2xKP|)LpEl3owfCB{FP=OPgG{A%bG)Q5? z5(`9m1R(`fdBiOaJiq_}1khl?0ZastMG6flalj1|5Wv8e1mN?)23k%~garoFc|Zk9 kU^0gYUcA$Q01+iHMFNB(WM~2oJv4v-0#H(n2mt{AJI!|_qW}N^ literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/clr_button_hover.gif b/services/console-proxy/server/images/clr_button_hover.gif new file mode 100644 index 0000000000000000000000000000000000000000..f882fa0baef700d492cd02fe95a274ab7962e5a7 GIT binary patch literal 437 zcmZ?wbhEHblwsg!xXQrb?(S}HZ=ai+TU1mO7Z(>25|WXT5g8eol$5k>+qS^K!1ng` z{QUfT_wFrPw8+`n+0D(Zrl#iT(W8}>m3#N@ZEkKpeE4v5bhML`Q$az&&Ye3O8XC@? zJ)51KJ!{r1e}Dfyd-hzrcFoVv@9Nd7Yu2nOE-tRFu1-x&4GId%$;t8c^=)l!y>jKs znKNf*%$N}n5HMxRl!+53_VxAc-o1PE>eX3US;4`)Ucg&Ru`i;Q89oC%A9bg#5RFP~hm8~c1KvH6SH zjRhAAiU|s8iy19;W8EYywP_oxl<+oTWhq@{gY9k%N5plGFmUNHaPpks;XJ}AdV=Bb z2~IvPE!3T`{ Z8WDw0sv;Efe8Qr literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/gray-green.png b/services/console-proxy/server/images/gray-green.png new file mode 100644 index 0000000000000000000000000000000000000000..e785a63f83eca4d54e2ead7ee4035b1ce4f508ed GIT binary patch literal 3833 zcmb_e`9D=_)PGzuPZ^VR%_Q?ST=Q^@nK?o#Q}$7kAvfZtBbnk7GHfC$^N@%#l_|tA zr!toz(WN&-l-Tcc{Pg|-@A`c9dCod(t?&A-?|RPXv*T^8P1%@_G6MkE%*~AK;dzYu zFw(+L<57hocrXQ+xm*Q+g`4`&fb9E60ARMcNFdnS`uGR?U-j`15H=?egad;7(TkV8 z0SKMQbs!NPmictHXEqHj;>hhA3Nv>>k^$t)fKI2mg%h+N$hqX9b++S(^YTNz#gn$S@eM$l@N)2etZ z7Ux(o#Lm)?Z_~5}r~GBXNag|hVM&HXK;MLhoSP--0P^X8d!Lu*Jg}7m?ou~Dj{O}!{q`S9tkL5eF^e}RJh)l%Mc;0aqOJd~5qIX2v%;>lh z#D(9)@z{x4q^O!H+20`wJDLft>jeFw5qs98F-5BfDBt=W(`>4AFS5o?=Phd;p`k?m z=xvjSEJGK9m9mrh@^glQo*mUmR4Z5-D)-@-NY*8EE4F?)!hm#MR+R7RFns!#EK{0S z-&4^F*7B~S)u$_Ho8q^;2A_t|aYY)pGO5vp7z%qFH!yRybhiFrRw8^%T;=ctlR6Xa zZM$|U(X5$Cb>jz!@G#=u{WyiYF*bHQfEf{OM!j2-k?o z$Q66BW!cgk6|TSj_|`CJ+>;eP*%Q5xyU?+KEC?=$th+Mjdg=#0-f@_dw(q@k zN??(5QDu?dJ4@NX@lm$@bV&j6uxj!d!^(ns`zLm>CEO~1XKVCF@tIo`Rz6H0J?g{n zvwiW&m*B?3)9J^pGl!f;`j$<?0a!po&C{AQ*#r02^lrS~%ve?+IS+CUen95yx%*CZUd(mK88s#GlNzo@ZNw$STioiC z&(xLA5$u}oX<0ZGycKxvQ`pE>Zcc4Z`)bMLlQp%qi)*n{4=5@RPUN{DSBE-!2p{bh zhxJ$0PpG}8zpKO(P^^fV{Ef7Fbx8YJd7 z81Nb-->ST|*j6dx)AQfmN|EiQtbvq)i|o<15ntX7F?f9TNa%=Qm5Ppyc8bwU3W#$) zI;@nb8ZtaZxH@a}%P7NWCWoYCqwz%Ul&XwwnfxW~3H3&mM!hb1)P*wUoa=0U+2WXn zyiAqamCG+@f3I%D&gIUr%!l)5DfFBRxLz>Oz8PK2S1QjXtC^v18E5_1z5g-blqum$ zlvw8ukw{{`-Y(Qm5>*&h8;<)xI4g79dfiOMDlGNClxEfI;;)fH*~Nz2Ql)pBE!ixm zb2Rh#vabpIDeGvqC`F~F+zm7=6R}FGC#{e^qu2T>yrXOOUtdXhu5dh^;f-(Y-HJml za?JIHMPG<#q~FUh)R4TL&{zHOy&4(ic_;fp z{@hY=Nux$P@cJb4Jnx~CF{kM(v|rq*m+>33DPMKUtcqxn472i$Jm1!PtF4a4_bZDd z^J4UkuT$?=&)GODxXjjh{EEpH)a8x8Q^hmMpUHdkgY;DBqF}6l?<;KT-m}v3&V~zv z=m@&}c}6bJl|7>@^K&Nq6}It-XKb^lpRI2FFrHM%?3bQwimE3xJbI=5qS?Fn%|O4F z9`P}8llY6Mwl*?d=kb2$%J7C_@~bI(_n$BKbH9biv8k~+{(HY` zPAtIa`oi)y)9m(fHqD7^iQyHW7)JlRCPWfaxMY~Uo^6ujkiF~b>Dp*R(H(xbG86XO zbn;J*`zqQl*H3K_-&~wnzBQUgS|3czP0zg%A^&sAeez*__>$Ma>?IEWZ;hP6pGJ)F zge9$Jt%0-aFM`)=CKtJ#UN~hx(RA}$30~3OPaC!AZo1!;UDa^bw_z=;r7j%ZUKY$# zADG^3x&ECGe>aw=p)=jG5$3=BdV6b@Z#p7KYHy&gX*@(~t+Lss*=d+tkfVts(gv!&9c)^wCj+`zc$=9E3y3SaM$sTg=zIzjQS;=JbO0mVXx$1}_Xs z=JD@8-0!R6=TnH-8TELD38barbhKF;zr7H-N%+1g>}6(e1wfbt0OTkD ze&X<42H73vhrR9PotS zpuyk%{|o^D1pos8zM|m(dI$m#6d)KtC;-8!K@{6oBE> zQVIenD4<|~LID&iQ-C7?M*)rjoC0Z6fvM0C3l_s{oZ5$KD1u-JLO~Fm+7@~!il7*R zQVri3#Tea4MQIq1;cQvkZ?ZO7J3v4 zM!})3a9qd&1~Ht1;ZR4YG!y{xg6U9bI4zVC>H%rOYA6Tf1P6u_Le>xrl7^L#7UTgN zLu?3*Q&&XIhGFPK!{NrDkZ?ZO7J8I}OGef8VBgf>!3wD|A8du1dmuDb^#kpwr3Z3Q zmvAs473qL9bteZrsK5u%@H)UPLGj>Bu$7T#wJp5RhlA~{9jN#F{~C~}#5`ee*WAc} z7&`GRRmrzQku!SMqHV=CpxgS#V+sG&d-%+Qz@oeQ@x>zr5<{=)uL|+F2Blu9)DQTw zvo@n8^yl~8@sDn)A=?^PLgqJ~X)Q3I2K~o2oi4b>#xrssK5oswkT^^e_KNGRD4FqY zLXc{CjjANz)|)H0yjFF;M=~&2NafPo{F?tjHz@Y2T#si54K5^0sOYMu6zeFZw_-zDF#m}g$| zUirWR11B9R))PyN4(3>8>doI`xFTwO*P62I7ghUPH&pm&%s$_}rHVIWJ9FNFM8Eaz H3=Gx)?!i0) literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/left.png b/services/console-proxy/server/images/left.png new file mode 100644 index 0000000000000000000000000000000000000000..fb7506618d7c3db40d02c31c3fdad17f5c6b3bd0 GIT binary patch literal 3024 zcmV;>3orDEP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}0002%NklzoL|HW`rOdWXls`3UQ>JMn-*tmxN`y92)&a$w*~Y{s zY`f#>;HR-QyF24(A+nAP2}fWJ1n4%si5i;13K*4)cch zh9qHTXg6Wz>6u48GrSq*4fXc>Y^sX$@d+9o9W%XuV7Om|*4#qR*ruy&{A Sd=wA>0000OZy#r8XODmo zcQ0>uU;ntcIFGRC?Ck9H+yW;jr;Ln@kkGJ*tiq(EWDozKjEqc=kVucvsECwI58r^u zm^eQ_zmSj+e}DgstgOhWsEERv$b{s?^vv{}e9!odxVZSp$jI2VOpoA*kcg<* z+_;2955EA{PLLGVs{_k#GHbJ+~V|{T#v|v zh`jQoloZdD{P3dMn1V_VzrdiNpoF~Q)YQ~~fB?_f)RdHzz`(%Z;9!P92^4>_fZe47 zB0+J&z<#}fozIhtp{>1xfla=NK|z>#;-tyU>}+xha!j-5%$+O1AjB>#p=fTWCb&w? z&RkJKxQ$)R(L`0x#@5}|Mo-nmQLK$mLtj_R!phRc(#k?hS6`z|Q%P1uTGT<=S=m8U zT18f=O>(V|7DGaUp>j?hTI3to(IX0)ieNZ)W9{ z;9+s#cVyz@4$G)mka(1dk5hnWO2PuAW?9|@k;aP&?93Ax86*M)7A!kE+k#Ptalrx= Yzd39w28|6%JQuojGA(Hk;9#%@0IxicasU7T literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/minimize_button_hover.gif b/services/console-proxy/server/images/minimize_button_hover.gif new file mode 100644 index 0000000000000000000000000000000000000000..7837ed00baf57a8978cf13cafcc71b456a1f7b30 GIT binary patch literal 227 zcmZ?wbhEHb6k_0KIKsf-?(S}HZ=aNu6c-m485x<8k&&C5TU1mO5)yLn-aR)rH)m(( z?CfkOCnrBYKYxG!prD|@z`)eh)RdHz;NakZfB*)(K=CIFSY8K2g6w2qO;}(M;+-%r zV}U}_2N8Fj^X&^P`jg+(+&6A)Ti~E0)vwlAbTWs_r+Gz*m+TCV8*9qE4HWm?Z>gBW vpzPDIrRpr(g4-XySl{E*dQp0U={%ng6L)z97q1|Xkcg_LCIbtPB7-#mJBdP0 literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/notready.jpg b/services/console-proxy/server/images/notready.jpg new file mode 100644 index 0000000000000000000000000000000000000000..406599cd595865c8d4dcd4ec79a0cefc370bcaeb GIT binary patch literal 1827 zcmbV~c{mh!7{`Bek1@jxGeg=E8dsJ|NkUIThRxC8DoT!`hLDV-7*UR7-8nYpm>ka} zAA`6*kRmt0oLP+I`yR*}r!C{@(BNyuau7zVGvXp69ppZ0R+?+uKrY z0R#d8hh+yW34nPx_j^bgYKUG zfx)5Sk!R2O0^#_t6K~$Wo17B={_&65PoE`U=9al20QMKwH?n_m5oBCYI2;B?EptJj zkut*waD>JNq|y!-luxL#=0*mZXq9=bu0u}Cc#nv5BGav_>W5o;iedae5q%Sb^ z(AylNOzA7UlTpJx_E;s>zJGBe{jDH4Bc^Jy1pD4$)Po+}PtLbHXM<(o-Zk-dB@}6I zF&*=H#nOw(Y7_v?dW2#4UZCc_CKs(+*>|JNQSGtDyT#cPPX#60OTq1njnQ?x1xVXM^PW_xf#2R4)4BR*+joQ49lft}o zy!mSLC4ql#>4uDk^Sd)^DpPivUkDA!4KWWDT3DSq62pqy*HYxM-^-C&>vy^h(=(+* z;nHE6>t7a=P?bb)`m3(!i*sEg(Ti0PCbz2D#o69=?_){1gN_boMM!HOj;X$)vmVdU z`wWd+G%zzN))*W1S^~K3t7a`Lq-Bf5g%y(6HY1VOrE7xsT6g&hHAj6bEEpqxgExeV z6EE7zJ~g}ck!?MA2T0CtK_6U^oRB28ThijnvwZRuOuwZfzx=GKSni2Xy!qh_^P|&z zO2WgFZS=H9#n%28HSaNgI@g;$;U}4Plk|#VU90g;B4*WiiBga^xyf$)f_3ZA4CPje zR(rZDGA!N@`2?k^TIMY29hZ9IpesN8Aj$fquGswp+dNY3PFEU}4G64`xEHyYX1$9v z)k!KXU~c-YNIG2qG~|r?tE|b-A)m*l-5MJ#FH1CDf561<7j3!V$Ps)w(P|MRl%9Af zxTE9VmfE9%Ow@Lw1QdE@p`2rf$L=Q`c>vwF8ZM!V=7l}6Mq=yLiUYC62x(J_EvxiK zT{tq^oK3sgd^;-GFxb;wE8M`Pr_*2cU~PWZ@i#Txv@2%ZK?=7;d_6hXsG>P*#-Sj> zgYQ;o*IGa^I7H#E(F&MyrkRI&>2#=HYFYy5R)@ft@KuX-Oi%vUD4#zXv^&tUfYN8R zg;SSPo<<8ZVNwW9FDHxf!}U2=QYu2U%X;H1B1Ycep4%ACECzOS^Q7BWsy9s3CN!{o zJEKDq8;r2;*okHad+ChWbu}-h@_d~$FE>y_DMOv6c3p?2isK(mdGsu_8F|-=KabZh z&`Nj=Rzz4Kr97{@N>b!$_NeakYS*9B(s;xJPvr?}=zHx>I$LXXE7NB~I16KAUL4I| W8XRsdB&6zAuhlJ01>fVP=YIlBFcT{P literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/play_button.gif b/services/console-proxy/server/images/play_button.gif new file mode 100644 index 0000000000000000000000000000000000000000..6f483082ecb33d34bb09dd744ff77521d6a54aa3 GIT binary patch literal 657 zcmZ?wbhEHb6k_0Kc*ekBZ*TAJ?w*vC6c-m~@8A#;5_0d}y~xPOjEoF7H#c`LZ)a!c z+}vDu4-a>5A9qjBqN3uWq9P|JC$H4P?CflhfMCz4q};rGkDzc5-vB>9KM%ivqT&*N zfB&4q;&Yd;rWKaD`}h?V7iVN;c|^o}#-_S^dZp(TCT8UY1O%iNR|E$KM`RcK6gGNB zCb)b1=H}-4p=j5iQrbbt_$0n!6C8c;KW_$PthZNL=*L3GK_atWKgoK6X6cpv;7p&WR*fTkI z)z)1eA(8RrjY%nK=dN5!E3S+$sR@Zqj4!T^NYC}kt&S(@*$mi#;!hT^unvd>#R&uZy9Op^O>Ty^_6`P4)+Pp74nYAW z8G9Ke0RcflCQd0?DIT6AaZ@=5IbT0iVsilrZ`TaK5cexcFW^8QocIgp5 z@7fsuN~#D~T&Na0>?gtc_s3Um$HOPYm{X#BHe5LB;>7TmY0rTKMz1Lr!d_=MJ}NA1 zVfibPGr_URms32bPa|VOz`3S6ewmyV4UE36JbW2ybC?qxo7nh8EF>N~G;%KFR9Ui6 WA;ndZDdKv9YJtW9My^H%25SKAOWz&< literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/play_button_hover.gif b/services/console-proxy/server/images/play_button_hover.gif new file mode 100644 index 0000000000000000000000000000000000000000..6689e3eec8145fca0cb0179380228b77bd320bf1 GIT binary patch literal 243 zcmZ?wbhEHb6k_0KIKsf-?(S}HZ=aNul#!7U7Z;bCn;RJ!84?mwR8(~D-aR)rH&0Jb zXJ_Z^>})3|r*r4d`TP6(`T3=$rUnKE1_uWR1qCH0CKeVJ1_T7GTD5BB%9U%@tjWyG zOi4*OfBrlJ1B2pE79h#MpaUX6b~3OQEO1EhNtljAhIHOa^h)I6}dU@u9axa=I>npw8nt(UV%f^<__jNWtGeF qj^8$|spaFUY^)XKsqD&Ulx5^;>CWfk6Xq2XQPtFBVB=L}um%8Cp;dVR literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/right.png b/services/console-proxy/server/images/right.png new file mode 100644 index 0000000000000000000000000000000000000000..4abac818410842f8fdb83eced02d0034451cab7d GIT binary patch literal 3131 zcmV-B48-$^P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}00042NklxfWM?K8xy9psNBBP8o8igv`NaZ;Dza7^ zd9ROrV+->7@nwOP@%{6QoBJWk#R}wh^lX8ZvE9v4RTVC;uE9w?HzUe;8GmALu!*{^ z!J?|oh(hKeAjAk3)zoZg4BSM7n83g3%xoxR)`Vr?!UC(L4iQaE_Q}%w21kUHzz~z! zP`aXHN?=IQY-pX}n7|nA&Y_VeM5MF;A4gASLm`6>@%BCjAtpm3&Mm{zi8C7t5fKOg zI$B_jc<&(s&^d25RIX?T5drBfu+|B75YRhY(OLt0FCa6T4TX>dodZb=taXA(f@Eek zlyS7bgK|KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0004hNklKkoVC>7qo*AcL4~OP`mhcvz=2*VAdvLfA00e0VLygupG+KWzyZLj+ z009Vd|L;L3KTb7SEXlrJTrofZ!fdXN)ODRcA05l5{fr_2!d&0})rYNj>ib?k?s|#< z2sO)DxfJ=hd!hz_*a4as*3cLoSR{0qq4f})U+kl3^=kFe;(^sI=K%$)oJ&*-GM z+D?zqD38F<^z2-(%(9TI;-sYHkkHWBqH52S{P>FI+`RmNiq7pvPy6}#rRNqTrKCl* z&x}h)(FPRGT^C*%}HMn!qXrF(?M1Ox=c)%64h2BwzR1qB6VWMo8k&h<(! z&dtqDNl8gfO?CJ2o3d(4aBwigzyZadEMUuZKqM$m7}(!5aPWF^F|@UJFtCd?F$nsx zOq?{Cg@avNP&?3ALt0drZ=tZLw1zQ1g8+w+se+D$p}CK_p@oiusZbk-vaY17qq?k@ zth%GCq^@!suZzC8lYx<*zn+nSleoT1n}eIZl&U4`lP8v{Quc0bVk&kvN+w!QwM>+3 z>{Qwqnb|mH6w7!t4N}}W_&9lZZkRV5Wa64;NQE4`lS1byw4NX&Z*fm^leq?A-Yrz785TAs384DDW3xRbh-gaR1%!>V+F$f39jQaO7%gW0d4+ cYhn~<cs$TEn7Iz;%cMlJDPcP4;oZQ@8&!{Aiu$YXD43CgVcW+;hfDk7qr|j(Pocw~w zj@h|+`Oy`v;q?UY}M*;Q2(l^!!GDXOTxZewKR z7v})?jKYxG!)YR0VprC+&fZ*WZl$4afz(5APK=CIFSY8K2g6w2qO<7>z;hiuq zV}U}_2LVr=^PLMU`jg+(+&5=zYjDt!>Q`$lI+?@e)4Za@OLhj|jhw{j6KTF38)ifc zbo(}JnYAWt@ofgPKfLRAOLFbD6Nr5AmQjR}yRx2vi$|1KKv-2%lYyOAk--`O8$&_% literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/images/winlog.png b/services/console-proxy/server/images/winlog.png new file mode 100644 index 0000000000000000000000000000000000000000..c7668c52f532f703f9dd8e4655fdb31dac58a9d5 GIT binary patch literal 2629 zcmV-L3cB@)P) zc&5)Z@6<^gI}azWy?*V>`41%AK}MmpJ<-8*x>`ObH~a~)=kq5t;5 z>3`s`l7D9I8hGz7j7WqoacRgJR$fTa*rhpn8&7#7N@@|sX4&=ln@-9(R4iEWsTD;p zITnq5^JcPSnpRfYRIb)YhD7rVHHu?JMiW(Q0lt&e3L@Hj-x#eg#28hg;9E;?Hy$ISgyk~w zeLl; z+YnCnM2fZ@IxzEtE(l}2tD3kmmg2+QlD?O*$mtX0X2C!o7yu(Ioq)hPkv!Z`6#2o@ za`EW(x6oH4O%E>CX1^m;;qy99{n1G4geE;NwRWU5!;OyI-U#HKejzpe1PEgr?xVPl8Qi0kFD z)-KmhkxbU0=E^9N20p8OazTTe=*u>E9#IB$K6hs6Wu&XGg4mF0uY%|>>mIJ0v8Ik^ zE>u#-E=L+D%3MeZDg?esO1i_S`<_-s&(7qCK*=zATFIt^L`ID^MdxxZoE6 zz(6cQTzlkLEplwU46=DCFdK@}()7ylr1GmE9?N~P3qUhLACcpe6SJ4Te$c;RIv(ET z6vl2J=o=dFip8vb%~0|33Rk}yp1tthYsP>1g}(NI&sm?ne?U#E-Gllxixsj1a)~y>D z@$#O?<$TKLJn}uXOUA1Po3Gv9*t_qE{NnV}ZFgz7J~?xz?_zL?7|M^4Ou+VeHw5dQ zCoEXDCdc(@dR86*V@u3&yXMseaYMG#y+Ap))usJBWi}SAlns@GLiCi-a7$&8D`WB_ z>gNshaFFsoQ{H&pL+s?wfAiWK)gR3-o_>7i_Wr&?-X$BNw4ft_Y6;l8?P%hG22-4Z zvYKy|)T^%VhD;>-VLcG*`yqF9Tcs7>RhLxMn7>9PUT71#>`E92jk=Kxx6*PW%VIUO zGs_=*`r*f4d42NY{#TEme#m%@PdILCV>;Ebf;h&yNX{>%mn)_I$;PaERF>p{uwFfu z>GONT~t>;$WIZ)euM2IEw9c%3HI(MYLHiA-d1RMN4Wx9`M7<)C!lbmvcQd)Xo5v?9 zl`lh6Rnj#WcE`}$?UA`-7uW=2va5wPAZ&nupWh#}VyFo}f&mkOflyG>P5{JM1GRJ3 z-Tnw8>V?6@<0hK^hRN(87B2!=fSJ=ktn|*iwvFeuEz%8iMQqVjrEh9sIheGsbPu~) z6RsniR$bo{v?H?76$)%HSghn5Qq5OLx;=rG+v zptQP<(NiM=L#Bnn(TZ_zir$*@4%Hhg%RXFJV{!*U`yCLL5StqH@Fv;r(QV*(#^PN@ zVlc7-2B4HKh-Rc|L!bC7RsX!rS}%u{oCmrXZx0}*-37r-1FEi*X(D+;p*>@&vEk_C zWjgFw3}ejDsa7kKueVN3998wXslIfkp3CKQHeV=o`ZnKQ%8fo?53IX(ET?B}er_)7mF{FH z15bK6Q3?hY$%xP;D1q*lK{xSRBSQN-bZW^zF?C_#eChJkZHYf3l6~m-Du{)JGe4gC z^$&lhn~M{8S`|hYS;Z`6#d;d_1}N<{o@><>HqD2dA6LN*nQAN!5w)|o1gl;apGtRqy>tGiI zWn=TD5RTB7M%m)mSf!OR>lePH=9V39Cp%rNkv$>|1Wh4;bnuB_0O4VF%v#0zo8#pp zL8F$xGQLZQq#n6d5J@}zvm@#2NZas-xwcL1XMXyJ&3fnX;v+k*X>844pOEb0RG0wK zm^F#T8)`$`=T*A=i`T*z#wV*Y=*Skn{$P^$3XrMQa=hQ8yS=wV@~pUhq_X9SmuE+R zHF{?8mY@Y&Pz?u(7kjI8tP#W+&@LYddfjkwZi1?6@SbxiUX5=5il n*~`Xj|HAnHe+~4GzX>n^g*+DN(p{zl00000NkvXXu0mjf;#L?^ literal 0 HcmV?d00001 diff --git a/services/console-proxy/server/js/ajaxkeys.js b/services/console-proxy/server/js/ajaxkeys.js new file mode 100644 index 00000000000..2ecf5b561e0 --- /dev/null +++ b/services/console-proxy/server/js/ajaxkeys.js @@ -0,0 +1,77 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +/* + * This var contains the limited keyboard translation tables. + * This is the table that users can modify to make special keyboard to work properly. + * They are used by the ajaxviewer.js + */ + +//client event type. corresponds to events in ajaxviewer. +X11_KEY_CIRCUMFLEX_ACCENT = 0x5e; // ^ +X11_KEY_YEN_MARK = 0xa5; +X11_KEY_OPEN_BRACKET = 0x5b; +X11_KEY_CLOSE_BRACKET = 0x5d; +X11_KEY_COLON = 0x3a; +X11_KEY_REVERSE_SOLIUS = 0x5c; // another back slash (back slash on JP keyboard) +X11_KEY_CAPSLOCK = 0xffe5; +X11_KEY_SEMI_COLON = 0x3b; +X11_KEY_SHIFT = 0xffe1; +X11_KEY_ADD = 0x2b; + +KEY_DOWN = 5; +KEY_UP = 6; + +//JP keyboard type +// +var keyboardTables = [ + {tindex: 0, keyboardType: "EN-Cooked", mappingTable: + {X11: [ {keycode: 222, entry: X11_KEY_CIRCUMFLEX_ACCENT}, + {keycode: 220, entry: X11_KEY_YEN_MARK}, + {keycode: 219, entry: X11_KEY_OPEN_BRACKET}, + {keycode: 221, entry: X11_KEY_CLOSE_BRACKET}, + {keycode: 59, entry: X11_KEY_COLON, browser: "Firefox"}, + {keycode: 186, entry: X11_KEY_COLON, browser: "Chrome"}, + {keycode: 9, entry: 9, guestos: "XenServer"}, + {keycode: 226, entry: X11_KEY_REVERSE_SOLIUS}, + {keycode: 240, entry: [ + {type: KEY_DOWN, code: X11_KEY_CAPSLOCK, modifiers: 0 }, + {type: KEY_UP, code: X11_KEY_CAPSLOCK, modifiers: 0 }, + ] + }, + ], + keyPress: [ + {keycode: 59, entry: [ + {type: KEY_DOWN, code: X11_KEY_SEMI_COLON, modifiers: 0 }, + {type: KEY_UP, code: X11_KEY_SEMI_COLON, modifiers: 0 }, + ] + }, + {keycode: 43, entry: [ + {type: KEY_DOWN, code: X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: KEY_DOWN, code: X11_KEY_ADD, modifiers: 0, shift: false }, + {type: KEY_UP, code: X11_KEY_ADD, modifiers: 0, shift: false }, + {type: KEY_UP, code: X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: KEY_DOWN, code: X11_KEY_ADD, modifiers: 0, shift: true }, + {type: KEY_UP, code: X11_KEY_ADD, modifiers: 0, shift: true }, + ] + }, + ] + } + } ] + diff --git a/services/console-proxy/server/js/ajaxviewer.js b/services/console-proxy/server/js/ajaxviewer.js new file mode 100644 index 00000000000..e95615d8946 --- /dev/null +++ b/services/console-proxy/server/js/ajaxviewer.js @@ -0,0 +1,1444 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +var g_logger; + +///////////////////////////////////////////////////////////////////////////// +// class StringBuilder +// +function StringBuilder(initStr) { + this.strings = new Array(""); + this.append(initStr); +} + +StringBuilder.prototype = { + append : function (str) { + if (str) { + this.strings.push(str); + } + return this; + }, + + clear : function() { + this.strings.length = 1; + return this; + }, + + toString: function() { + return this.strings.join(""); + } +}; + + +function getCurrentLanguage() { + if(acceptLanguages) { + var tokens = acceptLanguages.split(','); + if(tokens.length > 0) + return tokens[0]; + + return "en-us"; + } else { + return "en-us"; + } +} + +///////////////////////////////////////////////////////////////////////////// +// class KeyboardMapper +// +function KeyboardMapper() { + this.mappedInput = []; + this.jsX11KeysymMap = []; + this.jsKeyPressX11KeysymMap = []; +} + +// +// RAW keyboard +// Primarily translates KeyDown/KeyUp event, either as is (if there is no mapping entry) +// or through mapped result. +// +// For KeyPress event, it translates it only if there exist a mapping entry +// in jsX11KeysymMap map and the entry meets the condition +// +// COOKED keyboard +// Primarily translates KeyPress event, either as is or through mapped result +// It translates KeyDown/KeyUp only there exists a mapping entry, or if there +// is no mapping entry, translate when certain modifier key is pressed (i.e., +// CTRL or ALT key +// +// Mapping entry types +// direct : will be directly mapped into the entry value with the same event type +// boolean : only valid for jsX11KeysymMap, existence of this type, no matter true or false +// in value, corresponding KeyDown/KeyUp event will be masked +// array : contains a set of conditional mapping entry +// +// Conditional mapping entry +// +// { +// type: , code: , modifiers: , +// shift : , -- match on shift state +// guestos : , -- match on guestos type +// browser: , -- match on browser +// browserVersion: -- match on browser version +// } +// +KeyboardMapper.KEYBOARD_TYPE_RAW = 0; +KeyboardMapper.KEYBOARD_TYPE_COOKED = 1; + +KeyboardMapper.prototype = { + + setKeyboardType : function(keyboardType) { + this.keyboardType = keyboardType; + + if(keyboardType == KeyboardMapper.KEYBOARD_TYPE_RAW) { + // intialize keyboard mapping for RAW keyboard + this.jsX11KeysymMap[AjaxViewer.JS_KEY_CAPSLOCK] = AjaxViewer.X11_KEY_CAPSLOCK; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACKSPACE] = AjaxViewer.X11_KEY_BACKSPACE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_TAB] = AjaxViewer.X11_KEY_TAB; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ENTER] = AjaxViewer.X11_KEY_ENTER; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ESCAPE] = AjaxViewer.X11_KEY_ESCAPE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_INSERT] = AjaxViewer.X11_KEY_INSERT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DELETE] = AjaxViewer.X11_KEY_DELETE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_HOME] = AjaxViewer.X11_KEY_HOME; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_END] = AjaxViewer.X11_KEY_END; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEUP] = AjaxViewer.X11_KEY_PAGEUP; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEDOWN] = AjaxViewer.X11_KEY_PAGEDOWN; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_LEFT] = AjaxViewer.X11_KEY_LEFT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_UP] = AjaxViewer.X11_KEY_UP; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_RIGHT] = AjaxViewer.X11_KEY_RIGHT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DOWN] = AjaxViewer.X11_KEY_DOWN; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F1] = AjaxViewer.X11_KEY_F1; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F2] = AjaxViewer.X11_KEY_F2; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F3] = AjaxViewer.X11_KEY_F3; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F4] = AjaxViewer.X11_KEY_F4; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F5] = AjaxViewer.X11_KEY_F5; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F6] = AjaxViewer.X11_KEY_F6; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F7] = AjaxViewer.X11_KEY_F7; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F8] = AjaxViewer.X11_KEY_F8; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F9] = AjaxViewer.X11_KEY_F9; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F10] = AjaxViewer.X11_KEY_F10; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F11] = AjaxViewer.X11_KEY_F11; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F12] = AjaxViewer.X11_KEY_F12; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_SHIFT] = AjaxViewer.X11_KEY_SHIFT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_CTRL] = AjaxViewer.X11_KEY_CTRL; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ALT] = AjaxViewer.X11_KEY_ALT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_GRAVE_ACCENT] = AjaxViewer.X11_KEY_GRAVE_ACCENT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_SUBSTRACT] = AjaxViewer.X11_KEY_SUBSTRACT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ADD] = AjaxViewer.X11_KEY_ADD; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_OPEN_BRACKET] = AjaxViewer.X11_KEY_OPEN_BRACKET; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_CLOSE_BRACKET] = AjaxViewer.X11_KEY_CLOSE_BRACKET; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACK_SLASH] = AjaxViewer.X11_KEY_BACK_SLASH; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_SINGLE_QUOTE] = AjaxViewer.X11_KEY_SINGLE_QUOTE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_COMMA] = AjaxViewer.X11_KEY_COMMA; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_PERIOD] = AjaxViewer.X11_KEY_PERIOD; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_FORWARD_SLASH] = AjaxViewer.X11_KEY_FORWARD_SLASH; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DASH] = AjaxViewer.X11_KEY_DASH; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_SEMI_COLON] = AjaxViewer.X11_KEY_SEMI_COLON; + + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD0] = AjaxViewer.X11_KEY_NUMPAD0; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD1] = AjaxViewer.X11_KEY_NUMPAD1; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD2] = AjaxViewer.X11_KEY_NUMPAD2; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD3] = AjaxViewer.X11_KEY_NUMPAD3; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD4] = AjaxViewer.X11_KEY_NUMPAD4; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD5] = AjaxViewer.X11_KEY_NUMPAD5; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD6] = AjaxViewer.X11_KEY_NUMPAD6; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD7] = AjaxViewer.X11_KEY_NUMPAD7; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD8] = AjaxViewer.X11_KEY_NUMPAD8; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_NUMPAD9] = AjaxViewer.X11_KEY_NUMPAD9; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DECIMAL_POINT] = AjaxViewer.X11_KEY_DECIMAL_POINT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DIVIDE] = AjaxViewer.X11_KEY_DIVIDE; + + this.jsX11KeysymMap[AjaxViewer.JS_KEY_MULTIPLY] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0 }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ASTERISK, modifiers: 0 }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ASTERISK, modifiers: 0 }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0 } + ]; + + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ADD] = false; + this.jsKeyPressX11KeysymMap = []; + this.jsKeyPressX11KeysymMap[61] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false } + ]; + this.jsKeyPressX11KeysymMap[43] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true } + ]; + } else { + // initialize mapping for COOKED keyboard + this.jsX11KeysymMap[AjaxViewer.JS_KEY_CAPSLOCK] = AjaxViewer.X11_KEY_CAPSLOCK; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACKSPACE] = AjaxViewer.X11_KEY_BACKSPACE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_TAB] = AjaxViewer.X11_KEY_TAB; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ENTER] = AjaxViewer.X11_KEY_ENTER; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ESCAPE] = AjaxViewer.X11_KEY_ESCAPE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_INSERT] = AjaxViewer.X11_KEY_INSERT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DELETE] = AjaxViewer.X11_KEY_DELETE; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_HOME] = AjaxViewer.X11_KEY_HOME; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_END] = AjaxViewer.X11_KEY_END; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEUP] = AjaxViewer.X11_KEY_PAGEUP; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_PAGEDOWN] = AjaxViewer.X11_KEY_PAGEDOWN; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_LEFT] = AjaxViewer.X11_KEY_LEFT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_UP] = AjaxViewer.X11_KEY_UP; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_RIGHT] = AjaxViewer.X11_KEY_RIGHT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_DOWN] = AjaxViewer.X11_KEY_DOWN; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F1] = AjaxViewer.X11_KEY_F1; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F2] = AjaxViewer.X11_KEY_F2; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F3] = AjaxViewer.X11_KEY_F3; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F4] = AjaxViewer.X11_KEY_F4; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F5] = AjaxViewer.X11_KEY_F5; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F6] = AjaxViewer.X11_KEY_F6; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F7] = AjaxViewer.X11_KEY_F7; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F8] = AjaxViewer.X11_KEY_F8; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F9] = AjaxViewer.X11_KEY_F9; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F10] = AjaxViewer.X11_KEY_F10; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F11] = AjaxViewer.X11_KEY_F11; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_F12] = AjaxViewer.X11_KEY_F12; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_SHIFT] = AjaxViewer.X11_KEY_SHIFT; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_CTRL] = AjaxViewer.X11_KEY_CTRL; + this.jsX11KeysymMap[AjaxViewer.JS_KEY_ALT] = AjaxViewer.X11_KEY_ALT; + } + }, + RawkeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) { + if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) { + + // special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del + if(code == AjaxViewer.JS_KEY_INSERT) { + if((modifiers & AjaxViewer.ALT_KEY_MASK) != 0 && (modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) { + this.mappedInput.push({type : eventType, code: 0xffff, modifiers: modifiers}); + return; + } + } + + var X11Keysym = code; + if(this.jsX11KeysymMap[code] != undefined) { + X11Keysym = this.jsX11KeysymMap[code]; + if(typeof this.jsX11KeysymMap[code] == "boolean") { + return; + } else if($.isArray(X11Keysym)) { + for(var i = 0; i < X11Keysym.length; i++) { + // How to set the guestos, browser, version value??? add later + if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser, browserVersion)) { + this.mappedInput.push(X11Keysym[i]); + } + } + } else { + this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); + } + } else { + this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); + } + + // special handling for ALT/CTRL key + if(eventType == AjaxViewer.KEY_UP && (code == AjaxViewer.JS_KEY_ALT || code == code == AjaxViewer.JS_KEY_CTRL)) + this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers}); + + } else if(eventType == AjaxViewer.KEY_PRESS) { + var X11Keysym = code; + X11Keysym = this.jsKeyPressX11KeysymMap[code]; + if(X11Keysym) { + if($.isArray(X11Keysym)) { + for(var i = 0; i < X11Keysym.length; i++) { + if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser)) + this.mappedInput.push(X11Keysym[i]); + } + } else { + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: X11Keysym, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: X11Keysym, modifiers: modifiers}); + } + } + } + }, + + CookedKeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) { + if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) { + + // special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del + if(code == AjaxViewer.JS_KEY_INSERT) { + if((modifiers & AjaxViewer.ALT_KEY_MASK) != 0 && (modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) { + this.mappedInput.push({type : eventType, code: 0xffff, modifiers: modifiers}); + return; + } + } + + var X11Keysym = code; + if(this.jsX11KeysymMap[code] != undefined) { + X11Keysym = this.jsX11KeysymMap[code]; + if(typeof this.jsX11KeysymMap[code] == "boolean") { + return; + } else if($.isArray(X11Keysym)) { + for(var i = 0; i < X11Keysym.length; i++) { + if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser, browserVersion)) { + this.mappedInput.push(X11Keysym[i]); + } + } + } else { + this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); + } + } else { + if((modifiers & (AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK)) != 0) { + this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers}); + } + } + + // special handling for ALT/CTRL key + if(eventType == AjaxViewer.KEY_UP && (code == AjaxViewer.JS_KEY_ALT || code == code == AjaxViewer.JS_KEY_CTRL)) + this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers}); + + } else if(eventType == AjaxViewer.KEY_PRESS) { + // special handling for * and + key on number pad + if(code == AjaxViewer.JS_NUMPAD_MULTIPLY) { + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: 42, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: 42, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); + return; + } + + if(code == AjaxViewer.JS_NUMPAD_PLUS) { + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: 43, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: 43, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers}); + return; + } + + // ENTER/BACKSPACE key should already have been sent through KEY DOWN/KEY UP event + if(code == AjaxViewer.JS_KEY_ENTER || code == AjaxViewer.JS_KEY_BACKSPACE) + return; + + if(code > 0) { + var X11Keysym = code; + X11Keysym = this.jsKeyPressX11KeysymMap[code]; + if(X11Keysym) { + if($.isArray(X11Keysym)) { + for(var i = 0; i < X11Keysym.length; i++) { + if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser)) + this.mappedInput.push(X11Keysym[i]); + } + } else { + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: X11Keysym, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: X11Keysym, modifiers: modifiers}); + } + } else { + // if there is no mappting entry, use the JS keypress code directly + this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: code, modifiers: modifiers}); + this.mappedInput.push({type : AjaxViewer.KEY_UP, code: code, modifiers: modifiers}); + } + } + } + }, + + inputFeed : function(eventType, code, modifiers, guestos, browser, browserVersion) { + if(this.keyboardType == KeyboardMapper.KEYBOARD_TYPE_RAW) + this.RawkeyboardInputHandler(eventType, code, modifiers, guestos, browser, browserVersion); + else + this.CookedKeyboardInputHandler(eventType, code, modifiers, guestos, browser, browserVersion); + }, + + getMappedInput : function() { + var mappedInput = this.mappedInput; + this.mappedInput = []; + return mappedInput; + }, + + isConditionalEntryMatched : function(eventType, code, modifiers, entry, guestos, browser, browserVersion) { + if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) { + // for KeyDown/KeyUp events, we require that the type in entry should match with + // the real input + if(entry.type != eventType) + return false; + } + + // check conditional match + if(entry.shift != undefined) { + var shift = ((modifiers & AjaxViewer.SHIFT_KEY_MASK) != 0 ? true : false); + if(entry.shift ^ shift) + return false; + } + + if(entry.guestos != undefined) { + if(entry.guestos != guestos) + return false; + } + + if(entry.browser != undefined) { + if(entry.browser != browser) + return false; + } + + if(entry.browserVersion != undefined) { + if(entry.browserVersion != browserVersion) + return false; + } + + return true; + }, + + isModifierInput : function(code) { + return $.inArray(code, [AjaxViewer.ALT_KEY_MASK, AjaxViewer.SHIFT_KEY_MASK, AjaxViewer.CTRL_KEY_MASK, AjaxViewer.META_KEY_MASK]) >= 0; + } +}; + +///////////////////////////////////////////////////////////////////////////// +// class AjaxViewer +// +function AjaxViewer(panelId, imageUrl, updateUrl, tileMap, width, height, tileWidth, tileHeight) { + // logging is disabled by default so that it won't have negative impact on performance + // however, a back door key-sequence can trigger to open the logger window, it is designed to help + // trouble-shooting + g_logger = new Logger(); + + // g_logger.enable(true); + // g_logger.open(); + + var ajaxViewer = this; + this.imageLoaded = false; + this.fullImage = true; + this.imgUrl = imageUrl; + this.img = new Image(); + $(this.img).attr('src', imageUrl).load(function() { + ajaxViewer.imageLoaded = true; + }); + + this.updateUrl = updateUrl; + this.tileMap = tileMap; + this.dirty = true; + this.width = width; + this.height = height; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.maxTileZIndex = 1; + + this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_ENGLISH; + this.keyboardMappers = []; + + this.timer = 0; + this.eventQueue = []; + this.sendingEventInProgress = false; + + this.lastClickEvent = { x: 0, y: 0, button: 0, modifiers: 0, time: new Date().getTime() }; + + if(window.onStatusNotify == undefined) + window.onStatusNotify = function(status) {}; + + this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); +// this.setupKeyboardTranslationle(); + this.setupKeyboardTranslationTable(this.keyboardMappers); + this.setupUIController(); +} + +// client event types +AjaxViewer.MOUSE_MOVE = 1; +AjaxViewer.MOUSE_DOWN = 2; +AjaxViewer.MOUSE_UP = 3; +AjaxViewer.KEY_PRESS = 4; +AjaxViewer.KEY_DOWN = 5; +AjaxViewer.KEY_UP = 6; +AjaxViewer.EVENT_BAG = 7; +AjaxViewer.MOUSE_DBLCLK = 8; + +// use java AWT key modifier masks +AjaxViewer.SHIFT_KEY_MASK = 64; +AjaxViewer.CTRL_KEY_MASK = 128; +AjaxViewer.META_KEY_MASK = 256; +AjaxViewer.ALT_KEY_MASK = 512; +AjaxViewer.LEFT_SHIFT_MASK = 1024; +AjaxViewer.LEFT_CTRL_MASK = 2048; +AjaxViewer.LEFT_ALT_MASK = 4096; + +AjaxViewer.EVENT_QUEUE_MOUSE_EVENT = 1; +AjaxViewer.EVENT_QUEUE_KEYBOARD_EVENT = 2; + +AjaxViewer.STATUS_RECEIVING = 1; +AjaxViewer.STATUS_RECEIVED = 2; +AjaxViewer.STATUS_SENDING = 3; +AjaxViewer.STATUS_SENT = 4; + +AjaxViewer.KEYBOARD_TYPE_ENGLISH = "us"; +AjaxViewer.KEYBOARD_TYPE_JAPANESE = "jp"; + +AjaxViewer.JS_KEY_BACKSPACE = 8; +AjaxViewer.JS_KEY_TAB = 9; +AjaxViewer.JS_KEY_ENTER = 13; +AjaxViewer.JS_KEY_SHIFT = 16; +AjaxViewer.JS_KEY_CTRL = 17; +AjaxViewer.JS_KEY_ALT = 18; +AjaxViewer.JS_KEY_PAUSE = 19; +AjaxViewer.JS_KEY_CAPSLOCK = 20; +AjaxViewer.JS_KEY_ESCAPE = 27; +AjaxViewer.JS_KEY_PAGEUP = 33; +AjaxViewer.JS_KEY_PAGEDOWN = 34; +AjaxViewer.JS_KEY_END = 35; +AjaxViewer.JS_KEY_HOME = 36; +AjaxViewer.JS_KEY_LEFT = 37; +AjaxViewer.JS_KEY_UP = 38; +AjaxViewer.JS_KEY_RIGHT = 39; +AjaxViewer.JS_KEY_DOWN = 40; +AjaxViewer.JS_KEY_INSERT = 45; +AjaxViewer.JS_KEY_DELETE = 46; +AjaxViewer.JS_KEY_LEFT_WINDOW_KEY = 91; +AjaxViewer.JS_KEY_RIGHT_WINDOW_KEY = 92; +AjaxViewer.JS_KEY_SELECT_KEY = 93; +AjaxViewer.JS_KEY_NUMPAD0 = 96; +AjaxViewer.JS_KEY_NUMPAD1 = 97; +AjaxViewer.JS_KEY_NUMPAD2 = 98; +AjaxViewer.JS_KEY_NUMPAD3 = 99; +AjaxViewer.JS_KEY_NUMPAD4 = 100; +AjaxViewer.JS_KEY_NUMPAD5 = 101; +AjaxViewer.JS_KEY_NUMPAD6 = 102; +AjaxViewer.JS_KEY_NUMPAD7 = 103; +AjaxViewer.JS_KEY_NUMPAD8 = 104; +AjaxViewer.JS_KEY_NUMPAD9 = 105; +AjaxViewer.JS_KEY_MULTIPLY = 106; +AjaxViewer.JS_KEY_ADD = 107; +AjaxViewer.JS_KEY_SUBSTRACT = 109; +AjaxViewer.JS_KEY_DECIMAL_POINT = 110; +AjaxViewer.JS_KEY_DIVIDE = 111; +AjaxViewer.JS_KEY_F1 = 112; +AjaxViewer.JS_KEY_F2 = 113; +AjaxViewer.JS_KEY_F3 = 114; +AjaxViewer.JS_KEY_F4 = 115; +AjaxViewer.JS_KEY_F5 = 116; +AjaxViewer.JS_KEY_F6 = 117; +AjaxViewer.JS_KEY_F7 = 118; +AjaxViewer.JS_KEY_F8 = 119; +AjaxViewer.JS_KEY_F9 = 120; +AjaxViewer.JS_KEY_F10 = 121; +AjaxViewer.JS_KEY_F11 = 122; +AjaxViewer.JS_KEY_F12 = 123; +AjaxViewer.JS_KEY_NUMLOCK = 144; +AjaxViewer.JS_KEY_SCROLLLOCK = 145; +AjaxViewer.JS_KEY_SEMI_COLON = 186; // ; +AjaxViewer.JS_KEY_EQUAL_SIGN = 187; // = +AjaxViewer.JS_KEY_COMMA = 188; // , +AjaxViewer.JS_KEY_DASH = 189; // - +AjaxViewer.JS_KEY_PERIOD = 190; // . +AjaxViewer.JS_KEY_FORWARD_SLASH = 191; // / +AjaxViewer.JS_KEY_GRAVE_ACCENT = 192; // ` +AjaxViewer.JS_KEY_OPEN_BRACKET = 219; // [ +AjaxViewer.JS_KEY_BACK_SLASH = 220; // \ +AjaxViewer.JS_KEY_CLOSE_BRACKET = 221; // ] +AjaxViewer.JS_KEY_SINGLE_QUOTE = 222; // ' +AjaxViewer.JS_NUMPAD_PLUS = 43; +AjaxViewer.JS_NUMPAD_MULTIPLY = 42; +AjaxViewer.JS_KEY_NUM8 = 56; + +// keycode from Japanese keyboard +AjaxViewer.JS_KEY_JP_COLON = 222; // :* on JP keyboard +AjaxViewer.JS_KEY_JP_CLOSE_BRACKET = 220; // [{ on JP keyboard +AjaxViewer.JS_KEY_JP_AT_SIGN = 219; // @` on JP keyboard +AjaxViewer.JS_KEY_JP_OPEN_BRACKET = 221; // [{ on JP keyboard +AjaxViewer.JS_KEY_JP_BACK_SLASH = 193; // \| on JP keyboard +AjaxViewer.JS_KEY_JP_YEN_MARK = 255; + +AjaxViewer.JS_KEY_JP_EQUAL = 109; // -= ON JP keyboard +AjaxViewer.JS_KEY_JP_ACUTE = 107; // ^~ on JP keyboard + +// X11 keysym definitions +AjaxViewer.X11_KEY_CAPSLOCK = 0xffe5; +AjaxViewer.X11_KEY_BACKSPACE = 0xff08; +AjaxViewer.X11_KEY_TAB = 0xff09; +AjaxViewer.X11_KEY_ENTER = 0xff0d; +AjaxViewer.X11_KEY_ESCAPE = 0xff1b; +AjaxViewer.X11_KEY_INSERT = 0xff63; +AjaxViewer.X11_KEY_DELETE = 0xffff; +AjaxViewer.X11_KEY_HOME = 0xff50; +AjaxViewer.X11_KEY_END = 0xff57; +AjaxViewer.X11_KEY_PAGEUP = 0xff55; +AjaxViewer.X11_KEY_PAGEDOWN = 0xff56; +AjaxViewer.X11_KEY_LEFT = 0xff51; +AjaxViewer.X11_KEY_UP = 0xff52; +AjaxViewer.X11_KEY_RIGHT = 0xff53; +AjaxViewer.X11_KEY_DOWN = 0xff54; +AjaxViewer.X11_KEY_F1 = 0xffbe; +AjaxViewer.X11_KEY_F2 = 0xffbf; +AjaxViewer.X11_KEY_F3 = 0xffc0; +AjaxViewer.X11_KEY_F4 = 0xffc1; +AjaxViewer.X11_KEY_F5 = 0xffc2; +AjaxViewer.X11_KEY_F6 = 0xffc3; +AjaxViewer.X11_KEY_F7 = 0xffc4; +AjaxViewer.X11_KEY_F8 = 0xffc5; +AjaxViewer.X11_KEY_F9 = 0xffc6; +AjaxViewer.X11_KEY_F10 = 0xffc7; +AjaxViewer.X11_KEY_F11 = 0xffc8; +AjaxViewer.X11_KEY_F12 = 0xffc9; +AjaxViewer.X11_KEY_SHIFT = 0xffe1; +AjaxViewer.X11_KEY_CTRL = 0xffe3; +AjaxViewer.X11_KEY_ALT = 0xffe9; +AjaxViewer.X11_KEY_GRAVE_ACCENT = 0x60; +AjaxViewer.X11_KEY_SUBSTRACT = 0x2d; +AjaxViewer.X11_KEY_ADD = 0x2b; +AjaxViewer.X11_KEY_OPEN_BRACKET = 0x5b; +AjaxViewer.X11_KEY_CLOSE_BRACKET = 0x5d; +AjaxViewer.X11_KEY_BACK_SLASH = 0x7c; +AjaxViewer.X11_KEY_REVERSE_SOLIUS = 0x5c; // another back slash (back slash on JP keyboard) +AjaxViewer.X11_KEY_SINGLE_QUOTE = 0x22; +AjaxViewer.X11_KEY_COMMA = 0x3c; +AjaxViewer.X11_KEY_PERIOD = 0x3e; +AjaxViewer.X11_KEY_FORWARD_SLASH = 0x3f; +AjaxViewer.X11_KEY_DASH = 0x2d; +AjaxViewer.X11_KEY_COLON = 0x3a; +AjaxViewer.X11_KEY_SEMI_COLON = 0x3b; +AjaxViewer.X11_KEY_NUMPAD0 = 0x30; +AjaxViewer.X11_KEY_NUMPAD1 = 0x31; +AjaxViewer.X11_KEY_NUMPAD2 = 0x32; +AjaxViewer.X11_KEY_NUMPAD3 = 0x33; +AjaxViewer.X11_KEY_NUMPAD4 = 0x34; +AjaxViewer.X11_KEY_NUMPAD5 = 0x35; +AjaxViewer.X11_KEY_NUMPAD6 = 0x36; +AjaxViewer.X11_KEY_NUMPAD7 = 0x37; +AjaxViewer.X11_KEY_NUMPAD8 = 0x38; +AjaxViewer.X11_KEY_NUMPAD9 = 0x39; +AjaxViewer.X11_KEY_DECIMAL_POINT = 0x2e; +AjaxViewer.X11_KEY_DIVIDE = 0x3f; +AjaxViewer.X11_KEY_TILDE = 0x7e; // ~ +AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT = 0x5e; // ^ +AjaxViewer.X11_KEY_YEN_MARK = 0xa5; // Japanese YEN mark +AjaxViewer.X11_KEY_ASTERISK = 0x2a; + +AjaxViewer.getEventName = function(type) { + switch(type) { + case AjaxViewer.MOUSE_MOVE : + return "MOUSE_MOVE"; + + case AjaxViewer.MOUSE_DOWN : + return "MOUSE_DOWN"; + + case AjaxViewer.MOUSE_UP : + return "MOUSE_UP"; + + case AjaxViewer.KEY_PRESS : + return "KEY_PRESS"; + + case AjaxViewer.KEY_DOWN : + return "KEY_DOWN"; + + case AjaxViewer.KEY_UP : + return "KEY_UP"; + + case AjaxViewer.EVENT_BAG : + return "EVENT_BAG"; + + case AjaxViewer.MOUSE_DBLCLK : + return "MOUSE_DBLCLK"; + } + + return "N/A"; +}; + +AjaxViewer.prototype = { + setDirty: function(value) { + this.dirty = value; + }, + + isDirty: function() { + return this.dirty; + }, + + isImageLoaded: function() { + return this.imageLoaded; + }, + + refresh: function(imageUrl, tileMap, fullImage) { + var ajaxViewer = this; + var img = $(this.img); + this.fullImage = fullImage; + this.imgUrl=imageUrl; + + img.attr('src', imageUrl).load(function() { + ajaxViewer.imageLoaded = true; + }); + this.tileMap = tileMap; + }, + + resize: function(panelId, width, height, tileWidth, tileHeight) { + $(".canvas_tile", document.body).each(function() { + $(this).remove(); + }); + $("table", $("#" + panelId)).remove(); + + this.width = width; + this.height = height; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.panel = this.generateCanvas(panelId, width, height, tileWidth, tileHeight); + }, + + start: function() { + var ajaxViewer = this; + this.timer = setInterval(function() { ajaxViewer.heartbeat(); }, 50); + + $(document).bind("ajaxError", function(event, XMLHttpRequest, ajaxOptions, thrownError) { + ajaxViewer.onAjaxError(event, XMLHttpRequest, ajaxOptions, thrownError); + }); + + this.eventQueue = []; // reset event queue + this.sendingEventInProgress = false; + ajaxViewer.installMouseHook(); + ajaxViewer.installKeyboardHook(); + + $(window).bind("resize", function() { + ajaxViewer.onWindowResize(); + }); + }, + + stop: function() { + clearInterval(this.timer); + this.deleteCanvas(); + + this.uninstallMouseHook(); + this.uninstallKeyboardHook(); + this.eventQueue = []; + this.sendingEventInProgress = false; + + $(document).unbind("ajaxError"); + $(window).unbind("resize"); + }, + + sendMouseEvent: function(event, x, y, whichButton, modifiers) { + this.eventQueue.push({ + type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, + event: event, + x: x, + y: y, + code: whichButton, + modifiers: modifiers + }); + this.checkEventQueue(); + }, +//NEW insert the keyboard tables file here +// ajaxKeys.js + + setupKeyboardTranslationTable : function() { + this.keyboardMappers = []; + + var mapper = new KeyboardMapper(); + this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_ENGLISH] = mapper; + mapper.setKeyboardType(KeyboardMapper.KEYBOARD_TYPE_COOKED); + + mapper = new KeyboardMapper(); + this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_JAPANESE] = mapper; + mapper.setKeyboardType(KeyboardMapper.KEYBOARD_TYPE_RAW); + + // JP keyboard plugged in a English host OS +/* + mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_COLON] = AjaxViewer.X11_KEY_COLON; + mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_CLOSE_BRACKET] = AjaxViewer.X11_KEY_CLOSE_BRACKET; + mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_AT_SIGN] = AjaxViewer.X11_KEY_GRAVE_ACCENT; + mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_OPEN_BRACKET] = AjaxViewer.X11_KEY_OPEN_BRACKET; + mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_BACK_SLASH] = AjaxViewer.X11_KEY_REVERSE_SOLIUS; // X11 REVERSE SOLIDUS + mapper.jsX11KeysymMap[AjaxViewer.JS_KEY_JP_YEN_MARK] = AjaxViewer.X11_KEY_YEN_MARK; // X11 YEN SIGN + mapper.jsKeyPressX11KeysymMap[61] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT, modifiers: 0 }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT, modifiers: 0 }, + ]; + + mapper.jsKeyPressX11KeysymMap[43] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_TILDE, modifiers: 0, shift: true }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_TILDE, modifiers: 0, shift: true } + ]; +*/ + +/* Old + // JP keyboard plugged in a Japanese host OS + mapper.jsX11KeysymMap[222] = AjaxViewer.X11_KEY_CIRCUMFLEX_ACCENT; + mapper.jsX11KeysymMap[220] = AjaxViewer.X11_KEY_YEN_MARK; + mapper.jsX11KeysymMap[219] = AjaxViewer.X11_KEY_OPEN_BRACKET; + mapper.jsX11KeysymMap[221] = AjaxViewer.X11_KEY_CLOSE_BRACKET; + mapper.jsX11KeysymMap[59] = AjaxViewer.X11_KEY_COLON; // Firefox + mapper.jsX11KeysymMap[186] = AjaxViewer.X11_KEY_COLON; // Chrome + mapper.jsX11KeysymMap[226] = AjaxViewer.X11_KEY_REVERSE_SOLIUS; // \| key left to right SHIFT on JP keyboard + mapper.jsX11KeysymMap[240] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_CAPSLOCK, modifiers: 0 }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_CAPSLOCK, modifiers: 0 }, + ]; + + // for keycode 107, keypress 59 + mapper.jsKeyPressX11KeysymMap[59] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SEMI_COLON, modifiers: 0 }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SEMI_COLON, modifiers: 0 }, + ]; + + // for keycode 107, keypress 43 + mapper.jsKeyPressX11KeysymMap[43] = [ + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_SHIFT, modifiers: 0, shift: false }, + {type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }, + {type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }, + ]; + +*/ + // create the mapping table based on the tables input + if (keyboardTables != undefined ) { + + for(var i = 0; i < keyboardTables.length; i++) { + var mappingTbl = keyboardTables[i]; + var mappings = mappingTbl.mappingTable; + var x11Maps = mappings.X11; + for (var j = 0; j < x11Maps.length; j++) { + var code = x11Maps[j].keycode; + var mappedEntry = x11Maps[j].entry; + mapper.jsX11KeysymMap[code] = mappedEntry; + } + var keyPressMaps = mappings.keyPress; + for (var j = 0; j < keyPressMaps.length; j++) { + var code = keyPressMaps[j].keycode; + var mappedEntry = keyPressMaps[j].entry; + mapper.jsKeyPressX11KeysymMap[code] = mappedEntry; + } + + } + } + + }, // end of the setupKeyboardTranslationTable function + + getCurrentKeyboardMapper : function() { + return this.keyboardMappers[this.currentKeyboard]; + }, + + setupUIController : function() { + var ajaxViewer = this; + var pullDownElement = $("#toolbar").find(".pulldown"); + pullDownElement.hover( + function(e) { + var subMenu = pullDownElement.find("ul"); + var offset = subMenu.parent().offset(); + subMenu.css("left", offset.left); + + $("li.current").removeClass("current"); + $("li:has(a[cmd$=" + ajaxViewer.currentKeyboard + "])", subMenu).addClass("current"); + subMenu.css("z-index", "" + ajaxViewer.maxTileZIndex + 1).show(); + return false; + }, + + function(e) { + pullDownElement.find("ul").hide(); + return false; + } + ); + + $("[cmd]", "#toolbar").each(function(i, val) { + $(val).click(function(e) { + var cmd = $(e.target).attr("cmd"); + if(cmd) + ajaxViewer.onCommand(cmd); + else { + var cmdLink = $(e.target).closest("a"); + + if(cmdLink.attr("cmd")) { + var cmd = cmdLink.attr("cmd"); + ajaxViewer.onCommand(cmd); + } + } + }); + }); + }, + + onCommand : function(cmd) { + if(cmd == "keyboard_jp") { + $("#toolbar").find(".pulldown").find("ul").hide(); + this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_JAPANESE; + } else if(cmd == "keyboard_us") { + $("#toolbar").find(".pulldown").find("ul").hide(); + this.currentKeyboard = AjaxViewer.KEYBOARD_TYPE_ENGLISH; + } else if(cmd == "sendCtrlAltDel") { + this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffe9, 0); // X11 Alt + this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffe3, 0); // X11 Ctrl + this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffff, 0); // X11 Del + this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffff, 0); + this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffe3, 0); + this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffe9, 0); + } else if(cmd == "sendCtrlEsc") { + this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xffe3, 0); // X11 Ctrl + this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, 0xff1b, 0); // X11 ESC + this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xff1b, 0); + this.sendKeyboardEvent(AjaxViewer.KEY_UP, 0xffe3, 0); + } else if(cmd == "toggle_logwin") { + if(!g_logger.isOpen()) { + g_logger.enable(true); + g_logger.open(); + g_logger.log(Logger.LEVEL_SYS, "Accept languages: " + acceptLanguages + ", current language: " + getCurrentLanguage()); + } else { + g_logger.close(); + } + } + }, + + sendKeyboardEvent: function(event, code, modifiers) { + // back door to open logger window - CTRL-ATL-SHIFT+SPACE + if(code == 32 && + (modifiers & AjaxViewer.SHIFT_KEY_MASK | AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK) == (AjaxViewer.SHIFT_KEY_MASK | AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK)) { + + if(!g_logger.isOpen()) { + g_logger.enable(true); + g_logger.open(); + g_logger.log(Logger.LEVEL_SYS, "Accept languages: " + acceptLanguages + ", current language: " + getCurrentLanguage()); + } else { + g_logger.close(); + } + } + + var len; + g_logger.log(Logger.LEVEL_INFO, "Keyboard event: " + AjaxViewer.getEventName(event) + ", code: " + code + ", modifiers: " + modifiers + ', char: ' + String.fromCharCode(code)); + this.eventQueue.push({ + type: AjaxViewer.EVENT_QUEUE_KEYBOARD_EVENT, + event: event, + code: code, + modifiers: modifiers + }); + + if(event != AjaxViewer.KEY_DOWN) + this.checkEventQueue(); + }, + + aggregateEvents: function() { + var ajaxViewer = this; + var aggratedQueue = []; + + var aggregating = false; + var mouseX; + var mouseY; + $.each(ajaxViewer.eventQueue, function(index, item) { + if(item.type != AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { + aggratedQueue.push(item); + } else { + if(!aggregating) { + if(item.event == AjaxViewer.MOUSE_MOVE) { + aggregating = true; + mouseX = item.x; + mouseY = item.y; + } else { + aggratedQueue.push(item); + } + } else { + if(item.event == AjaxViewer.MOUSE_MOVE) { + // continue to aggregate mouse move event + mouseX = item.x; + mouseY = item.y; + } else { + aggratedQueue.push({ + type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, + event: AjaxViewer.MOUSE_MOVE, + x: mouseX, + y: mouseY, + code: 0, + modifiers: 0 + }); + aggregating = false; + + aggratedQueue.push(item); + } + } + } + }); + + if(aggregating) { + aggratedQueue.push({ + type: AjaxViewer.EVENT_QUEUE_MOUSE_EVENT, + event: AjaxViewer.MOUSE_MOVE, + x: mouseX, + y: mouseY, + code: 0, + modifiers: 0 + }); + } + + this.eventQueue = aggratedQueue; + }, + + checkEventQueue: function() { + var ajaxViewer = this; + + if(!this.sendingEventInProgress && this.eventQueue.length > 0) { + var sb = new StringBuilder(); + sb.append(""+this.eventQueue.length).append("|"); + $.each(this.eventQueue, function() { + var item = this; + if(item.type == AjaxViewer.EVENT_QUEUE_MOUSE_EVENT) { + sb.append(""+item.type).append("|"); + sb.append(""+item.event).append("|"); + sb.append(""+item.x).append("|"); + sb.append(""+item.y).append("|"); + sb.append(""+item.code).append("|"); + sb.append(""+item.modifiers).append("|"); + } else { + sb.append(""+item.type).append("|"); + sb.append(""+item.event).append("|"); + sb.append(""+item.code).append("|"); + sb.append(""+item.modifiers).append("|"); + } + }); + this.eventQueue.length = 0; + + var url = ajaxViewer.updateUrl + "&event=" + AjaxViewer.EVENT_BAG; + + g_logger.log(Logger.LEVEL_TRACE, "Posting client event " + sb.toString() + "..."); + + ajaxViewer.sendingEventInProgress = true; + window.onStatusNotify(AjaxViewer.STATUS_SENDING); + $.post(url, {data: sb.toString()}, function(data, textStatus) { + g_logger.log(Logger.LEVEL_TRACE, "Client event " + sb.toString() + " is posted"); + + ajaxViewer.sendingEventInProgress = false; + window.onStatusNotify(AjaxViewer.STATUS_SENT); + + ajaxViewer.checkUpdate(); + }, 'html'); + } + }, + + onAjaxError: function(event, XMLHttpRequest, ajaxOptions, thrownError) { + if(window.onClientError != undefined && jQuery.isFunction(window.onClientError)) { + window.onClientError(); + } + }, + + onWindowResize: function() { + var offset = this.panel.offset(); + + var row = $('tr:first', this.panel); + var cell = $('td:first', row); + var tile = this.getTile(cell, 'tile'); + + var tileOffset = tile.offset(); + var deltaX = offset.left - tileOffset.left; + var deltaY = offset.top - tileOffset.top; + + if(deltaX != 0 || deltaY != 0) { + $(".canvas_tile").each(function() { + var offsetFrom = $(this).offset(); + $(this).css('left', offsetFrom.left + deltaX).css('top', offsetFrom.top + deltaY); + }); + } + }, + + deleteCanvas: function() { + $('.canvas_tile', $(document.body)).each(function() { + $(this).remove(); + }); + }, + + generateCanvas: function(wrapperDivId, width, height, tileWidth, tileHeight) { + var canvasParent = $('#' + wrapperDivId); + canvasParent.width(width); + canvasParent.height(height); + + if(window.onCanvasSizeChange != undefined && jQuery.isFunction(window.onCanvasSizeChange)) + window.onCanvasSizeChange(width, height); + + var tableDef = '\r\n'; + var i = 0; + var j = 0; + for(i = 0; i < Math.ceil((height + tileHeight - 1) / tileHeight); i++) { + var rowHeight = Math.min(height - i*tileHeight, tileHeight); + tableDef += '\r\n'; + + for(j = 0; j < Math.ceil((width + tileWidth - 1) / tileWidth); j++) { + var colWidth = Math.min(width - j*tileWidth, tileWidth); + tableDef += '\r\n'; + } + tableDef += '\r\n'; + } + tableDef += '
\r\n'; + + return $(tableDef).appendTo(canvasParent); + }, + + getTile: function(cell, name) { + var clonedDiv = cell.data(name); + if(!clonedDiv) { + var offset = cell.offset(); + var divDef = "
"; + + clonedDiv = $(divDef).appendTo($(document.body)); + cell.data(name, clonedDiv); + } + + return clonedDiv; + }, + + initCell: function(cell) { + if(!cell.data("init")) { + cell.data("init", true); + + cell.data("current", 0); + this.getTile(cell, "tile2"); + this.getTile(cell, "tile"); + } + }, + + displayCell: function(cell, bg) { + var div; + var divPrev; + if(!cell.data("current")) { + cell.data("current", 1); + + divPrev = this.getTile(cell, "tile"); + div = this.getTile(cell, "tile2"); + } else { + cell.data("current", 0); + divPrev = this.getTile(cell, "tile2"); + div = this.getTile(cell, "tile"); + } + + var zIndex = parseInt(divPrev.css("z-index")) + 1; + this.maxTileZIndex = Math.max(this.maxTileZIndex, zIndex); + div.css("z-index", zIndex); + div.css("background", bg); + }, + + updateTile: function() { + if(this.dirty) { + var ajaxViewer = this; + var tileWidth = this.tileWidth; + var tileHeight = this.tileHeight; + var imgUrl = this.imgUrl; + var panel = this.panel; + + if(this.fullImage) { + $.each(this.tileMap, function() { + var i = $(this)[0]; + var j = $(this)[1]; + var row = $("TR:eq("+i+")", panel); + var cell = $("TD:eq("+j+")", row); + var attr = "url(" + imgUrl + ") -"+j*tileWidth+"px -"+i*tileHeight + "px"; + + ajaxViewer.initCell(cell); + ajaxViewer.displayCell(cell, attr); + }); + } else { + $.each(this.tileMap, function(index) { + var i = $(this)[0]; + var j = $(this)[1]; + var offset = index*tileWidth; + var attr = "url(" + imgUrl + ") no-repeat -"+offset+"px 0px"; + var row = $("TR:eq("+i+")", panel); + var cell = $("TD:eq("+j+")", row); + + ajaxViewer.initCell(cell); + ajaxViewer.displayCell(cell, attr); + }); + } + + this.dirty = false; + } + }, + + heartbeat: function() { + this.checkEventQueue(); + this.checkUpdate(); + }, + + checkUpdate: function() { + if(!this.isDirty()) + return; + + if(this.isImageLoaded()) { + this.updateTile(); + var url = this.updateUrl; + var ajaxViewer = this; + + window.onStatusNotify(AjaxViewer.STATUS_RECEIVING); + $.getScript(url, function(data, textStatus) { + if(/^/.test(data)) { + ajaxViewer.stop(); + $(document.body).html(data); + } else { + eval(data); + ajaxViewer.setDirty(true); + window.onStatusNotify(AjaxViewer.STATUS_RECEIVED); + + ajaxViewer.checkUpdate(); + } + }); + } + }, + + ptInPanel: function(pageX, pageY) { + var mainPanel = this.panel; + + var offset = mainPanel.offset(); + var x = pageX - offset.left; + var y = pageY - offset.top; + + if(x < 0 || y < 0 || x > mainPanel.width() - 1 || y > mainPanel.height() - 1) + return false; + return true; + }, + + pageToPanel: function(pageX, pageY) { + var mainPanel = this.panel; + + var offset = mainPanel.offset(); + var x = pageX - offset.left; + var y = pageY - offset.top; + + if(x < 0) + x = 0; + if(x > mainPanel.width() - 1) + x = mainPanel.width() - 1; + + if(y < 0) + y = 0; + if(y > mainPanel.height() - 1) + y = mainPanel.height() - 1; + + return { x: Math.ceil(x), y: Math.ceil(y) }; + }, + + installMouseHook: function() { + var ajaxViewer = this; + var target = $(document.body); + + target.mousemove(function(e) { + if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) + return true; + + var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + ajaxViewer.onMouseMove(pt.x, pt.y); + + e.stopPropagation(); + return false; + }); + + target.mousedown(function(e) { + ajaxViewer.panel.parent().focus(); + + if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) + return true; + + var modifiers = ajaxViewer.getKeyModifiers(e); + var whichButton = e.button; + + var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + ajaxViewer.onMouseDown(pt.x, pt.y, whichButton, modifiers); + + e.stopPropagation(); + return false; + }); + + target.mouseup(function(e) { + if(!ajaxViewer.ptInPanel(e.pageX, e.pageY)) + return true; + + var modifiers = ajaxViewer.getKeyModifiers(e); + var whichButton = e.button; + + var pt = ajaxViewer.pageToPanel(e.pageX, e.pageY); + + ajaxViewer.onMouseUp(pt.x, pt.y, whichButton, modifiers); + e.stopPropagation(); + return false; + }); + + // disable browser right-click context menu + target.bind("contextmenu", function() { return false; }); + }, + + uninstallMouseHook : function() { + var target = $(document); + target.unbind("mousemove"); + target.unbind("mousedown"); + target.unbind("mouseup"); + target.unbind("contextmenu"); + }, + + requiresDefaultKeyProcess : function(e) { + switch(e.which) { + case 8 : // backspace + case 9 : // TAB + case 19 : // PAUSE/BREAK + case 20 : // CAPSLOCK + case 27 : // ESCAPE + case 16 : // SHIFT key + case 17 : // CTRL key + case 18 : // ALT key + case 33 : // PGUP + case 34 : // PGDN + case 35 : // END + case 36 : // HOME + case 37 : // LEFT + case 38 : // UP + case 39 : // RIGHT + case 40 : // DOWN + return false; + } + + if(this.getKeyModifiers(e) == AjaxViewer.SHIFT_KEY_MASK) + return true; + + if(this.getKeyModifiers(e) != 0) + return false; + + return true; + }, + + installKeyboardHook: function() { + var ajaxViewer = this; + var target = $(document); + + target.keypress(function(e) { + ajaxViewer.onKeyPress(e.which, ajaxViewer.getKeyModifiers(e)); + + e.stopPropagation(); + if(ajaxViewer.requiresDefaultKeyProcess(e)) + return true; + + e.preventDefault(); + return false; + }); + + target.keydown(function(e) { + ajaxViewer.onKeyDown(e.which, ajaxViewer.getKeyModifiers(e)); + + e.stopPropagation(); + if(ajaxViewer.requiresDefaultKeyProcess(e)) + return true; + + e.preventDefault(); + return false; + }); + + target.keyup(function(e) { + ajaxViewer.onKeyUp(e.which, ajaxViewer.getKeyModifiers(e)); + + e.stopPropagation(); + if(ajaxViewer.requiresDefaultKeyProcess(e)) + return true; + + e.preventDefault(); + return false; + }); + }, + + uninstallKeyboardHook : function() { + var target = $(document); + target.unbind("keypress"); + target.unbind("keydown"); + target.unbind("keyup"); + }, + + onMouseMove: function(x, y) { + this.sendMouseEvent(AjaxViewer.MOUSE_MOVE, x, y, 0, 0); + }, + + onMouseDown: function(x, y, whichButton, modifiers) { + this.sendMouseEvent(AjaxViewer.MOUSE_DOWN, x, y, whichButton, modifiers); + }, + + onMouseUp: function(x, y, whichButton, modifiers) { + this.sendMouseEvent(AjaxViewer.MOUSE_UP, x, y, whichButton, modifiers); + + var curTick = new Date().getTime(); + if(this.lastClickEvent.time && (curTick - this.lastClickEvent.time < 300)) { + this.onMouseDblClick(this.lastClickEvent.x, this.lastClickEvent.y, + this.lastClickEvent.button, this.lastClickEvent.modifiers); + } + + this.lastClickEvent.x = x; + this.lastClickEvent.y = y; + this.lastClickEvent.button = whichButton; + this.lastClickEvent.modifiers = modifiers; + this.lastClickEvent.time = curTick; + }, + + onMouseDblClick: function(x, y, whichButton, modifiers) { + this.sendMouseEvent(AjaxViewer.MOUSE_DBLCLK, x, y, whichButton, modifiers); + }, + + onKeyPress: function(code, modifiers) { + g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-PRESS: " + code + ", modifers: " + modifiers); + + this.dispatchKeyboardInput(AjaxViewer.KEY_PRESS, code, modifiers); + }, + + onKeyDown: function(code, modifiers) { + g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-DOWN: " + code + ", modifers: " + modifiers); + + this.dispatchKeyboardInput(AjaxViewer.KEY_DOWN, code, modifiers); + }, + + onKeyUp: function(code, modifiers) { + g_logger.log(Logger.LEVEL_WARN, "RAW KEYBOARD EVENT. KEY-UP: " + code + ", modifers: " + modifiers); + + this.dispatchKeyboardInput(AjaxViewer.KEY_UP, code, modifiers); + }, + + dispatchKeyboardInput : function(event, code, modifiers) { + var keyboardMapper = ajaxViewer.getCurrentKeyboardMapper(); + keyboardMapper.inputFeed(event, code, modifiers, this.guestos, $.browser, $.browser.version); + this.dispatchMappedKeyboardInput(keyboardMapper.getMappedInput()); + }, + + dispatchMappedKeyboardInput : function(mappedInput) { + for(var i = 0; i < mappedInput.length; i++) { + switch(mappedInput[i].type) { + case AjaxViewer.KEY_DOWN : + this.sendKeyboardEvent(AjaxViewer.KEY_DOWN, mappedInput[i].code, mappedInput[i].modifiers); + break; + + case AjaxViewer.KEY_UP : + this.sendKeyboardEvent(AjaxViewer.KEY_UP, mappedInput[i].code, mappedInput[i].modifiers); + break; + + case AjaxViewer.KEY_PRESS : + this.sendKeyboardEvent(AjaxViewer.KEY_PRESS, mappedInput[i].code, mappedInput[i].modifiers); + break; + } + } + }, + + getKeyModifiers: function(e) { + var modifiers = 0; + if(e.altKey) + modifiers |= AjaxViewer.ALT_KEY_MASK; + + if(e.altLeft) + modifiers |= AjaxViewer.LEFT_ALT_MASK; + + if(e.ctrlKey) + modifiers |= AjaxViewer.CTRL_KEY_MASK; + + if(e.ctrlLeft) + modifiers |= AjaxViewer.LEFT_CTRL_MASK; + + if(e.shiftKey) + modifiers |= AjaxViewer.SHIFT_KEY_MASK; + + if(e.shiftLeft) + modifiers |= AjaxViewer.LEFT_SHIFT_MASK; + + if(e.metaKey) + modifiers |= AjaxViewer.META_KEY_MASK; + + return modifiers; + } +}; + diff --git a/services/console-proxy/server/js/cloud.logger.js b/services/console-proxy/server/js/cloud.logger.js new file mode 100644 index 00000000000..751627ed96c --- /dev/null +++ b/services/console-proxy/server/js/cloud.logger.js @@ -0,0 +1,338 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +// Version: 1.9.1.152 + +// +// Javascript logger utility +// Author +// Kelven Yang +// 2/25/2010 +// + +function Logger() { + this.bDockEnabled = true; + + this.logWin = null; + this.logger = null; + this.header = null; + + this.bEnabled = true; + this.level = 0; + + this.bMoving = false; + this.offsetStart = {left: 0, top: 0}; + this.ptStart = {x: 0, y: 0}; +} + +Logger.DEFAULT_WIN_HEIGHT = 500; +Logger.LEVEL_TRACE = 0; +Logger.LEVEL_DEBUG = 1; +Logger.LEVEL_INFO = 2; +Logger.LEVEL_WARN = 3; +Logger.LEVEL_ERROR = 4; +Logger.LEVEL_FATAL = 5; +Logger.LEVEL_SYS = 100; + +Logger.prototype = { + + open: function() { + if(this.logWin) { + this.logWin.show(); + + this.log(Logger.LEVEL_SYS, "Logger is open in browser: " + this.objectToString($.browser)); + return; + } + + var logger = this; + var logWinMarkup = [ + '
', + '
', + '
', + '', + '', + '', + '
', + '', + '
', + '
', + '
', + '', + '', + '
', + '
', + '
', + '
' + ].join(''); + + this.logWin = $(logWinMarkup).appendTo(document.body); + this.header = $('.logwin_title:first', this.logWin); + this.logger = $('.logwin_content:first', this.logWin); + + $(".logwin_title", this.logWin).mousedown(function(e) { + if($(e.target).attr('cmd')) + return true; + + if(!logger.bMoving) { + logger.bMoving = true; + logger.offsetStart = logger.logWin.offset(); + logger.ptStart = {x: e.pageX, y: e.pageY}; + + $(document).bind("mousemove", function(e) { + if(logger.bMoving) { + logger.enableDocking(false); + + var logWinNewLeft = logger.offsetStart.left + e.pageX - logger.ptStart.x; + var logWinNewTop = logger.offsetStart.top + e.pageY - logger.ptStart.y; + + logger.logWin.css("left", logWinNewLeft + "px").css("top", logWinNewTop + "px"); + } + return false; + }); + + $(document).bind("mouseup", function(e) { + if(logger.bMoving) { + logger.bMoving = false; + $(document).unbind("mousemove", arguments.callee.name); + $(document).unbind("mouseup", arguments.callee.name); + + return false; + } + return true; + }); + } + + // prevent default handling + return false; + }).dblclick(function(e) { + logger.expand(!logger.isExpanded()); + }); + + this.logWin.click(function(e) { + if($(e.target).attr('cmd')) { + switch($(e.target).attr('cmd')) { + case '1' : + logger.enable(true); + break; + + case '2' : + logger.enable(false); + break; + + case '3' : + logger.clear(); + break; + + case '4' : + logger.enableDocking(true); + logger.dockIn(); + break; + + case '5' : + logger.expand(!logger.isExpanded()); + break; + + default : + break; + } + } + }); + + $("#template_type", this.logWin).change(function(e) { + logger.setLevel(parseInt($(this).val())); + }); + + this.logWin.css("left", (($(document.body).width() - this.logWin.width()) / 2) + "px"); + this.dockIn(); + + this.log(Logger.LEVEL_SYS, "Logger is open in browser: " + this.objectToString($.browser)); + }, + + close: function() { + if(this.logWin) + this.logWin.hide(); + }, + + isOpen: function() { + if(this.logWin) + return this.logWin.is(":visible"); + return false; + }, + + dockIn: function() { + var logger = this; + var offset = this.logWin.offset(); + var bottom = offset.top + this.logWin.height(); + var delta = bottom - 2; + + this.logWin.animate({top: (offset.top - delta) + "px"}, 200, + function() { + logger.logWin.unbind("mouseleave"); + logger.logWin.bind("mouseenter", function(e) { + if(logger.bDockEnabled) + logger.dockOut(); + }); + } + ); + }, + + dockOut: function() { + var logger = this; + this.logWin.animate({top: "0px"}, 200, + function() { + logger.logWin.unbind("mouseenter"); + logger.logWin.bind("mouseleave", function(e) { + if(logger.bDockEnabled) { + var xPosInLogWin = e.pageX - logger.logWin.offset().left; + var yPosInLogWin = e.pageY - logger.logWin.offset().top; + + if(xPosInLogWin < 0 || yPosInLogWin < 0 || + xPosInLogWin > logger.logWin.width() || yPosInLogWin > logger.logWin.height()) { + logger.dockIn(); + } + } + }); + } + ); + }, + + enableDocking: function(bEnable) { + this.bDockEnabled = bEnable; + }, + + log: function(level, message) { + // Note : LEVEL_SYS message will always be logged + if(this.logger && (level == Logger.LEVEL_SYS || this.bEnabled && level >= this.level)) { + var curTime = new Date(); + var curTimeString = [ + '', curTime.getMonth(), + '/', curTime.getDate(), + '/', curTime.getYear(), + ' ', + curTime.getHours(), + ':', curTime.getMinutes(), + ":", curTime.getSeconds(), + ".", curTime.getMilliseconds()].join(''); + + this.logger.append(this.getLevelDisplayString(level) + " - " + curTimeString + " - " + message + '
'); + } + }, + + clear: function() { + if(this.logger) { + this.logger.empty(); + this.log(Logger.LEVEL_SYS, "Logger is cleared"); + } + }, + + setLevel: function(level) { + this.level = level; + + this.log(Logger.LEVEL_SYS, "Set logger trace level to " + this.getLevelDisplayString(level)); + }, + + enable: function(bEnabled) { + this.bEnabled = bEnabled; + + if(bEnabled) + this.log(Logger.LEVEL_SYS, "Logger is enabled"); + else + this.log(Logger.LEVEL_SYS, "Logger is disabled"); + }, + + expand: function(bExpand) { + if(bExpand) { + this.logWin.height(Logger.DEFAULT_WIN_HEIGHT); + this.logger.height(Logger.DEFAULT_WIN_HEIGHT - this.header.height()); + } else { + this.logWin.height(this.header.height()); + this.logger.height(0); + } + }, + + isExpanded: function() { + return this.logWin.height() > this.header.height(); + }, + + getLevelDisplayString: function(level) { + switch(level) { + case Logger.LEVEL_TRACE : + return "TRACE"; + + case Logger.LEVEL_DEBUG : + return "DEBUG"; + + case Logger.LEVEL_INFO : + return "INFO"; + + case Logger.LEVEL_WARN : + return "WARN"; + + case Logger.LEVEL_ERROR : + return "ERROR"; + + case Logger.LEVEL_FATAL : + return "FATAL"; + + case Logger.LEVEL_SYS : + return "SYSINFO"; + } + + return "LEVEL " + level; + }, + + // this is a util function which actually can be put elsewhere instead of in this class + objectToString : function(object) { + if(object) { + if(object instanceof Object) { + var sb = ['{' ]; + + $.each(object, function(name, val) { + sb.push('' + name + ': '); + + if(val instanceof Object) { + sb.push(this.objectToString(val)); + } else { + sb.push('' + val); + } + + sb.push(','); + }); + + if(sb[sb.length - 1] == ',' ) + sb.length = sb.length - 1; + + sb.push('}'); + return sb.join(""); + } else { + return '' + object; + } + } else { + return 'N/A'; + } + } +}; + diff --git a/services/console-proxy/server/js/handler.js b/services/console-proxy/server/js/handler.js new file mode 100644 index 00000000000..d22ff079ee6 --- /dev/null +++ b/services/console-proxy/server/js/handler.js @@ -0,0 +1,72 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ + +// +// Callback handlers for AJAX viewer +// Author +// Kelven Yang +// 11/18/2009 +// +function onKickoff() { + ajaxViewer.stop(); + $('#toolbar').remove(); + $('#main_panel').html('

This session is terminated because a session for the same VM has been created elsewhere.

'); +} + +function onDisconnect() { + ajaxViewer.stop(); + $('#toolbar').remove(); + $('#main_panel').html('

This session is terminated as the machine you are accessing has terminated the connection.

'); +} + +function onClientError() { + ajaxViewer.stop(); + $('#toolbar').remove(); + $('#main_panel').html('

Client communication error, please retry later.

'); +} + +function onCanvasSizeChange(width, height) { + $('#toolbar').width(width); +} + +function onStatusNotify(status) { + if(status == ajaxViewer.STATUS_SENDING || status == ajaxViewer.STATUS_RECEIVING) + $('#light').removeClass('dark').addClass('bright'); + else + $('#light').removeClass('bright').addClass('dark'); +} + +function sendCtrlAltDel() { + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 45, ajaxViewer.CTRL_KEY | ajaxViewer.ALT_KEY); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 45, ajaxViewer.CTRL_KEY | ajaxViewer.ALT_KEY); +} + +function sendCtrlEsc() { + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 17, 0); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 27, ajaxViewer.CTRL_KEY); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 27, ajaxViewer.CTRL_KEY); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 17, 0); +} + +function sendAltTab() { + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 18, 0); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_DOWN, 9, ajaxViewer.ALT_KEY); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 9, ajaxViewer.ALT_KEY); + ajaxViewer.sendKeyboardEvent(ajaxViewer.KEY_UP, 18, 0); +} diff --git a/services/console-proxy/server/js/jquery.js b/services/console-proxy/server/js/jquery.js new file mode 100644 index 00000000000..b1ae21d8b23 --- /dev/null +++ b/services/console-proxy/server/js/jquery.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/services/console-proxy/server/libexec/console-proxy-runner.in b/services/console-proxy/server/libexec/console-proxy-runner.in new file mode 100755 index 00000000000..4f18aab36e5 --- /dev/null +++ b/services/console-proxy/server/libexec/console-proxy-runner.in @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +#run.sh runs the agent client. + +cd `dirname "$0"` + +SYSTEMJARS="@SYSTEMJARS@" +SCP=$(build-classpath $SYSTEMJARS) ; if [ $? != 0 ] ; then SCP="@SYSTEMCLASSPATH@" ; fi +DCP="@DEPSCLASSPATH@" +ACP="@AGENTCLASSPATH@" +export CLASSPATH=$SCP:$DCP:$ACP:@CPSYSCONFDIR@ +for jarfile in "@PREMIUMJAVADIR@"/* ; do + if [ ! -e "$jarfile" ] ; then continue ; fi + CLASSPATH=$jarfile:$CLASSPATH +done +for plugin in "@PLUGINJAVADIR@"/* ; do + if [ ! -e "$plugin" ] ; then continue ; fi + CLASSPATH=$plugin:$CLASSPATH +done +export CLASSPATH + +set -e +cd "@CPLIBDIR@" +echo Current directory is "$PWD" +echo CLASSPATH to run the console proxy: "$CLASSPATH" + +export PATH=/sbin:/usr/sbin:"$PATH" +SERVICEARGS= +for x in private public ; do + configuration=`grep -q "^$x.network.device" "@CPSYSCONFDIR@"/agent.properties || true` + if [ -n "$CONFIGURATION" ] ; then + echo "Using manually-configured network device $CONFIGURATION" + else + defaultroute=`ip route | grep ^default | cut -d ' ' -f 5` + test -n "$defaultroute" + echo "Using auto-discovered network device $defaultroute which is the default route" + SERVICEARGS="$SERVICEARGS $x.network.device="$defaultroute + fi +done + +function termagent() { + if [ "$agentpid" != "" ] ; then + echo Killing VMOps Console Proxy "(PID $agentpid)" with SIGTERM >&2 + kill -TERM $agentpid + echo Waiting for agent to exit >&2 + wait $agentpid + ex=$? + echo Agent exited with return code $ex >&2 + else + echo Agent PID is unknown >&2 + fi +} + +trap termagent TERM +while true ; do + java -Xms128M -Xmx384M -cp "$CLASSPATH" "$@" com.cloud.agent.AgentShell $SERVICEARGS & + agentpid=$! + echo "Console Proxy started. PID: $!" >&2 + wait $agentpid + ex=$? + if [ $ex -gt 128 ]; then + echo "wait on console proxy process interrupted by SIGTERM" >&2 + exit $ex + fi + echo "Console proxy exited with return code $ex" >&2 + if [ $ex -eq 0 ] || [ $ex -eq 1 ] || [ $ex -eq 66 ] || [ $ex -gt 128 ]; then + echo "Exiting..." > /dev/stderr + exit $ex + fi + echo "Restarting console proxy..." > /dev/stderr + sleep 1 +done diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml new file mode 100644 index 00000000000..58fd62a71a3 --- /dev/null +++ b/services/console-proxy/server/pom.xml @@ -0,0 +1,247 @@ + + + 4.0.0 + cloud-console-proxy + Apache CloudStack Console Proxy + + org.apache.cloudstack + cloud-service-console-proxy + 4.1.0-SNAPSHOT + ../pom.xml + + + mkisofs + + + + log4j + log4j + ${cs.log4j.version} + + + com.google.code.gson + gson + ${cs.gson.version} + + + commons-codec + commons-codec + ${cs.codec.version} + + + + org.apache.cloudstack + cloud-agent + ${project.version} + + + org.apache.cloudstack + cloud-patches + ${project.version} + pom + + + + install + src + + + certs + + realhostip.csr + + + + + + maven-assembly-plugin + 2.3 + + systemvm + false + + systemvm-descriptor.xml + + + + + make-systemvm + package + + single + + + + + + maven-resources-plugin + 2.6 + + + copy-resources + + package + + copy-resources + + + dist + + + target + + systemvm.zip + + + + ../patches/systemvm/debian/config/root/.ssh + + authorized_keys + + + + + + + + + maven-antrun-plugin + 1.7 + + + copy-cloud-scripts + package + + run + + + + + + + + + + + + + + + + genisoimage + + + /usr/bin/genisoimage + + + + genisoimage + + + + vmware + + + nonoss + + + + + org.apache.cloudstack + cloud-plugin-hypervisor-vmware + ${project.version} + + + org.apache.cloudstack + cloud-vmware-base + ${project.version} + + + + + systemvm + + + systemvm + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + package + + exec + + + + + ${mkisofs} + dist + + -quiet + -r + -o + systemvm.iso + systemvm.zip + cloud-scripts.tgz + authorized_keys + + + + + + + + + diff --git a/services/console-proxy/server/scripts/_run.sh b/services/console-proxy/server/scripts/_run.sh new file mode 100755 index 00000000000..e408378afbc --- /dev/null +++ b/services/console-proxy/server/scripts/_run.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + + + +#run.sh runs the console proxy. + +# make sure we delete the old files from the original template +rm console-proxy.jar +rm console-common.jar +rm conf/cloud.properties + +set -x + +CP=./:./conf +for file in *.jar +do + CP=${CP}:$file +done +keyvalues= + +CMDLINE=$(cat /var/cache/cloud/cmdline) + +#CMDLINE="graphical utf8 eth0ip=0.0.0.0 eth0mask=255.255.255.0 eth1ip=192.168.140.40 eth1mask=255.255.255.0 eth2ip=172.24.0.50 eth2mask=255.255.0.0 gateway=172.24.0.1 dns1=72.52.126.11 template=domP dns2=72.52.126.12 host=192.168.1.142 port=8250 mgmtcidr=192.168.1.0/24 localgw=192.168.140.1 zone=5 pod=5" +for i in $CMDLINE + do + KEY=$(echo $i | cut -s -d= -f1) + VALUE=$(echo $i | cut -s -d= -f2) + [ "$KEY" == "" ] && continue + case $KEY in + *) + keyvalues="${keyvalues} $KEY=$VALUE" + esac + done + +tot_mem_k=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}') +let "tot_mem_m=tot_mem_k>>10" +let "eightypcnt=$tot_mem_m*8/10" +let "maxmem=$tot_mem_m-80" + +if [ $maxmem -gt $eightypcnt ] +then + maxmem=$eightypcnt +fi + +java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@ diff --git a/services/console-proxy/server/scripts/config_auth.sh b/services/console-proxy/server/scripts/config_auth.sh new file mode 100755 index 00000000000..4b74f8eb995 --- /dev/null +++ b/services/console-proxy/server/scripts/config_auth.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + + + + +BASE_DIR="/var/www/html/copy/template/" +HTACCESS="$BASE_DIR/.htaccess" + +PASSWDFILE="/etc/httpd/.htpasswd" +if [ -d /etc/apache2 ] +then + PASSWDFILE="/etc/apache2/.htpasswd" +fi + +config_htaccess() { + mkdir -p $BASE_DIR + result=$? + echo "Options -Indexes" > $HTACCESS + let "result=$result+$?" + echo "AuthType Basic" >> $HTACCESS + let "result=$result+$?" + echo "AuthName \"Authentication Required\"" >> $HTACCESS + let "result=$result+$?" + echo "AuthUserFile \"$PASSWDFILE\"" >> $HTACCESS + let "result=$result+$?" + echo "Require valid-user" >> $HTACCESS + let "result=$result+$?" + return $result +} + +write_passwd() { + local user=$1 + local passwd=$2 + htpasswd -bc $PASSWDFILE $user $passwd + return $? +} + +if [ $# -ne 2 ] ; then + echo $"Usage: `basename $0` username password " + exit 0 +fi + +write_passwd $1 $2 +if [ $? -ne 0 ] +then + echo "Failed to update password" + exit 2 +fi + +config_htaccess +exit $? diff --git a/services/console-proxy/server/scripts/config_ssl.sh b/services/console-proxy/server/scripts/config_ssl.sh new file mode 100755 index 00000000000..8d80c4731ad --- /dev/null +++ b/services/console-proxy/server/scripts/config_ssl.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + + +help() { + printf " -c use customized key/cert\n" + printf " -k path of private key\n" + printf " -p path of certificate of public key\n" + printf " -t path of certificate chain\n" +} + + +config_httpd_conf() { + local ip=$1 + local srvr=$2 + cp -f /etc/httpd/conf/httpd.conf.orig /etc/httpd/conf/httpd.conf + sed -i -e "s/Listen.*:80$/Listen $ip:80/" /etc/httpd/conf/httpd.conf + echo " " >> /etc/httpd/conf/httpd.conf + echo " DocumentRoot /var/www/html/" >> /etc/httpd/conf/httpd.conf + echo " ServerName $srvr" >> /etc/httpd/conf/httpd.conf + echo " SSLEngine on" >> /etc/httpd/conf/httpd.conf + echo " SSLCertificateFile /etc/httpd/ssl/certs/realhostip.crt" >> /etc/httpd/conf/httpd.conf + echo " SSLCertificateKeyFile /etc/httpd/ssl/keys/realhostip.key" >> /etc/httpd/conf/httpd.conf + echo "" >> /etc/httpd/conf/httpd.conf +} + +config_apache2_conf() { + local ip=$1 + local srvr=$2 + cp -f /etc/apache2/sites-available/default.orig /etc/apache2/sites-available/default + cp -f /etc/apache2/sites-available/default-ssl.orig /etc/apache2/sites-available/default-ssl + sed -i -e "s///" /etc/apache2/sites-available/default + sed -i -e "s///" /etc/apache2/sites-available/default-ssl + sed -i -e "s/Listen .*:80/Listen $ip:80/g" /etc/apache2/ports.conf + sed -i -e "s/Listen .*:443/Listen $ip:443/g" /etc/apache2/ports.conf + sed -i -e "s/NameVirtualHost .*:80/NameVirtualHost $ip:80/g" /etc/apache2/ports.conf + sed -i 's/ssl-cert-snakeoil.key/cert_apache.key/' /etc/apache2/sites-available/default-ssl + sed -i 's/ssl-cert-snakeoil.pem/cert_apache.crt/' /etc/apache2/sites-available/default-ssl +} + +copy_certs() { + local certdir=$(dirname $0)/certs + local mydir=$(dirname $0) + if [ -d $certdir ] && [ -f $customPrivKey ] && [ -f $customPrivCert ] ; then + mkdir -p /etc/httpd/ssl/keys && mkdir -p /etc/httpd/ssl/certs && cp $customprivKey /etc/httpd/ssl/keys && cp $customPrivCert /etc/httpd/ssl/certs + return $? + fi + if [ ! -z customCertChain ] && [ -f $customCertChain ] ; then + cp $customCertChain /etc/httpd/ssl/certs + fi + return 1 +} + +copy_certs_apache2() { + local certdir=$(dirname $0)/certs + local mydir=$(dirname $0) + if [ -f $customPrivKey ] && [ -f $customPrivCert ] ; then + cp $customPrivKey /etc/ssl/private/cert_apache.key && cp $customPrivCert /etc/ssl/certs/cert_apache.crt + fi + if [ ! -z "$customCertChain" ] && [ -f "$customCertChain" ] ; then + cp $customCertChain /etc/ssl/certs/cert_apache_chain.crt + fi + return 0 +} + + +cflag= +cpkflag= +cpcflag= +cccflag= +customPrivKey=$(dirname $0)/certs/realhostip.key +customPrivCert=$(dirname $0)/certs/realhostip.crt +customCertChain= +publicIp= +hostName= +while getopts 'i:h:k:p:t:c' OPTION +do + case $OPTION in + c) cflag=1 + ;; + k) cpkflag=1 + customPrivKey="$OPTARG" + ;; + p) cpcflag=1 + customPrivCert="$OPTARG" + ;; + t) cccflag=1 + customCertChain="$OPTARG" + ;; + i) publicIp="$OPTARG" + ;; + h) hostName="$OPTARG" + ;; + ?) help + ;; + esac +done + + +if [ -z "$publicIp" ] || [ -z "$hostName" ] +then + help + exit 1 +fi + +if [ "$cflag" == "1" ] +then + if [ "$cpkflag$cpcflag" != "11" ] + then + help + exit 1 + fi + if [ ! -f "$customPrivKey" ] + then + printf "priviate key file is not exist\n" + exit 2 + fi + + if [ ! -f "$customPrivCert" ] + then + printf "public certificate is not exist\n" + exit 3 + fi + + if [ "$cccflag" == "1" ] + then + if [ ! -f "$customCertChain" ] + then + printf "certificate chain is not exist\n" + exit 4 + fi + fi +fi + +if [ -d /etc/apache2 ] +then + copy_certs_apache2 +else + copy_certs +fi + +if [ $? -ne 0 ] +then + echo "Failed to copy certificates" + exit 2 +fi + +if [ -d /etc/apache2 ] +then + config_apache2_conf $publicIp $hostName + /etc/init.d/apache2 stop + /etc/init.d/apache2 start +else + config_httpd_conf $publicIp $hostName +fi + + diff --git a/services/console-proxy/server/scripts/ipfirewall.sh b/services/console-proxy/server/scripts/ipfirewall.sh new file mode 100755 index 00000000000..4711b8ac6db --- /dev/null +++ b/services/console-proxy/server/scripts/ipfirewall.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +BASE_DIR="/var/www/html/copy/" +HTACCESS="$BASE_DIR/.htaccess" + +config_htaccess() { + mkdir -p $BASE_DIR + result=$? + echo "Options -Indexes" > $HTACCESS + let "result=$result+$?" + echo "order deny,allow" >> $HTACCESS + let "result=$result+$?" + echo "deny from all" >> $HTACCESS + let "result=$result+$?" + return $result +} + +ips(){ + echo "allow from $1" >> $HTACCESS + result=$? + return $result +} + +is_append="$1" +shift +if [ $is_append != "true" ]; then + config_htaccess +fi +for i in $@ +do + ips "$i" +done +exit $? + diff --git a/services/console-proxy/server/scripts/run-proxy.sh b/services/console-proxy/server/scripts/run-proxy.sh new file mode 100644 index 00000000000..d6ccf7c0091 --- /dev/null +++ b/services/console-proxy/server/scripts/run-proxy.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + + + +#run.sh runs the console proxy. + +# make sure we delete the old files from the original template +rm console-proxy.jar +rm console-common.jar +rm conf/cloud.properties + +CP=./:./conf +for file in *.jar +do + CP=${CP}:$file +done + +#CMDLINE=$(cat /proc/cmdline) +#for i in $CMDLINE +# do +# KEY=$(echo $i | cut -d= -f1) +# VALUE=$(echo $i | cut -d= -f2) +# case $KEY in +# mgmt_host) +# MGMT_HOST=$VALUE +# ;; +# esac +# done + +java -mx700m -cp $CP:./conf com.cloud.consoleproxy.ConsoleProxy $@ diff --git a/services/console-proxy/server/scripts/run.bat b/services/console-proxy/server/scripts/run.bat new file mode 100644 index 00000000000..ce6dc404574 --- /dev/null +++ b/services/console-proxy/server/scripts/run.bat @@ -0,0 +1,18 @@ +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. + +java -mx700m -cp cloud-console-proxy.jar;;cloud-console-common.jar;log4j-1.2.15.jar;apache-log4j-extras-1.0.jar;gson-1.3.jar;commons-logging-1.1.1.jar;.;.\conf; com.cloud.consoleproxy.ConsoleProxy %* diff --git a/services/console-proxy/server/scripts/run.sh b/services/console-proxy/server/scripts/run.sh new file mode 100755 index 00000000000..146d96f0287 --- /dev/null +++ b/services/console-proxy/server/scripts/run.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + + + +#_run.sh runs the agent client. + +# set -x + +while true +do + ./_run.sh "$@" & + wait + ex=$? + if [ $ex -eq 0 ] || [ $ex -eq 1 ] || [ $ex -eq 66 ] || [ $ex -gt 128 ]; then + # permanent errors + sleep 5 + fi + + # user stop agent by service cloud stop + grep 'stop' /usr/local/cloud/systemvm/user_request &>/dev/null + if [ $? -eq 0 ]; then + timestamp=$(date) + echo "$timestamp User stops cloud.com service" >> /var/log/cloud.log + exit 0 + fi + sleep 5 +done diff --git a/services/console-proxy/server/scripts/ssvm-check.sh b/services/console-proxy/server/scripts/ssvm-check.sh new file mode 100644 index 00000000000..a4011647f07 --- /dev/null +++ b/services/console-proxy/server/scripts/ssvm-check.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +# Health check script for the Secondary Storage VM + +# DNS server is specified. + + +CMDLINE=/var/cache/cloud/cmdline +for i in `cat $CMDLINE` +do + key=`echo $i | cut -d= -f1` + value=`echo $i | cut -d= -f2` + case $key in + host) + MGMTSERVER=$value + ;; + esac +done + + +# ping dns server +echo ================================================ +DNSSERVER=`egrep '^nameserver' /etc/resolv.conf | awk '{print $2}'| head -1` +echo "First DNS server is " $DNSSERVER +ping -c 2 $DNSSERVER +if [ $? -eq 0 ] +then + echo "Good: Can ping DNS server" +else + echo "WARNING: cannot ping DNS server" + echo "route follows" + route -n +fi + + +# check dns resolve +echo ================================================ +nslookup download.cloud.com 1> /tmp/dns 2>&1 +grep 'no servers could' /tmp/dns 1> /dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "ERROR: DNS not resolving download.cloud.com" + echo resolv.conf follows + cat /etc/resolv.conf + exit 2 +else + echo "Good: DNS resolves download.cloud.com" +fi + + +# check to see if we have the NFS volume mounted +echo ================================================ +mount|grep -v sunrpc|grep nfs 1> /dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "NFS is currently mounted" + # check for write access + for MOUNTPT in `mount|grep -v sunrpc|grep nfs| awk '{print $3}'` + do + if [ $MOUNTPT != "/proc/xen" ] # mounted by xen + then + echo Mount point is $MOUNTPT + touch $MOUNTPT/foo + if [ $? -eq 0 ] + then + echo "Good: Can write to mount point" + rm $MOUNTPT/foo + else + echo "ERROR: Cannot write to mount point" + echo "You need to export with norootsquash" + fi + fi + done +else + echo "ERROR: NFS is not currently mounted" + echo "Try manually mounting from inside the VM" + NFSSERVER=`awk '{print $17}' $CMDLINE|awk -F= '{print $2}'|awk -F: '{print $1}'` + echo "NFS server is " $NFSSERVER + ping -c 2 $NFSSERVER + if [ $? -eq 0 ] + then + echo "Good: Can ping NFS server" + else + echo "WARNING: cannot ping NFS server" + echo routing table follows + route -n + fi +fi + + +# check for connectivity to the management server +echo ================================================ +echo Management server is $MGMTSERVER. Checking connectivity. +socatout=$(echo | socat - TCP:$MGMTSERVER:8250,connect-timeout=3 2>&1) +if [ $? -eq 0 ] +then + echo "Good: Can connect to management server port 8250" +else + echo "ERROR: Cannot connect to $MGMTSERVER port 8250" + echo $socatout + exit 4 +fi + + +# check for the java process running +echo ================================================ +ps -eaf|grep -v grep|grep java 1> /dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "Good: Java process is running" +else + echo "ERROR: Java process not running. Try restarting the SSVM." + exit 3 +fi + +echo ================================================ +echo Tests Complete. Look for ERROR or WARNING above. + +exit 0 diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java b/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java new file mode 100644 index 00000000000..cff00b3317d --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; + +public class AjaxFIFOImageCache { + private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class); + + private List fifoQueue; + private Map cache; + private int cacheSize; + private int nextKey = 0; + + public AjaxFIFOImageCache(int cacheSize) { + this.cacheSize = cacheSize; + fifoQueue = new ArrayList(); + cache = new HashMap(); + } + + public synchronized void clear() { + fifoQueue.clear(); + cache.clear(); + } + + public synchronized int putImage(byte[] image) { + while(cache.size() >= cacheSize) { + Integer keyToRemove = fifoQueue.remove(0); + cache.remove(keyToRemove); + + if(s_logger.isTraceEnabled()) + s_logger.trace("Remove image from cache, key: " + keyToRemove); + } + + int key = getNextKey(); + + if(s_logger.isTraceEnabled()) + s_logger.trace("Add image to cache, key: " + key); + + cache.put(key, image); + fifoQueue.add(key); + return key; + } + + public synchronized byte[] getImage(int key) { + if (key == 0) { + key = nextKey; + } + if (cache.containsKey(key)) { + if (s_logger.isTraceEnabled()) + s_logger.trace("Retrieve image from cache, key: " + key); + + return cache.get(key); + } + + if (s_logger.isTraceEnabled()) + s_logger.trace("Image is no long in cache, key: " + key); + return null; + } + + public synchronized int getNextKey() { + return ++nextKey; + } +} \ No newline at end of file diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java b/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java new file mode 100644 index 00000000000..3fa266792ae --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +public class AuthenticationException extends Exception { + private static final long serialVersionUID = -393139302884898842L; + public AuthenticationException() { + super(); + } + public AuthenticationException(String s) { + super(s); + } + public AuthenticationException(String message, Throwable cause) { + super(message, cause); + } + public AuthenticationException(Throwable cause) { + super(cause); + } +} \ No newline at end of file diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java new file mode 100644 index 00000000000..a722d8305a2 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java @@ -0,0 +1,499 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Hashtable; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.xml.DOMConfigurator; + +import com.cloud.consoleproxy.util.Logger; +import com.google.gson.Gson; +import com.sun.net.httpserver.HttpServer; + +/** + * + * ConsoleProxy, singleton class that manages overall activities in console proxy process. To make legacy code work, we still + */ +public class ConsoleProxy { + private static final Logger s_logger = Logger.getLogger(ConsoleProxy.class); + + public static final int KEYBOARD_RAW = 0; + public static final int KEYBOARD_COOKED = 1; + + public static int VIEWER_LINGER_SECONDS = 180; + + public static Object context; + + // this has become more ugly, to store keystore info passed from management server (we now use management server managed keystore to support + // dynamically changing to customer supplied certificate) + public static byte[] ksBits; + public static String ksPassword; + + public static Method authMethod; + public static Method reportMethod; + public static Method ensureRouteMethod; + + static Hashtable connectionMap = new Hashtable(); + static int httpListenPort = 80; + static int httpCmdListenPort = 8001; + static int reconnectMaxRetry = 5; + static int readTimeoutSeconds = 90; + static int keyboardType = KEYBOARD_RAW; + static String factoryClzName; + static boolean standaloneStart = false; + + static String encryptorPassword = genDefaultEncryptorPassword(); + + private static String genDefaultEncryptorPassword() { + try { + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + + byte[] randomBytes = new byte[16]; + random.nextBytes(randomBytes); + return Base64.encodeBase64String(randomBytes); + } catch (NoSuchAlgorithmException e) { + s_logger.error("Unexpected exception ", e); + assert(false); + } + + return "Dummy"; + } + + private static void configLog4j() { + URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); + if(configUrl == null) + configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); + + if(configUrl == null) + configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); + + if(configUrl != null) { + try { + System.out.println("Configure log4j using " + configUrl.toURI().toString()); + } catch (URISyntaxException e1) { + e1.printStackTrace(); + } + + try { + File file = new File(configUrl.toURI()); + + System.out.println("Log4j configuration from : " + file.getAbsolutePath()); + DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); + } catch (URISyntaxException e) { + System.out.println("Unable to convert log4j configuration Url to URI"); + } + // DOMConfigurator.configure(configUrl); + } else { + System.out.println("Configure log4j with default properties"); + } + } + + private static void configProxy(Properties conf) { + s_logger.info("Configure console proxy..."); + for(Object key : conf.keySet()) { + s_logger.info("Property " + (String)key + ": " + conf.getProperty((String)key)); + } + + String s = conf.getProperty("consoleproxy.httpListenPort"); + if (s!=null) { + httpListenPort = Integer.parseInt(s); + s_logger.info("Setting httpListenPort=" + s); + } + + s = conf.getProperty("premium"); + if(s != null && s.equalsIgnoreCase("true")) { + s_logger.info("Premium setting will override settings from consoleproxy.properties, listen at port 443"); + httpListenPort = 443; + factoryClzName = "com.cloud.consoleproxy.ConsoleProxySecureServerFactoryImpl"; + } else { + factoryClzName = ConsoleProxyBaseServerFactoryImpl.class.getName(); + } + + s = conf.getProperty("consoleproxy.httpCmdListenPort"); + if (s!=null) { + httpCmdListenPort = Integer.parseInt(s); + s_logger.info("Setting httpCmdListenPort=" + s); + } + + s = conf.getProperty("consoleproxy.reconnectMaxRetry"); + if (s!=null) { + reconnectMaxRetry = Integer.parseInt(s); + s_logger.info("Setting reconnectMaxRetry=" + reconnectMaxRetry); + } + + s = conf.getProperty("consoleproxy.readTimeoutSeconds"); + if (s!=null) { + readTimeoutSeconds = Integer.parseInt(s); + s_logger.info("Setting readTimeoutSeconds=" + readTimeoutSeconds); + } + } + + public static ConsoleProxyServerFactory getHttpServerFactory() { + try { + Class clz = Class.forName(factoryClzName); + try { + ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory)clz.newInstance(); + factory.init(ConsoleProxy.ksBits, ConsoleProxy.ksPassword); + return factory; + } catch (InstantiationException e) { + s_logger.error(e.getMessage(), e); + return null; + } catch (IllegalAccessException e) { + s_logger.error(e.getMessage(), e); + return null; + } + } catch (ClassNotFoundException e) { + s_logger.warn("Unable to find http server factory class: " + factoryClzName); + return new ConsoleProxyBaseServerFactoryImpl(); + } + } + + public static ConsoleProxyAuthenticationResult authenticateConsoleAccess(ConsoleProxyClientParam param, boolean reauthentication) { + + ConsoleProxyAuthenticationResult authResult = new ConsoleProxyAuthenticationResult(); + authResult.setSuccess(true); + authResult.setReauthentication(reauthentication); + authResult.setHost(param.getClientHostAddress()); + authResult.setPort(param.getClientHostPort()); + + if(standaloneStart) { + return authResult; + } + + if(authMethod != null) { + Object result; + try { + result = authMethod.invoke(ConsoleProxy.context, + param.getClientHostAddress(), + String.valueOf(param.getClientHostPort()), + param.getClientTag(), + param.getClientHostPassword(), + param.getTicket(), + new Boolean(reauthentication)); + } catch (IllegalAccessException e) { + s_logger.error("Unable to invoke authenticateConsoleAccess due to IllegalAccessException" + " for vm: " + param.getClientTag(), e); + authResult.setSuccess(false); + return authResult; + } catch (InvocationTargetException e) { + s_logger.error("Unable to invoke authenticateConsoleAccess due to InvocationTargetException " + " for vm: " + param.getClientTag(), e); + authResult.setSuccess(false); + return authResult; + } + + if(result != null && result instanceof String) { + authResult = new Gson().fromJson((String)result, ConsoleProxyAuthenticationResult.class); + } else { + s_logger.error("Invalid authentication return object " + result + " for vm: " + param.getClientTag() + ", decline the access"); + authResult.setSuccess(false); + } + } else { + s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and allow access to vm: " + param.getClientTag()); + } + + return authResult; + } + + public static void reportLoadInfo(String gsonLoadInfo) { + if(reportMethod != null) { + try { + reportMethod.invoke(ConsoleProxy.context, gsonLoadInfo); + } catch (IllegalAccessException e) { + s_logger.error("Unable to invoke reportLoadInfo due to " + e.getMessage()); + } catch (InvocationTargetException e) { + s_logger.error("Unable to invoke reportLoadInfo due to " + e.getMessage()); + } + } else { + s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and ignore load report"); + } + } + + public static void ensureRoute(String address) { + if(ensureRouteMethod != null) { + try { + ensureRouteMethod.invoke(ConsoleProxy.context, address); + } catch (IllegalAccessException e) { + s_logger.error("Unable to invoke ensureRoute due to " + e.getMessage()); + } catch (InvocationTargetException e) { + s_logger.error("Unable to invoke ensureRoute due to " + e.getMessage()); + } + } else { + s_logger.warn("Unable to find ensureRoute method, console proxy agent is not up to date"); + } + } + + public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword) { + s_logger.info("Start console proxy with context"); + if(conf != null) { + for(Object key : conf.keySet()) { + s_logger.info("Context property " + (String)key + ": " + conf.getProperty((String)key)); + } + } + + configLog4j(); + Logger.setFactory(new ConsoleProxyLoggerFactory()); + + // Using reflection to setup private/secure communication channel towards management server + ConsoleProxy.context = context; + ConsoleProxy.ksBits = ksBits; + ConsoleProxy.ksPassword = ksPassword; + try { + Class contextClazz = Class.forName("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource"); + authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, String.class, String.class, String.class, Boolean.class); + reportMethod = contextClazz.getDeclaredMethod("reportLoadInfo", String.class); + ensureRouteMethod = contextClazz.getDeclaredMethod("ensureRoute", String.class); + } catch (SecurityException e) { + s_logger.error("Unable to setup private channel due to SecurityException", e); + } catch (NoSuchMethodException e) { + s_logger.error("Unable to setup private channel due to NoSuchMethodException", e); + } catch (IllegalArgumentException e) { + s_logger.error("Unable to setup private channel due to IllegalArgumentException", e); + } catch(ClassNotFoundException e) { + s_logger.error("Unable to setup private channel due to ClassNotFoundException", e); + } + + // merge properties from conf file + InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties"); + Properties props = new Properties(); + if (confs == null) { + s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration"); + } else { + try { + props.load(confs); + + for(Object key : props.keySet()) { + // give properties passed via context high priority, treat properties from consoleproxy.properties + // as default values + if(conf.get(key) == null) + conf.put(key, props.get(key)); + } + } catch (Exception e) { + s_logger.error(e.toString(), e); + } + } + + start(conf); + } + + public static void start(Properties conf) { + System.setProperty("java.awt.headless", "true"); + + configProxy(conf); + + ConsoleProxyServerFactory factory = getHttpServerFactory(); + if(factory == null) { + s_logger.error("Unable to load console proxy server factory"); + System.exit(1); + } + + if(httpListenPort != 0) { + startupHttpMain(); + } else { + s_logger.error("A valid HTTP server port is required to be specified, please check your consoleproxy.httpListenPort settings"); + System.exit(1); + } + + if(httpCmdListenPort > 0) { + startupHttpCmdPort(); + } else { + s_logger.info("HTTP command port is disabled"); + } + + ConsoleProxyGCThread cthread = new ConsoleProxyGCThread(connectionMap); + cthread.setName("Console Proxy GC Thread"); + cthread.start(); + } + + private static void startupHttpMain() { + try { + ConsoleProxyServerFactory factory = getHttpServerFactory(); + if(factory == null) { + s_logger.error("Unable to load HTTP server factory"); + System.exit(1); + } + + HttpServer server = factory.createHttpServerInstance(httpListenPort); + server.createContext("/getscreen", new ConsoleProxyThumbnailHandler()); + server.createContext("/resource/", new ConsoleProxyResourceHandler()); + server.createContext("/ajax", new ConsoleProxyAjaxHandler()); + server.createContext("/ajaximg", new ConsoleProxyAjaxImageHandler()); + server.setExecutor(new ThreadExecutor()); // creates a default executor + server.start(); + } catch(Exception e) { + s_logger.error(e.getMessage(), e); + System.exit(1); + } + } + + private static void startupHttpCmdPort() { + try { + s_logger.info("Listening for HTTP CMDs on port " + httpCmdListenPort); + HttpServer cmdServer = HttpServer.create(new InetSocketAddress(httpCmdListenPort), 2); + cmdServer.createContext("/cmd", new ConsoleProxyCmdHandler()); + cmdServer.setExecutor(new ThreadExecutor()); // creates a default executor + cmdServer.start(); + } catch(Exception e) { + s_logger.error(e.getMessage(), e); + System.exit(1); + } + } + + public static void main(String[] argv) { + standaloneStart = true; + configLog4j(); + Logger.setFactory(new ConsoleProxyLoggerFactory()); + + InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties"); + Properties conf = new Properties(); + if (confs == null) { + s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration"); + } else { + try { + conf.load(confs); + } catch (Exception e) { + s_logger.error(e.toString(), e); + } + } + start(conf); + } + + public static ConsoleProxyClient getVncViewer(ConsoleProxyClientParam param) throws Exception { + ConsoleProxyClient viewer = null; + + boolean reportLoadChange = false; + String clientKey = param.getClientMapKey(); + synchronized (connectionMap) { + viewer = connectionMap.get(clientKey); + if (viewer == null) { + viewer = new ConsoleProxyVncClient(); + viewer.initClient(param); + connectionMap.put(clientKey, viewer); + s_logger.info("Added viewer object " + viewer); + + reportLoadChange = true; + } else if (!viewer.isFrontEndAlive()) { + s_logger.info("The rfb thread died, reinitializing the viewer " + viewer); + viewer.initClient(param); + } else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) { + s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: " + viewer.getClientHostPassword() + + ", sid in request: " + param.getClientHostPassword()); + viewer.initClient(param); + } + } + + if(reportLoadChange) { + ConsoleProxyClientStatsCollector statsCollector = getStatsCollector(); + String loadInfo = statsCollector.getStatsReport(); + reportLoadInfo(loadInfo); + if(s_logger.isDebugEnabled()) + s_logger.debug("Report load change : " + loadInfo); + } + + return viewer; + } + + public static ConsoleProxyClient getAjaxVncViewer(ConsoleProxyClientParam param, String ajaxSession) throws Exception { + + boolean reportLoadChange = false; + String clientKey = param.getClientMapKey(); + synchronized (connectionMap) { + ConsoleProxyClient viewer = connectionMap.get(clientKey); + if (viewer == null) { + viewer = new ConsoleProxyVncClient(); + viewer.initClient(param); + + connectionMap.put(clientKey, viewer); + s_logger.info("Added viewer object " + viewer); + reportLoadChange = true; + } else if (!viewer.isFrontEndAlive()) { + s_logger.info("The rfb thread died, reinitializing the viewer " + viewer); + viewer.initClient(param); + } else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) { + s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: " + + viewer.getClientHostPassword() + ", sid in request: " + param.getClientHostPassword()); + viewer.initClient(param); + } else { + if(ajaxSession == null || ajaxSession.isEmpty()) + authenticationExternally(param); + } + + if(reportLoadChange) { + ConsoleProxyClientStatsCollector statsCollector = getStatsCollector(); + String loadInfo = statsCollector.getStatsReport(); + reportLoadInfo(loadInfo); + if(s_logger.isDebugEnabled()) + s_logger.debug("Report load change : " + loadInfo); + } + return viewer; + } + } + + public static void removeViewer(ConsoleProxyClient viewer) { + synchronized (connectionMap) { + for(Map.Entry entry : connectionMap.entrySet()) { + if(entry.getValue() == viewer) { + connectionMap.remove(entry.getKey()); + return; + } + } + } + } + + public static ConsoleProxyClientStatsCollector getStatsCollector() { + return new ConsoleProxyClientStatsCollector(connectionMap); + } + + public static void authenticationExternally(ConsoleProxyClientParam param) throws AuthenticationException { + ConsoleProxyAuthenticationResult authResult = authenticateConsoleAccess(param, false); + + if(authResult == null || !authResult.isSuccess()) { + s_logger.warn("External authenticator failed authencation request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword()); + + throw new AuthenticationException("External authenticator failed request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword()); + } + } + + public static ConsoleProxyAuthenticationResult reAuthenticationExternally(ConsoleProxyClientParam param) { + return authenticateConsoleAccess(param, true); + } + + public static String getEncryptorPassword() { + return encryptorPassword; + } + + public static void setEncryptorPassword(String password) { + encryptorPassword = password; + } + + static class ThreadExecutor implements Executor { + public void execute(Runnable r) { + new Thread(r).start(); + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java new file mode 100644 index 00000000000..6cadeca1f4a --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java @@ -0,0 +1,406 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyAjaxHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class); + + public ConsoleProxyAjaxHandler() { + } + + public void handle(HttpExchange t) throws IOException { + try { + if(s_logger.isTraceEnabled()) + s_logger.trace("AjaxHandler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + + doHandle(t); + + if(s_logger.isTraceEnabled()) + s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IOException e) { + throw e; + } catch (IllegalArgumentException e) { + s_logger.warn("Exception, ", e); + t.sendResponseHeaders(400, -1); // bad request + } catch(Throwable e) { + s_logger.error("Unexpected exception, ", e); + t.sendResponseHeaders(500, -1); // server error + } finally { + t.close(); + } + } + + private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { + String queries = t.getRequestURI().getQuery(); + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle AJAX request: " + queries); + + Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); + + String host = queryMap.get("host"); + String portStr = queryMap.get("port"); + String sid = queryMap.get("sid"); + String tag = queryMap.get("tag"); + String ticket = queryMap.get("ticket"); + String ajaxSessionIdStr = queryMap.get("sess"); + String eventStr = queryMap.get("event"); + String console_url = queryMap.get("consoleurl"); + String console_host_session = queryMap.get("sessionref"); + + if(tag == null) + tag = ""; + + long ajaxSessionId = 0; + int event = 0; + + int port; + + if(host == null || portStr == null || sid == null) + throw new IllegalArgumentException(); + + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + portStr); + throw new IllegalArgumentException(e); + } + + if(ajaxSessionIdStr != null) { + try { + ajaxSessionId = Long.parseLong(ajaxSessionIdStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr); + throw new IllegalArgumentException(e); + } + } + + if(eventStr != null) { + try { + event = Integer.parseInt(eventStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + eventStr); + throw new IllegalArgumentException(e); + } + } + + ConsoleProxyClient viewer = null; + try { + ConsoleProxyClientParam param = new ConsoleProxyClientParam(); + param.setClientHostAddress(host); + param.setClientHostPort(port); + param.setClientHostPassword(sid); + param.setClientTag(tag); + param.setTicket(ticket); + param.setClientTunnelUrl(console_url); + param.setClientTunnelSession(console_host_session); + + viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr); + } catch(Exception e) { + + s_logger.warn("Failed to create viewer due to " + e.getMessage(), e); + + String[] content = new String[] { + "", + "
", + "

Access is denied for the console session. Please close the window and retry again

", + "
" + }; + + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < content.length; i++) + sb.append(content[i]); + + sendResponse(t, "text/html", sb.toString()); + return; + } + + if(event != 0) { + if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) { + if(event == 7) { + // client send over an event bag + InputStream is = t.getRequestBody(); + handleClientEventBag(viewer, convertStreamToString(is, true)); + } else { + handleClientEvent(viewer, event, queryMap); + } + sendResponse(t, "text/html", "OK"); + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); + + sendResponse(t, "text/html", "Invalid ajax client session id"); + } + } else { + if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) { + s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId()); + handleClientKickoff(t, viewer); + } else if(ajaxSessionId == 0) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Ajax request indicates a fresh client start"); + + String title = queryMap.get("t"); + String guest = queryMap.get("guest"); + handleClientStart(t, viewer, title != null ? title : "", guest); + } else { + + if(s_logger.isTraceEnabled()) + s_logger.trace("Ajax request indicates client update"); + + handleClientUpdate(t, viewer); + } + } + } + + private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + String line = null; + try { + while ((line = reader.readLine()) != null) { + sb.append(line + "\n"); + } + } catch (IOException e) { + s_logger.warn("Exception while reading request body: ", e); + } finally { + if(closeStreamAfterRead) { + try { + is.close(); + } catch (IOException e) { + } + } + } + return sb.toString(); + } + + private void sendResponse(HttpExchange t, String contentType, String response) throws IOException { + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", contentType); + + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } + + @SuppressWarnings("deprecation") + private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) { + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle event bag, event bag: " + requestData); + + int start = requestData.indexOf("="); + if(start < 0) + start = 0; + else if(start > 0) + start++; + String data = URLDecoder.decode(requestData.substring(start)); + String[] tokens = data.split("\\|"); + if(tokens != null && tokens.length > 0) { + int count = 0; + try { + count = Integer.parseInt(tokens[0]); + int parsePos = 1; + int type, event, x, y, code, modifiers; + for(int i = 0; i < count; i++) { + type = Integer.parseInt(tokens[parsePos++]); + if(type == 1) { + // mouse event + event = Integer.parseInt(tokens[parsePos++]); + x = Integer.parseInt(tokens[parsePos++]); + y = Integer.parseInt(tokens[parsePos++]); + code = Integer.parseInt(tokens[parsePos++]); + modifiers = Integer.parseInt(tokens[parsePos++]); + + Map queryMap = new HashMap(); + queryMap.put("event", String.valueOf(event)); + queryMap.put("x", String.valueOf(x)); + queryMap.put("y", String.valueOf(y)); + queryMap.put("code", String.valueOf(code)); + queryMap.put("modifier", String.valueOf(modifiers)); + handleClientEvent(viewer, event, queryMap); + } else { + // keyboard event + event = Integer.parseInt(tokens[parsePos++]); + code = Integer.parseInt(tokens[parsePos++]); + modifiers = Integer.parseInt(tokens[parsePos++]); + + Map queryMap = new HashMap(); + queryMap.put("event", String.valueOf(event)); + queryMap.put("code", String.valueOf(code)); + queryMap.put("modifier", String.valueOf(modifiers)); + handleClientEvent(viewer, event, queryMap); + } + } + } catch(NumberFormatException e) { + s_logger.warn("Exception in handle client event bag: " + data + ", ", e); + } catch(Exception e) { + s_logger.warn("Exception in handle client event bag: " + data + ", ", e); + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } + } + } + + private void handleClientEvent(ConsoleProxyClient viewer, int event, Map queryMap) { + int code = 0; + int x = 0, y = 0; + int modifiers = 0; + + String str; + switch(event) { + case 1: // mouse move + case 2: // mouse down + case 3: // mouse up + case 8: // mouse double click + str = queryMap.get("x"); + if(str != null) { + try { + x = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + } + str = queryMap.get("y"); + if(str != null) { + try { + y = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + } + + if(event != 1) { + str = queryMap.get("code"); + try { + code = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + str = queryMap.get("modifier"); + try { + modifiers = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers); + } else { + if(s_logger.isTraceEnabled()) + s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y); + } + viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers); + break; + + case 4: // key press + case 5: // key down + case 6: // key up + str = queryMap.get("code"); + try { + code = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + str = queryMap.get("modifier"); + try { + modifiers = Integer.parseInt(str); + } catch (NumberFormatException e) { + s_logger.warn("Invalid number parameter in query string: " + str); + throw new IllegalArgumentException(e); + } + + if(s_logger.isDebugEnabled()) + s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers); + viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers); + break; + + default : + break; + } + } + + private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException { + String response = viewer.onAjaxClientKickoff(); + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } + + private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException { + List languages = t.getRequestHeaders().get("Accept-Language"); + String response = viewer.onAjaxClientStart(title, languages, guest); + + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "text/html"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, response.length()); + + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } + + private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException { + String response = viewer.onAjaxClientUpdate(); + + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "text/javascript"); + t.sendResponseHeaders(200, response.length()); + + OutputStream os = t.getResponseBody(); + try { + os.write(response.getBytes()); + } finally { + os.close(); + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java new file mode 100644 index 00000000000..5e1014963f3 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java @@ -0,0 +1,159 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyAjaxImageHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class); + + public void handle(HttpExchange t) throws IOException { + try { + if(s_logger.isDebugEnabled()) + s_logger.debug("AjaxImageHandler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + + doHandle(t); + + if(s_logger.isDebugEnabled()) + s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IOException e) { + throw e; + } catch (IllegalArgumentException e) { + s_logger.warn("Exception, ", e); + t.sendResponseHeaders(400, -1); // bad request + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch(Throwable e) { + s_logger.error("Unexpected exception, ", e); + t.sendResponseHeaders(500, -1); // server error + } finally { + t.close(); + } + } + + private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { + String queries = t.getRequestURI().getQuery(); + Map queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries); + + String host = queryMap.get("host"); + String portStr = queryMap.get("port"); + String sid = queryMap.get("sid"); + String tag = queryMap.get("tag"); + String ticket = queryMap.get("ticket"); + String keyStr = queryMap.get("key"); + String console_url = queryMap.get("consoleurl"); + String console_host_session = queryMap.get("sessionref"); + String w = queryMap.get("w"); + String h = queryMap.get("h"); + + int key = 0; + int width = 144; + int height = 110; + + if(tag == null) + tag = ""; + + int port; + if(host == null || portStr == null || sid == null) + throw new IllegalArgumentException(); + + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + s_logger.warn("Invalid numeric parameter in query string: " + portStr); + throw new IllegalArgumentException(e); + } + + try { + if (keyStr != null) + key = Integer.parseInt(keyStr); + if(null != w) + width = Integer.parseInt(w); + + if(null != h) + height = Integer.parseInt(h); + + } catch (NumberFormatException e) { + s_logger.warn("Invalid numeric parameter in query string: " + keyStr); + throw new IllegalArgumentException(e); + } + + ConsoleProxyClientParam param = new ConsoleProxyClientParam(); + param.setClientHostAddress(host); + param.setClientHostPort(port); + param.setClientHostPassword(sid); + param.setClientTag(tag); + param.setTicket(ticket); + param.setClientTunnelUrl(console_url); + param.setClientTunnelSession(console_host_session); + + ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); + + if (key == 0) { + Image scaledImage = viewer.getClientScaledImage(width, height); + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_3BYTE_BGR); + Graphics2D bufImageGraphics = bufferedImage.createGraphics(); + bufImageGraphics.drawImage(scaledImage, 0, 0, null); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + } else { + AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache(); + byte[] img = imageCache.getImage(key); + + if(img != null) { + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + t.sendResponseHeaders(200, img.length); + + OutputStream os = t.getResponseBody(); + try { + os.write(img, 0, img.length); + } finally { + os.close(); + } + } else { + if(s_logger.isInfoEnabled()) + s_logger.info("Image has already been swept out, key: " + key); + t.sendResponseHeaders(404, -1); + } + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java new file mode 100644 index 00000000000..26ee9b33155 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java @@ -0,0 +1,81 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +// duplicated class +public class ConsoleProxyAuthenticationResult { + private boolean success; + private boolean isReauthentication; + private String host; + private int port; + private String tunnelUrl; + private String tunnelSession; + + public ConsoleProxyAuthenticationResult() { + success = false; + isReauthentication = false; + port = 0; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean isReauthentication() { + return isReauthentication; + } + + public void setReauthentication(boolean isReauthentication) { + this.isReauthentication = isReauthentication; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getTunnelUrl() { + return tunnelUrl; + } + + public void setTunnelUrl(String tunnelUrl) { + this.tunnelUrl = tunnelUrl; + } + + public String getTunnelSession() { + return tunnelSession; + } + + public void setTunnelSession(String tunnelSession) { + this.tunnelSession = tunnelSession; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java new file mode 100644 index 00000000000..c9ad8ab82fa --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import javax.net.ssl.SSLServerSocket; + +import com.cloud.consoleproxy.util.Logger; +import com.sun.net.httpserver.HttpServer; + +public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class); + + @Override + public void init(byte[] ksBits, String ksPassword) { + } + + @Override + public HttpServer createHttpServerInstance(int port) throws IOException { + if(s_logger.isInfoEnabled()) + s_logger.info("create HTTP server instance at port: " + port); + return HttpServer.create(new InetSocketAddress(port), 5); + } + + @Override + public SSLServerSocket createSSLServerSocket(int port) throws IOException { + if(s_logger.isInfoEnabled()) + s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl"); + + return null; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java new file mode 100644 index 00000000000..8a0be051903 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.awt.Image; +import java.util.List; + +/** + * ConsoleProxyClient defines an standard interface that a console client should implement, + * + * ConsoleProxyClient maintains a session towards the target host, it glues the session + * to a AJAX front-end viewer + */ +public interface ConsoleProxyClient { + int getClientId(); + + // + // Quick status + // + boolean isHostConnected(); + boolean isFrontEndAlive(); + + // + // AJAX viewer + // + long getAjaxSessionId(); + AjaxFIFOImageCache getAjaxImageCache(); + Image getClientScaledImage(int width, int height); // client thumbnail support + + String onAjaxClientStart(String title, List languages, String guest); + String onAjaxClientUpdate(); + String onAjaxClientKickoff(); + + // + // Input handling + // + void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers); + void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers); + + // + // Info/Stats + // + long getClientCreateTime(); + long getClientLastFrontEndActivityTime(); + String getClientHostAddress(); + int getClientHostPort(); + String getClientHostPassword(); + String getClientTag(); + + // + // Setup/house-keeping + // + void initClient(ConsoleProxyClientParam param); + void closeClient(); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java new file mode 100644 index 00000000000..289bdab2f8a --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java @@ -0,0 +1,457 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.awt.Image; +import java.awt.Rectangle; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.consoleproxy.util.TileInfo; +import com.cloud.consoleproxy.util.TileTracker; +import com.cloud.consoleproxy.vnc.FrameBufferCanvas; + +/** + * + * an instance of specialized console protocol implementation, such as VNC or RDP + * + * It mainly implements the features needed by front-end AJAX viewer + * + */ +public abstract class ConsoleProxyClientBase implements ConsoleProxyClient, ConsoleProxyClientListener { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyClientBase.class); + + private static int s_nextClientId = 0; + protected int clientId = getNextClientId(); + + protected long ajaxSessionId = 0; + + protected boolean dirtyFlag = false; + protected Object tileDirtyEvent = new Object(); + protected TileTracker tracker; + protected AjaxFIFOImageCache ajaxImageCache = new AjaxFIFOImageCache(2); + + protected ConsoleProxyClientParam clientParam; + protected String clientToken; + + protected long createTime = System.currentTimeMillis(); + protected long lastFrontEndActivityTime = System.currentTimeMillis(); + + protected boolean framebufferResized = false; + protected int resizedFramebufferWidth; + protected int resizedFramebufferHeight; + + public ConsoleProxyClientBase() { + tracker = new TileTracker(); + tracker.initTracking(64, 64, 800, 600); + } + + // + // interface ConsoleProxyClient + // + @Override + public int getClientId() { + return clientId; + } + + public abstract boolean isHostConnected(); + public abstract boolean isFrontEndAlive(); + + @Override + public long getAjaxSessionId() { + return this.ajaxSessionId; + } + + @Override + public AjaxFIFOImageCache getAjaxImageCache() { + return ajaxImageCache; + } + + public Image getClientScaledImage(int width, int height) { + FrameBufferCanvas canvas = getFrameBufferCavas(); + if(canvas != null) + return canvas.getFrameBufferScaledImage(width, height); + + return null; + } + + public abstract void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers); + public abstract void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers); + + @Override + public long getClientCreateTime() { + return createTime; + } + + @Override + public long getClientLastFrontEndActivityTime() { + return lastFrontEndActivityTime; + } + + @Override + public String getClientHostAddress() { + return clientParam.getClientHostAddress(); + } + + @Override + public int getClientHostPort() { + return clientParam.getClientHostPort(); + } + + @Override + public String getClientHostPassword() { + return clientParam.getClientHostPassword(); + } + + @Override + public String getClientTag() { + if(clientParam.getClientTag() != null) + return clientParam.getClientTag(); + return ""; + } + + @Override + public abstract void initClient(ConsoleProxyClientParam param); + + @Override + public abstract void closeClient(); + + // + // interface FrameBufferEventListener + // + @Override + public void onFramebufferSizeChange(int w, int h) { + tracker.resize(w, h); + + synchronized(this) { + framebufferResized = true; + resizedFramebufferWidth = w; + resizedFramebufferHeight = h; + } + + signalTileDirtyEvent(); + } + + @Override + public void onFramebufferUpdate(int x, int y, int w, int h) { + if(s_logger.isTraceEnabled()) + s_logger.trace("Frame buffer update {" + x + "," + y + "," + w + "," + h + "}"); + tracker.invalidate(new Rectangle(x, y, w, h)); + + signalTileDirtyEvent(); + } + + // + // AJAX Image manipulation + // + public byte[] getFrameBufferJpeg() { + FrameBufferCanvas canvas = getFrameBufferCavas(); + if(canvas != null) + return canvas.getFrameBufferJpeg(); + + return null; + } + + public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight) { + FrameBufferCanvas canvas = getFrameBufferCavas(); + if(canvas != null) + return canvas.getTilesMergedJpeg(tileList, tileWidth, tileHeight); + return null; + } + + private String prepareAjaxImage(List tiles, boolean init) { + byte[] imgBits; + if(init) + imgBits = getFrameBufferJpeg(); + else + imgBits = getTilesMergedJpeg(tiles, tracker.getTileWidth(), tracker.getTileHeight()); + + if(imgBits == null) { + s_logger.warn("Unable to generate jpeg image"); + } else { + if(s_logger.isTraceEnabled()) + s_logger.trace("Generated jpeg image size: " + imgBits.length); + } + + int key = ajaxImageCache.putImage(imgBits); + StringBuffer sb = new StringBuffer(); + sb.append("/ajaximg?token=").append(clientToken); + sb.append("&key=").append(key); + sb.append("&ts=").append(System.currentTimeMillis()); + + return sb.toString(); + } + + private String prepareAjaxSession(boolean init) { + if(init) { + synchronized(this) { + ajaxSessionId++; + } + } + + StringBuffer sb = new StringBuffer(); + sb.append("/ajax?token=").append(clientToken).append("&sess=").append(ajaxSessionId); + return sb.toString(); + } + + @Override + public String onAjaxClientKickoff() { + return "onKickoff();"; + } + + private boolean waitForViewerReady() { + long startTick = System.currentTimeMillis(); + while(System.currentTimeMillis() - startTick < 5000) { + if(getFrameBufferCavas() != null) + return true; + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + return false; + } + + private String onAjaxClientConnectFailed() { + return "

" + + "Unable to start console session as connection is refused by the machine you are accessing" + + "

"; + } + + @Override + public String onAjaxClientStart(String title, List languages, String guest) { + updateFrontEndActivityTime(); + + if(!waitForViewerReady()) + return onAjaxClientConnectFailed(); + + synchronized(this) { + ajaxSessionId++; + framebufferResized = false; + } + + int tileWidth = tracker.getTileWidth(); + int tileHeight = tracker.getTileHeight(); + int width = tracker.getTrackWidth(); + int height = tracker.getTrackHeight(); + + if(s_logger.isTraceEnabled()) + s_logger.trace("Ajax client start, frame buffer w: " + width + ", " + height); + + int retry = 0; + tracker.initCoverageTest(); + while(!tracker.hasFullCoverage() && retry < 10) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + retry++; + } + + List tiles = tracker.scan(true); + String imgUrl = prepareAjaxImage(tiles, true); + String updateUrl = prepareAjaxSession(true); + + StringBuffer sbTileSequence = new StringBuffer(); + int i = 0; + for(TileInfo tile : tiles) { + sbTileSequence.append("[").append(tile.getRow()).append(",").append(tile.getCol()).append("]"); + if(i < tiles.size() - 1) + sbTileSequence.append(","); + + i++; + } + + return getAjaxViewerPageContent(sbTileSequence.toString(), imgUrl, + updateUrl, width, height, tileWidth, tileHeight, title, + ConsoleProxy.keyboardType == ConsoleProxy.KEYBOARD_RAW, + languages, guest); + } + + private String getAjaxViewerPageContent(String tileSequence, String imgUrl, String updateUrl, int width, + int height, int tileWidth, int tileHeight, String title, boolean rawKeyboard, List languages, String guest) { + + StringBuffer sbLanguages = new StringBuffer(""); + if(languages != null) { + for(String lang : languages) { + if(sbLanguages.length() > 0) { + sbLanguages.append(","); + } + sbLanguages.append(lang); + } + } + + String[] content = new String[] { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + title + "", + "", + "", + "
", + "", + "", + "
", + "
", + "", + "", + "" + }; + + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < content.length; i++) + sb.append(content[i]); + + return sb.toString(); + } + + public String onAjaxClientDisconnected() { + return "onDisconnect();"; + } + + @Override + public String onAjaxClientUpdate() { + updateFrontEndActivityTime(); + if(!waitForViewerReady()) + return onAjaxClientDisconnected(); + + synchronized(tileDirtyEvent) { + if(!dirtyFlag) { + try { + tileDirtyEvent.wait(3000); + } catch(InterruptedException e) { + } + } + } + + boolean doResize = false; + synchronized(this) { + if(framebufferResized) { + framebufferResized = false; + doResize = true; + } + } + + List tiles; + + if(doResize) + tiles = tracker.scan(true); + else + tiles = tracker.scan(false); + dirtyFlag = false; + + String imgUrl = prepareAjaxImage(tiles, false); + StringBuffer sbTileSequence = new StringBuffer(); + int i = 0; + for(TileInfo tile : tiles) { + sbTileSequence.append("[").append(tile.getRow()).append(",").append(tile.getCol()).append("]"); + if(i < tiles.size() - 1) + sbTileSequence.append(","); + + i++; + } + + return getAjaxViewerUpdatePageContent(sbTileSequence.toString(), imgUrl, doResize, + resizedFramebufferWidth, resizedFramebufferHeight, + tracker.getTileWidth(), tracker.getTileHeight()); + } + + private String getAjaxViewerUpdatePageContent(String tileSequence, String imgUrl, boolean resized, int width, + int height, int tileWidth, int tileHeight) { + + String[] content = new String[] { + "tileMap = [ " + tileSequence + " ];", + resized ? "ajaxViewer.resize('main_panel', " + width + ", " + height + " , " + tileWidth + ", " + tileHeight + ");" : "", + "ajaxViewer.refresh('" + imgUrl + "', tileMap, false);" + }; + + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < content.length; i++) + sb.append(content[i]); + + return sb.toString(); + } + + // + // Helpers + // + private synchronized static int getNextClientId() { + return ++s_nextClientId; + } + + private void signalTileDirtyEvent() { + synchronized(tileDirtyEvent) { + dirtyFlag = true; + tileDirtyEvent.notifyAll(); + } + } + + public void updateFrontEndActivityTime() { + lastFrontEndActivityTime = System.currentTimeMillis(); + } + + protected abstract FrameBufferCanvas getFrameBufferCavas(); + + public ConsoleProxyClientParam getClientParam() { + return clientParam; + } + + public void setClientParam(ConsoleProxyClientParam clientParam) { + this.clientParam = clientParam; + ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(ConsoleProxy.getEncryptorPassword()); + this.clientToken = encryptor.encryptObject(ConsoleProxyClientParam.class, clientParam); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java new file mode 100644 index 00000000000..43a0bab8bed --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +public interface ConsoleProxyClientListener { + void onFramebufferSizeChange(int w, int h); + void onFramebufferUpdate(int x, int y, int w, int h); + + void onClientConnected(); + void onClientClose(); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java new file mode 100644 index 00000000000..8de4955d4b7 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +/** + * + * Data object to store parameter info needed by client to connect to its host + */ +public class ConsoleProxyClientParam { + + private String clientHostAddress; + private int clientHostPort; + private String clientHostPassword; + private String clientTag; + private String ticket; + + private String clientTunnelUrl; + private String clientTunnelSession; + + private String ajaxSessionId; + + public ConsoleProxyClientParam() { + clientHostPort = 0; + } + + public String getClientHostAddress() { + return clientHostAddress; + } + + public void setClientHostAddress(String clientHostAddress) { + this.clientHostAddress = clientHostAddress; + } + + public int getClientHostPort() { + return clientHostPort; + } + + public void setClientHostPort(int clientHostPort) { + this.clientHostPort = clientHostPort; + } + + public String getClientHostPassword() { + return clientHostPassword; + } + + public void setClientHostPassword(String clientHostPassword) { + this.clientHostPassword = clientHostPassword; + } + + public String getClientTag() { + return clientTag; + } + + public void setClientTag(String clientTag) { + this.clientTag = clientTag; + } + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public String getClientTunnelUrl() { + return clientTunnelUrl; + } + + public void setClientTunnelUrl(String clientTunnelUrl) { + this.clientTunnelUrl = clientTunnelUrl; + } + + public String getClientTunnelSession() { + return clientTunnelSession; + } + + public void setClientTunnelSession(String clientTunnelSession) { + this.clientTunnelSession = clientTunnelSession; + } + + public String getAjaxSessionId() { + return this.ajaxSessionId; + } + + public void setAjaxSessionId(String ajaxSessionId) { + this.ajaxSessionId = ajaxSessionId; + } + + public String getClientMapKey() { + if(clientTag != null && !clientTag.isEmpty()) + return clientTag; + + return clientHostAddress + ":" + clientHostPort; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java new file mode 100644 index 00000000000..15cf451ca2c --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientStatsCollector.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * + * ConsoleProxyClientStatsCollector collects client stats for console proxy agent to report + */ +public class ConsoleProxyClientStatsCollector { + + ArrayList connections; + + public ConsoleProxyClientStatsCollector() { + } + + public ConsoleProxyClientStatsCollector(Hashtable connMap) { + setConnections(connMap); + } + + public String getStatsReport() { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + return gson.toJson(this); + } + + public void getStatsReport(OutputStreamWriter os) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + gson.toJson(this, os); + } + + private void setConnections(Hashtable connMap) { + + ArrayList conns = new ArrayList(); + Enumeration e = connMap.keys(); + while (e.hasMoreElements()) { + synchronized (connMap) { + String key = e.nextElement(); + ConsoleProxyClient client = connMap.get(key); + + ConsoleProxyConnection conn = new ConsoleProxyConnection(); + + conn.id = client.getClientId(); + conn.clientInfo = ""; + conn.host = client.getClientHostAddress(); + conn.port = client.getClientHostPort(); + conn.tag = client.getClientTag(); + conn.createTime = client.getClientCreateTime(); + conn.lastUsedTime = client.getClientLastFrontEndActivityTime(); + conns.add(conn); + } + } + connections = conns; + } + + public static class ConsoleProxyConnection { + public int id; + public String clientInfo; + public String host; + public int port; + public String tag; + public long createTime; + public long lastUsedTime; + + public ConsoleProxyConnection() { + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java new file mode 100644 index 00000000000..408eb0419d7 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import com.cloud.consoleproxy.util.Logger; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyCmdHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyCmdHandler.class); + + public void handle(HttpExchange t) throws IOException { + try { + Thread.currentThread().setName("Cmd Thread " + + Thread.currentThread().getId() + " " + t.getRemoteAddress()); + s_logger.info("CmdHandler " + t.getRequestURI()); + doHandle(t); + } catch (Exception e) { + s_logger.error(e.toString(), e); + String response = "Not found"; + t.sendResponseHeaders(404, response.length()); + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch (Throwable e) { + s_logger.error(e.toString(), e); + } finally { + t.close(); + } + } + + public void doHandle(HttpExchange t) throws Exception { + String path = t.getRequestURI().getPath(); + int i = path.indexOf("/", 1); + String cmd = path.substring(i + 1); + s_logger.info("Get CMD request for " + cmd); + if (cmd.equals("getstatus")) { + ConsoleProxyClientStatsCollector statsCollector = ConsoleProxy.getStatsCollector(); + + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "text/plain"); + t.sendResponseHeaders(200, 0); + OutputStreamWriter os = new OutputStreamWriter(t.getResponseBody()); + statsCollector.getStatsReport(os); + os.close(); + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java new file mode 100644 index 00000000000..7f82a965f69 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyGCThread.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.File; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.apache.log4j.Logger; + +/** + * + * ConsoleProxyGCThread does house-keeping work for the process, it helps cleanup log files, + * recycle idle client sessions without front-end activities and report client stats to external + * management software + */ +public class ConsoleProxyGCThread extends Thread { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyGCThread.class); + + private final static int MAX_SESSION_IDLE_SECONDS = 180; + + private Hashtable connMap; + private long lastLogScan = 0; + + public ConsoleProxyGCThread(Hashtable connMap) { + this.connMap = connMap; + } + + private void cleanupLogging() { + if(lastLogScan != 0 && System.currentTimeMillis() - lastLogScan < 3600000) + return; + + lastLogScan = System.currentTimeMillis(); + + File logDir = new File("./logs"); + File files[] = logDir.listFiles(); + if(files != null) { + for(File file : files) { + if(System.currentTimeMillis() - file.lastModified() >= 86400000L) { + try { + file.delete(); + } catch(Throwable e) { + } + } + } + } + } + + @Override + public void run() { + + boolean bReportLoad = false; + while (true) { + cleanupLogging(); + bReportLoad = false; + + if(s_logger.isDebugEnabled()) + s_logger.debug("connMap=" + connMap); + Enumeration e = connMap.keys(); + while (e.hasMoreElements()) { + String key; + ConsoleProxyClient client; + + synchronized (connMap) { + key = e.nextElement(); + client = connMap.get(key); + } + + long seconds_unused = (System.currentTimeMillis() - client.getClientLastFrontEndActivityTime()) / 1000; + if (seconds_unused < MAX_SESSION_IDLE_SECONDS) { + continue; + } + + synchronized (connMap) { + connMap.remove(key); + bReportLoad = true; + } + + // close the server connection + s_logger.info("Dropping " + client + " which has not been used for " + seconds_unused + " seconds"); + client.closeClient(); + } + + if(bReportLoad) { + // report load changes + String loadInfo = new ConsoleProxyClientStatsCollector(connMap).getStatsReport(); + ConsoleProxy.reportLoadInfo(loadInfo); + if(s_logger.isDebugEnabled()) + s_logger.debug("Report load change : " + loadInfo); + } + + try { Thread.sleep(5000); } catch (InterruptedException ex) {} + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java new file mode 100644 index 00000000000..7756d01cd7f --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.util.HashMap; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; + +public class ConsoleProxyHttpHandlerHelper { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class); + + public static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap(); + for (String param : params) { + String[] paramTokens = param.split("="); + if(paramTokens != null && paramTokens.length == 2) { + String name = param.split("=")[0]; + String value = param.split("=")[1]; + map.put(name, value); + } else if (paramTokens.length == 3) { + // very ugly, added for Xen tunneling url + String name = paramTokens[0]; + String value = paramTokens[1] + "=" + paramTokens[2]; + map.put(name, value); + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("Invalid paramemter in URL found. param: " + param); + } + } + + // This is a ugly solution for now. We will do encryption/decryption translation + // here to make it transparent to rest of the code. + if(map.get("token") != null) { + ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor( + ConsoleProxy.getEncryptorPassword()); + + ConsoleProxyClientParam param = encryptor.decryptObject(ConsoleProxyClientParam.class, map.get("token")); + if(param != null) { + if(param.getClientHostAddress() != null) + map.put("host", param.getClientHostAddress()); + if(param.getClientHostPort() != 0) + map.put("port", String.valueOf(param.getClientHostPort())); + if(param.getClientTag() != null) + map.put("tag", param.getClientTag()); + if(param.getClientHostPassword() != null) + map.put("sid", param.getClientHostPassword()); + if(param.getClientTunnelUrl() != null) + map.put("consoleurl", param.getClientTunnelUrl()); + if(param.getClientTunnelSession() != null) + map.put("sessionref", param.getClientTunnelSession()); + if(param.getTicket() != null) + map.put("ticket", param.getTicket()); + } + } + + return map; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java new file mode 100644 index 00000000000..ff66de3bcc4 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import com.cloud.consoleproxy.util.Logger; +import com.cloud.consoleproxy.util.LoggerFactory; + +public class ConsoleProxyLoggerFactory implements LoggerFactory { + public ConsoleProxyLoggerFactory() { + } + + public Logger getLogger(Class clazz) { + return new Log4jLogger(org.apache.log4j.Logger.getLogger(clazz)); + } + + public static class Log4jLogger extends Logger { + private org.apache.log4j.Logger logger; + + public Log4jLogger(org.apache.log4j.Logger logger) { + this.logger = logger; + } + + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + public void trace(Object message) { + logger.trace(message); + } + + public void trace(Object message, Throwable exception) { + logger.trace(message, exception); + } + + public void info(Object message) { + logger.info(message); + } + + public void info(Object message, Throwable exception) { + logger.info(message, exception); + } + + public void debug(Object message) { + logger.debug(message); + } + + public void debug(Object message, Throwable exception) { + logger.debug(message, exception); + } + + public void warn(Object message) { + logger.warn(message); + } + + public void warn(Object message, Throwable exception) { + logger.warn(message, exception); + } + + public void error(Object message) { + logger.error(message); + } + + public void error(Object message, Throwable exception) { + logger.error(message, exception); + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java new file mode 100644 index 00000000000..030b2f452eb --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyMonitor.java @@ -0,0 +1,153 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.xml.DOMConfigurator; + +import com.cloud.consoleproxy.util.Logger; + + +// +// +// I switched to a simpler solution to monitor only unrecoverable exceptions, under these cases, console proxy process will exit +// itself and the shell script will re-launch console proxy +// +public class ConsoleProxyMonitor { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyMonitor.class); + + private String[] _argv; + private Map _argMap = new HashMap(); + + private volatile Process _process; + private boolean _quit = false; + + public ConsoleProxyMonitor(String[] argv) { + _argv = argv; + + for(String arg : _argv) { + String[] tokens = arg.split("="); + if(tokens.length == 2) { + s_logger.info("Add argument " + tokens[0] + "=" + tokens[1] + " to the argument map"); + + _argMap.put(tokens[0].trim(), tokens[1].trim()); + } else { + s_logger.warn("unrecognized argument, skip adding it to argument map"); + } + } + } + + private void run() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + _quit = true; + onShutdown(); + } + }); + + while(!_quit) { + String cmdLine = getLaunchCommandLine(); + + s_logger.info("Launch console proxy process with command line: " + cmdLine); + + try { + _process = Runtime.getRuntime().exec(cmdLine); + } catch (IOException e) { + s_logger.error("Unexpected exception ", e); + System.exit(1); + } + + boolean waitSucceeded = false; + int exitCode = 0; + while(!waitSucceeded) { + try { + exitCode = _process.waitFor(); + waitSucceeded = true; + + if(s_logger.isInfoEnabled()) + s_logger.info("Console proxy process exits with code: " + exitCode); + } catch (InterruptedException e) { + if(s_logger.isInfoEnabled()) + s_logger.info("InterruptedException while waiting for termination of console proxy, will retry"); + } + } + } + } + + private String getLaunchCommandLine() { + StringBuffer sb = new StringBuffer("java "); + String jvmOptions = _argMap.get("jvmoptions"); + + if(jvmOptions != null) + sb.append(jvmOptions); + + for(Map.Entry entry : _argMap.entrySet()) { + if(!"jvmoptions".equalsIgnoreCase(entry.getKey())) + sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue()); + } + + return sb.toString(); + } + + private void onShutdown() { + if(_process != null) { + if(s_logger.isInfoEnabled()) + s_logger.info("Console proxy monitor shuts dwon, terminate console proxy process"); + _process.destroy(); + } + } + + private static void configLog4j() { + URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); + if(configUrl == null) + configUrl = ClassLoader.getSystemResource("log4j-cloud.xml"); + + if(configUrl == null) + configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml"); + + if(configUrl != null) { + try { + System.out.println("Configure log4j using " + configUrl.toURI().toString()); + } catch (URISyntaxException e1) { + e1.printStackTrace(); + } + + try { + File file = new File(configUrl.toURI()); + + System.out.println("Log4j configuration from : " + file.getAbsolutePath()); + DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); + } catch (URISyntaxException e) { + System.out.println("Unable to convert log4j configuration Url to URI"); + } + } else { + System.out.println("Configure log4j with default properties"); + } + } + + public static void main(String[] argv) { + configLog4j(); + (new ConsoleProxyMonitor(argv)).run(); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java new file mode 100644 index 00000000000..29826f0ea92 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyPasswordBasedEncryptor.java @@ -0,0 +1,142 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * + * A simple password based encyrptor based on DES. It can serialize simple POJO object into URL safe string + * and deserialize it back. + * + */ +public class ConsoleProxyPasswordBasedEncryptor { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class); + + private String password; + private Gson gson; + + public ConsoleProxyPasswordBasedEncryptor(String password) { + this.password = password; + gson = new GsonBuilder().create(); + } + + public String encryptText(String text) { + if(text == null || text.isEmpty()) + return text; + + assert(password != null); + assert(!password.isEmpty()); + + try { + Cipher cipher = Cipher.getInstance("DES"); + int maxKeySize = 8; + SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + byte[] encryptedBytes = cipher.doFinal(text.getBytes()); + return Base64.encodeBase64URLSafeString(encryptedBytes); + } catch (NoSuchAlgorithmException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (NoSuchPaddingException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (IllegalBlockSizeException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (BadPaddingException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (InvalidKeyException e) { + s_logger.error("Unexpected exception ", e); + return null; + } + } + + public String decryptText(String encryptedText) { + if(encryptedText == null || encryptedText.isEmpty()) + return encryptedText; + + assert(password != null); + assert(!password.isEmpty()); + + try { + Cipher cipher = Cipher.getInstance("DES"); + int maxKeySize = 8; + SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES"); + cipher.init(Cipher.DECRYPT_MODE, keySpec); + + byte[] encryptedBytes = Base64.decodeBase64(encryptedText); + return new String(cipher.doFinal(encryptedBytes)); + } catch (NoSuchAlgorithmException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (NoSuchPaddingException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (IllegalBlockSizeException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (BadPaddingException e) { + s_logger.error("Unexpected exception ", e); + return null; + } catch (InvalidKeyException e) { + s_logger.error("Unexpected exception ", e); + return null; + } + } + + public String encryptObject(Class clz, T obj) { + if(obj == null) + return null; + + String json = gson.toJson(obj); + return encryptText(json); + } + + @SuppressWarnings("unchecked") + public T decryptObject(Class clz, String encrypted) { + if(encrypted == null || encrypted.isEmpty()) + return null; + + String json = decryptText(encrypted); + return (T)gson.fromJson(json, clz); + } + + private static byte[] normalizeKey(byte[] keyBytes, int keySize) { + assert(keySize > 0); + byte[] key = new byte[keySize]; + + for(int i = 0; i < keyBytes.length; i++) + key[i%keySize] ^= keyBytes[i]; + + return key; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java new file mode 100644 index 00000000000..7d160472e8a --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java @@ -0,0 +1,181 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyResourceHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyResourceHandler.class); + + static Map s_mimeTypes; + static { + s_mimeTypes = new HashMap(); + s_mimeTypes.put("jar", "application/java-archive"); + s_mimeTypes.put("js", "text/javascript"); + s_mimeTypes.put("css", "text/css"); + s_mimeTypes.put("jpg", "image/jpeg"); + s_mimeTypes.put("html", "text/html"); + s_mimeTypes.put("htm", "text/html"); + s_mimeTypes.put("log", "text/plain"); + } + + static Map s_validResourceFolders; + static { + s_validResourceFolders = new HashMap(); + s_validResourceFolders.put("applet", ""); + s_validResourceFolders.put("logs", ""); + s_validResourceFolders.put("images", ""); + s_validResourceFolders.put("js", ""); + s_validResourceFolders.put("css", ""); + s_validResourceFolders.put("html", ""); + } + + public ConsoleProxyResourceHandler() { + } + + public void handle(HttpExchange t) throws IOException { + try { + if(s_logger.isDebugEnabled()) + s_logger.debug("Resource Handler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + + doHandle(t); + + if(s_logger.isDebugEnabled()) + s_logger.debug(t.getRequestURI() + " Process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IOException e) { + throw e; + } catch(Throwable e) { + s_logger.error("Unexpected exception, ", e); + t.sendResponseHeaders(500, -1); // server error + } finally { + t.close(); + } + } + + @SuppressWarnings("deprecation") + private void doHandle(HttpExchange t) throws Exception { + String path = t.getRequestURI().getPath(); + + if(s_logger.isInfoEnabled()) + s_logger.info("Get resource request for " + path); + + int i = path.indexOf("/", 1); + String filepath = path.substring(i + 1); + i = path.lastIndexOf("."); + String extension = (i == -1) ? "" : path.substring(i + 1); + String contentType = getContentType(extension); + + if(!validatePath(filepath)) { + if(s_logger.isInfoEnabled()) + s_logger.info("Resource access is forbidden, uri: " + path); + + t.sendResponseHeaders(403, -1); // forbidden + return; + } + + File f = new File ("./" + filepath); + if(f.exists()) { + long lastModified = f.lastModified(); + String ifModifiedSince = t.getRequestHeaders().getFirst("If-Modified-Since"); + if (ifModifiedSince != null) { + long d = Date.parse(ifModifiedSince); + if (d + 1000 >= lastModified) { + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", contentType); + t.sendResponseHeaders(304, -1); + + if(s_logger.isInfoEnabled()) + s_logger.info("Sent 304 file has not been " + + "modified since " + ifModifiedSince); + return; + } + } + + long length = f.length(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", contentType); + hds.set("Last-Modified", new Date(lastModified).toGMTString()); + t.sendResponseHeaders(200, length); + responseFileContent(t, f); + + if(s_logger.isInfoEnabled()) + s_logger.info("Sent file " + path + " with content type " + contentType); + } else { + if(s_logger.isInfoEnabled()) + s_logger.info("file does not exist" + path); + t.sendResponseHeaders(404, -1); + } + } + + private static String getContentType(String extension) { + String key = extension.toLowerCase(); + if(s_mimeTypes.containsKey(key)) { + return s_mimeTypes.get(key); + } + return "application/octet-stream"; + } + + private static void responseFileContent(HttpExchange t, File f) throws Exception { + OutputStream os = t.getResponseBody(); + FileInputStream fis = new FileInputStream(f); + while (true) { + byte[] b = new byte[8192]; + int n = fis.read(b); + if (n < 0) { + break; + } + os.write(b, 0, n); + } + fis.close(); + os.close(); + } + + private static boolean validatePath(String path) { + int i = path.indexOf("/"); + if(i == -1) { + if(s_logger.isInfoEnabled()) + s_logger.info("Invalid resource path: can not start at resource root"); + return false; + } + + if(path.contains("..")) { + if(s_logger.isInfoEnabled()) + s_logger.info("Invalid resource path: contains relative up-level navigation"); + + return false; + } + + return isValidResourceFolder(path.substring(0, i)); + } + + private static boolean isValidResourceFolder(String name) { + return s_validResourceFolders.containsKey(name); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java new file mode 100644 index 00000000000..ee0ee13fa92 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxySecureServerFactoryImpl.java @@ -0,0 +1,145 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.security.KeyStore; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.log4j.Logger; + +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsParameters; +import com.sun.net.httpserver.HttpsServer; + +public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFactory { + private static final Logger s_logger = Logger.getLogger(ConsoleProxySecureServerFactoryImpl.class); + + private SSLContext sslContext = null; + + public ConsoleProxySecureServerFactoryImpl() { + } + + @Override + public void init(byte[] ksBits, String ksPassword) { + s_logger.info("Start initializing SSL"); + + if(ksBits == null) { + try { + s_logger.info("Initializing SSL from built-in default certificate"); + + char[] passphrase = "vmops.com".toCharArray(); + KeyStore ks = KeyStore.getInstance("JKS"); + + ks.load(new FileInputStream("certs/realhostip.keystore"), passphrase); + // ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase); + + s_logger.info("SSL certificate loaded"); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + s_logger.info("Key manager factory is initialized"); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + s_logger.info("Trust manager factory is initialized"); + + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + s_logger.info("SSL context is initialized"); + } catch (Exception ioe) { + s_logger.error(ioe.toString(), ioe); + } + + } else { + char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null; + try { + s_logger.info("Initializing SSL from passed-in certificate"); + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new ByteArrayInputStream(ksBits), passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + s_logger.info("Key manager factory is initialized"); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + s_logger.info("Trust manager factory is initialized"); + + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + s_logger.info("SSL context is initialized"); + } catch(Exception e) { + s_logger.error("Unable to init factory due to exception ", e); + } + } + + } + + public HttpServer createHttpServerInstance(int port) throws IOException { + try { + HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5); + server.setHttpsConfigurator (new HttpsConfigurator(sslContext) { + @Override + public void configure (HttpsParameters params) { + + // get the remote address if needed + InetSocketAddress remote = params.getClientAddress(); + SSLContext c = getSSLContext(); + + // get the default parameters + SSLParameters sslparams = c.getDefaultSSLParameters(); + + params.setSSLParameters(sslparams); + // statement above could throw IAE if any params invalid. + // eg. if app has a UI and parameters supplied by a user. + } + }); + + s_logger.info("create HTTPS server instance on port: " + port); + return server; + } catch (Exception ioe) { + s_logger.error(ioe.toString(), ioe); + } + return null; + } + + public SSLServerSocket createSSLServerSocket(int port) throws IOException { + try { + SSLServerSocket srvSock = null; + SSLServerSocketFactory ssf = sslContext.getServerSocketFactory(); + srvSock = (SSLServerSocket) ssf.createServerSocket(port); + + s_logger.info("create SSL server socket on port: " + port); + return srvSock; + } catch (Exception ioe) { + s_logger.error(ioe.toString(), ioe); + } + return null; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java new file mode 100644 index 00000000000..7e0e5c77bf4 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyServerFactory.java @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.IOException; + +import javax.net.ssl.SSLServerSocket; + +import com.sun.net.httpserver.HttpServer; + +public interface ConsoleProxyServerFactory { + void init(byte[] ksBits, String ksPassword); + HttpServer createHttpServerInstance(int port) throws IOException; + SSLServerSocket createSSLServerSocket(int port) throws IOException; +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java new file mode 100644 index 00000000000..6d34d3b9162 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java @@ -0,0 +1,212 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +import com.cloud.consoleproxy.util.Logger; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class ConsoleProxyThumbnailHandler implements HttpHandler { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyThumbnailHandler.class); + + public ConsoleProxyThumbnailHandler() { + } + + public void handle(HttpExchange t) throws IOException { + try { + Thread.currentThread().setName("JPG Thread " + + Thread.currentThread().getId() + " " + t.getRemoteAddress()); + + if(s_logger.isDebugEnabled()) + s_logger.debug("ScreenHandler " + t.getRequestURI()); + + long startTick = System.currentTimeMillis(); + doHandle(t); + + if(s_logger.isDebugEnabled()) + s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms"); + } catch (IllegalArgumentException e) { + String response = "Bad query string"; + s_logger.error(response + ", request URI : " + t.getRequestURI()); + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } catch(OutOfMemoryError e) { + s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched"); + System.exit(1); + } catch (Throwable e) { + s_logger.error("Unexpected exception while handing thumbnail request, ", e); + + String queries = t.getRequestURI().getQuery(); + Map queryMap = getQueryMap(queries); + int width = 0; + int height = 0; + String ws = queryMap.get("w"); + String hs = queryMap.get("h"); + try { + width = Integer.parseInt(ws); + height = Integer.parseInt(hs); + } catch (NumberFormatException ex) { + } + width = Math.min(width, 800); + height = Math.min(height, 600); + + BufferedImage img = generateTextImage(width, height, "Cannot Connect"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(img, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + s_logger.error("Cannot get console, sent error JPG response for " + t.getRequestURI()); + return; + } finally { + t.close(); + } + } + + private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException { + String queries = t.getRequestURI().getQuery(); + Map queryMap = getQueryMap(queries); + int width = 0; + int height = 0; + int port = 0; + String ws = queryMap.get("w"); + String hs = queryMap.get("h"); + String host = queryMap.get("host"); + String portStr = queryMap.get("port"); + String sid = queryMap.get("sid"); + String tag = queryMap.get("tag"); + String ticket = queryMap.get("ticket"); + String console_url = queryMap.get("consoleurl"); + String console_host_session = queryMap.get("sessionref"); + + if(tag == null) + tag = ""; + + if (ws == null || hs == null || host == null || portStr == null || sid == null ) { + throw new IllegalArgumentException(); + } + try { + width = Integer.parseInt(ws); + height = Integer.parseInt(hs); + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(e); + } + + ConsoleProxyClientParam param = new ConsoleProxyClientParam(); + param.setClientHostAddress(host); + param.setClientHostPort(port); + param.setClientHostPassword(sid); + param.setClientTag(tag); + param.setTicket(ticket); + param.setClientTunnelUrl(console_url); + param.setClientTunnelSession(console_host_session); + + ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param); + + if (!viewer.isHostConnected()) { + // use generated image instead of static + BufferedImage img = generateTextImage(width, height, "Connecting"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(img, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + + if(s_logger.isInfoEnabled()) + s_logger.info("Console not ready, sent dummy JPG response"); + return; + } + + { + Image scaledImage = viewer.getClientScaledImage(width, height); + BufferedImage bufferedImage = new BufferedImage(width, height, + BufferedImage.TYPE_3BYTE_BGR); + Graphics2D bufImageGraphics = bufferedImage.createGraphics(); + bufImageGraphics.drawImage(scaledImage, 0, 0, null); + ByteArrayOutputStream bos = new ByteArrayOutputStream(8196); + javax.imageio.ImageIO.write(bufferedImage, "jpg", bos); + byte[] bs = bos.toByteArray(); + Headers hds = t.getResponseHeaders(); + hds.set("Content-Type", "image/jpeg"); + hds.set("Cache-Control", "no-cache"); + hds.set("Cache-Control", "no-store"); + t.sendResponseHeaders(200, bs.length); + OutputStream os = t.getResponseBody(); + os.write(bs); + os.close(); + } + } + + public static BufferedImage generateTextImage(int w, int h, String text) { + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = img.createGraphics(); + g.setColor(Color.BLACK); + g.fillRect(0, 0, w, h); + g.setColor(Color.WHITE); + try { + g.setFont(new Font(null, Font.PLAIN, 12)); + FontMetrics fm = g.getFontMetrics(); + int textWidth = fm.stringWidth(text); + int startx = (w-textWidth) / 2; + if(startx < 0) + startx = 0; + g.drawString(text, startx, h/2); + } catch (Throwable e) { + s_logger.warn("Problem in generating text to thumnail image, return blank image"); + } + return img; + } + + public static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap(); + for (String param : params) { + String name = param.split("=")[0]; + String value = param.split("=")[1]; + map.put(name, value); + } + return map; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java new file mode 100644 index 00000000000..6a473b5f007 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyVncClient.java @@ -0,0 +1,235 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +import java.io.IOException; +import java.net.URI; +import java.net.UnknownHostException; + +import org.apache.log4j.Logger; + +import com.cloud.consoleproxy.vnc.FrameBufferCanvas; +import com.cloud.consoleproxy.vnc.RfbConstants; +import com.cloud.consoleproxy.vnc.VncClient; + +/** + * + * ConsoleProxyVncClient bridges a VNC engine with the front-end AJAX viewer + * + */ +public class ConsoleProxyVncClient extends ConsoleProxyClientBase { + private static final Logger s_logger = Logger.getLogger(ConsoleProxyVncClient.class); + + private static final int SHIFT_KEY_MASK = 64; + private static final int CTRL_KEY_MASK = 128; + private static final int META_KEY_MASK = 256; + private static final int ALT_KEY_MASK = 512; + + private static final int X11_KEY_SHIFT = 0xffe1; + private static final int X11_KEY_CTRL = 0xffe3; + private static final int X11_KEY_ALT = 0xffe9; + private static final int X11_KEY_META = 0xffe7; + + private VncClient client; + private Thread worker; + private boolean workerDone = false; + + private int lastModifierStates = 0; + private int lastPointerMask = 0; + + public ConsoleProxyVncClient() { + } + + public boolean isHostConnected() { + if(client != null) + return client.isHostConnected(); + + return false; + } + + @Override + public boolean isFrontEndAlive() { + if(workerDone || System.currentTimeMillis() - getClientLastFrontEndActivityTime() > ConsoleProxy.VIEWER_LINGER_SECONDS*1000) { + s_logger.info("Front end has been idle for too long"); + return false; + } + return true; + } + + @Override + public void initClient(ConsoleProxyClientParam param) { + setClientParam(param); + + client = new VncClient(this); + worker = new Thread(new Runnable() { + public void run() { + String tunnelUrl = getClientParam().getClientTunnelUrl(); + String tunnelSession = getClientParam().getClientTunnelSession(); + + for(int i = 0; i < 15; i++) { + try { + if(tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null && !tunnelSession.isEmpty()) { + URI uri = new URI(tunnelUrl); + s_logger.info("Connect to VNC server via tunnel. url: " + tunnelUrl + ", session: " + tunnelSession); + + ConsoleProxy.ensureRoute(uri.getHost()); + client.connectTo( + uri.getHost(), uri.getPort(), + uri.getPath() + "?" + uri.getQuery(), + tunnelSession, "https".equalsIgnoreCase(uri.getScheme()), + getClientHostPassword()); + } else { + s_logger.info("Connect to VNC server directly. host: " + getClientHostAddress() + ", port: " + getClientHostPort()); + ConsoleProxy.ensureRoute(getClientHostAddress()); + client.connectTo(getClientHostAddress(), getClientHostPort(), getClientHostPassword()); + } + } catch (UnknownHostException e) { + s_logger.error("Unexpected exception (will retry until timeout)", e); + } catch (IOException e) { + s_logger.error("Unexpected exception (will retry until timeout) ", e); + } catch (Throwable e) { + s_logger.error("Unexpected exception (will retry until timeout) ", e); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + + if(tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null && !tunnelSession.isEmpty()) { + ConsoleProxyAuthenticationResult authResult = ConsoleProxy.reAuthenticationExternally(getClientParam()); + if(authResult != null && authResult.isSuccess()) { + if(authResult.getTunnelUrl() != null && !authResult.getTunnelUrl().isEmpty() && + authResult.getTunnelSession() != null && !authResult.getTunnelSession().isEmpty()) { + tunnelUrl = authResult.getTunnelUrl(); + tunnelSession = authResult.getTunnelSession(); + + s_logger.info("Reset XAPI session. url: " + tunnelUrl + ", session: " + tunnelSession); + } + } + } + } + + s_logger.info("Receiver thread stopped."); + workerDone = true; + client.getClientListener().onClientClose(); + } + }); + + worker.setDaemon(true); + worker.start(); + } + + @Override + public void closeClient() { + if(client != null) + client.shutdown(); + } + + @Override + public void onClientConnected() { + } + + public void onClientClose() { + s_logger.info("Received client close indication. remove viewer from map."); + + ConsoleProxy.removeViewer(this); + } + + @Override + public void onFramebufferUpdate(int x, int y, int w, int h) { + super.onFramebufferUpdate(x, y, w, h); + client.requestUpdate(false); + } + + public void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers) { + if(client == null) + return; + + updateFrontEndActivityTime(); + + switch(event) { + case KEY_DOWN : + sendModifierEvents(modifiers); + client.sendClientKeyboardEvent(RfbConstants.KEY_DOWN, code, 0); + break; + + case KEY_UP : + client.sendClientKeyboardEvent(RfbConstants.KEY_UP, code, 0); + sendModifierEvents(0); + break; + + case KEY_PRESS : + break; + + default : + assert(false); + break; + } + } + + public void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers) { + if(client == null) + return; + + updateFrontEndActivityTime(); + + if (event == InputEventType.MOUSE_DOWN) { + if (code == 2) { + lastPointerMask |= 4; + } else if (code == 0) { + lastPointerMask |= 1; + } + } + + if (event == InputEventType.MOUSE_UP) { + if (code == 2) { + lastPointerMask ^= 4; + } else if (code == 0) { + lastPointerMask ^= 1; + } + } + + sendModifierEvents(modifiers); + client.sendClientMouseEvent(lastPointerMask, x, y, code, modifiers); + if(lastPointerMask == 0) + sendModifierEvents(0); + } + + @Override + protected FrameBufferCanvas getFrameBufferCavas() { + if(client != null) + return client.getFrameBufferCanvas(); + return null; + } + + private void sendModifierEvents(int modifiers) { + if((modifiers & SHIFT_KEY_MASK) != (lastModifierStates & SHIFT_KEY_MASK)) + client.sendClientKeyboardEvent((modifiers & SHIFT_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_SHIFT, 0); + + if((modifiers & CTRL_KEY_MASK) != (lastModifierStates & CTRL_KEY_MASK)) + client.sendClientKeyboardEvent((modifiers & CTRL_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_CTRL, 0); + + if((modifiers & META_KEY_MASK) != (lastModifierStates & META_KEY_MASK)) + client.sendClientKeyboardEvent((modifiers & META_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_META, 0); + + if((modifiers & ALT_KEY_MASK) != (lastModifierStates & ALT_KEY_MASK)) + client.sendClientKeyboardEvent((modifiers & ALT_KEY_MASK) != 0 ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP, X11_KEY_ALT, 0); + + lastModifierStates = modifiers; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/InputEventType.java b/services/console-proxy/server/src/com/cloud/consoleproxy/InputEventType.java new file mode 100644 index 00000000000..4a5aff16bcf --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/InputEventType.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy; + +public enum InputEventType { + MOUSE_MOVE(1), + MOUSE_DOWN(2), + MOUSE_UP(3), + KEY_PRESS(4), + KEY_DOWN(5), + KEY_UP(6), + MOUSE_DBLCLICK(8); + + int eventCode; + private InputEventType(int eventCode) { + this.eventCode = eventCode; + } + + public int getEventCode() { + return eventCode; + } + + public static InputEventType fromEventCode(int eventCode) { + switch(eventCode) { + case 1 : + return MOUSE_MOVE; + case 2 : + return MOUSE_DOWN; + case 3 : + return MOUSE_UP; + case 4 : + return KEY_PRESS; + case 5 : + return KEY_DOWN; + case 6 : + return KEY_UP; + case 8 : + return MOUSE_DBLCLICK; + default : + break; + } + throw new IllegalArgumentException("Unsupport event code: " + eventCode); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/ITileScanListener.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/ITileScanListener.java new file mode 100644 index 00000000000..2ff82d7242f --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/ITileScanListener.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.List; + +public interface ITileScanListener { + boolean onTileChange(Rectangle rowMergedRect, int row, int col); + void onRegionChange(List regionList); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/ImageHelper.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/ImageHelper.java new file mode 100644 index 00000000000..bb7373e78b2 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/ImageHelper.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class ImageHelper { + public static byte[] jpegFromImage(BufferedImage image) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(128000); + javax.imageio.ImageIO.write(image, "jpg", bos); + + byte[] jpegBits = bos.toByteArray(); + bos.close(); + return jpegBits; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/Logger.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/Logger.java new file mode 100644 index 00000000000..f4357bd4bc4 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/Logger.java @@ -0,0 +1,223 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +// logger facility for dynamic switch between console logger used in Applet and log4j based logger +public class Logger { + private static LoggerFactory factory = null; + + public static final int LEVEL_TRACE = 1; + public static final int LEVEL_DEBUG = 2; + public static final int LEVEL_INFO = 3; + public static final int LEVEL_WARN = 4; + public static final int LEVEL_ERROR = 5; + + private Class clazz; + private Logger logger; + + private static int level = LEVEL_INFO; + + public static Logger getLogger(Class clazz) { + return new Logger(clazz); + } + + public static void setFactory(LoggerFactory f) { + factory = f; + } + + public static void setLevel(int l) { + level = l; + } + + public Logger(Class clazz) { + this.clazz = clazz; + } + + protected Logger() { + } + + public boolean isTraceEnabled() { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + return logger.isTraceEnabled(); + } + return level <= LEVEL_TRACE; + } + + public boolean isDebugEnabled() { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + return logger.isDebugEnabled(); + } + return level <= LEVEL_DEBUG; + } + + public boolean isInfoEnabled() { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + return logger.isInfoEnabled(); + } + return level <= LEVEL_INFO; + } + + public void trace(Object message) { + + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.trace(message); + } else { + if(level <= LEVEL_TRACE) + System.out.println(message); + } + } + + public void trace(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.trace(message, exception); + } else { + if(level <= LEVEL_TRACE) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void info(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.info(message); + } else { + if(level <= LEVEL_INFO) + System.out.println(message); + } + } + + public void info(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.info(message, exception); + } else { + if(level <= LEVEL_INFO) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void debug(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.debug(message); + } else { + if(level <= LEVEL_DEBUG) + System.out.println(message); + } + } + + public void debug(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.debug(message, exception); + } else { + if(level <= LEVEL_DEBUG) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void warn(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.warn(message); + } else { + if(level <= LEVEL_WARN) + System.out.println(message); + } + } + + public void warn(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.warn(message, exception); + } else { + if(level <= LEVEL_WARN) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } + + public void error(Object message) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.error(message); + } else { + if(level <= LEVEL_ERROR) + System.out.println(message); + } + } + + public void error(Object message, Throwable exception) { + if(factory != null) { + if(logger == null) + logger = factory.getLogger(clazz); + + logger.error(message, exception); + } else { + if(level <= LEVEL_ERROR) { + System.out.println(message); + if (exception != null) { + exception.printStackTrace(System.out); + } + } + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/LoggerFactory.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/LoggerFactory.java new file mode 100644 index 00000000000..121411adf16 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/LoggerFactory.java @@ -0,0 +1,21 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +public interface LoggerFactory { + Logger getLogger(Class clazz); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/RawHTTP.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/RawHTTP.java new file mode 100644 index 00000000000..c77b5511270 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/RawHTTP.java @@ -0,0 +1,249 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +// +// This file is originally from XenConsole with modifications +// + +/** + * Send an HTTP CONNECT or PUT request to a XenAPI host with a Session ID, + * return the connected socket and the Task ID. Used for tunnelling VNC + * connections and import/export operations. + */ +public final class RawHTTP { + private static final Logger s_logger = Logger.getLogger(RawHTTP.class); + + private static final Pattern END_PATTERN = Pattern.compile("^\r\n$"); + private static final Pattern HEADER_PATTERN = Pattern + .compile("^([A-Z_a-z0-9-]+):\\s*(.*)\r\n$"); + private static final Pattern HTTP_PATTERN = Pattern + .compile("^HTTP/\\d+\\.\\d+ (\\d*) (.*)\r\n$"); + + /** + * @uml.property name="command" + */ + private final String command; + /** + * @uml.property name="host" + */ + private final String host; + /** + * @uml.property name="port" + */ + private final int port; + /** + * @uml.property name="path" + */ + private final String path; + /** + * @uml.property name="session" + */ + private final String session; + /** + * @uml.property name="useSSL" + */ + private final boolean useSSL; + + /** + * @uml.property name="responseHeaders" + * @uml.associationEnd qualifier="group:java.lang.String java.lang.String" + */ + private final Map responseHeaders = new HashMap(); + + /** + * @uml.property name="ic" + */ + private InputStream ic; + /** + * @uml.property name="oc" + */ + private OutputStream oc; + /** + * @uml.property name="s" + */ + private Socket s; + + public InputStream getInputStream() { + return ic; + } + + public OutputStream getOutputStream() { + return oc; + } + + public Socket getSocket() { + return s; + } + + public RawHTTP(String command, String host, int port, String path, + String session, boolean useSSL) { + this.command = command; + this.host = host; + this.port = port; + this.path = path; + this.session = session; + this.useSSL = useSSL; + } + + private static final TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + + private Socket _getSocket() throws IOException { + if (useSSL) { + SSLContext context = getClientSSLContext(); + if(context == null) + throw new IOException("Unable to setup SSL context"); + + SSLSocket ssl = null; + try { + context.init(null, trustAllCerts, new SecureRandom()); + SocketFactory factory = context.getSocketFactory(); + ssl = (SSLSocket) factory.createSocket(host, port); + /* ssl.setSSLParameters(context.getDefaultSSLParameters()); */ + } catch (IOException e) { + s_logger.error("IOException: " + e.getMessage(), e); + throw e; + } catch (KeyManagementException e) { + s_logger.error("KeyManagementException: " + e.getMessage(), e); + } + return ssl; + } else { + return new Socket(host, port); + } + } + + public Socket connect() throws IOException { + String[] headers = makeHeaders(); + s = _getSocket(); + try { + oc = s.getOutputStream(); + for (String header : headers) { + oc.write(header.getBytes()); + oc.write("\r\n".getBytes()); + } + oc.flush(); + ic = s.getInputStream(); + while (true) { + String line = readline(ic); + + Matcher m = END_PATTERN.matcher(line); + if (m.matches()) { + return s; + } + + m = HEADER_PATTERN.matcher(line); + if (m.matches()) { + responseHeaders.put(m.group(1), m.group(2)); + continue; + } + + m = HTTP_PATTERN.matcher(line); + if (m.matches()) { + String status_code = m.group(1); + String reason_phrase = m.group(2); + if (!"200".equals(status_code)) { + throw new IOException("HTTP status " + status_code + + " " + reason_phrase); + } + } else { + throw new IOException("Unknown HTTP line " + line); + } + } + } catch (IOException exn) { + s.close(); + throw exn; + } catch (RuntimeException exn) { + s.close(); + throw exn; + } + } + + public Map getResponseHeaders() { + return responseHeaders; + } + + private String[] makeHeaders() { + String[] headers = { String.format("%s %s HTTP/1.0", command, path), + String.format("Host: %s", host), + String.format("Cookie: session_id=%s", session), "" }; + return headers; + } + + private static String readline(InputStream ic) throws IOException { + String result = ""; + while (true) { + try { + int c = ic.read(); + + if (c == -1) { + return result; + } + result = result + (char) c; + if (c == 0x0a /* LF */) { + return result; + } + } catch (IOException e) { + ic.close(); + throw e; + } + } + } + + private SSLContext getClientSSLContext() { + SSLContext sslContext = null; + try { + sslContext = SSLContext.getInstance("SSL", "SunJSSE"); + } catch (NoSuchAlgorithmException e) { + s_logger.error("Unexpected exception ", e); + } catch (NoSuchProviderException e) { + s_logger.error("Unexpected exception ", e); + } + return sslContext; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/Region.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/Region.java new file mode 100644 index 00000000000..a5d1751d63b --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/Region.java @@ -0,0 +1,90 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class Region { + private Rectangle bound; + private List rectList; + + public Region() { + bound = new Rectangle(0, 0, 0, 0); + rectList = new ArrayList(); + } + + public Region(Rectangle rect) { + bound = new Rectangle(rect.x, rect.y, rect.width, rect.height); + rectList = new ArrayList(); + rectList.add(rect); + } + + public Rectangle getBound() { + return bound; + } + + public void clearBound() { + assert(rectList.size() == 0); + bound.x = bound.y = bound.width = bound.height = 0; + } + + public List getRectangles() { + return rectList; + } + + public boolean add(Rectangle rect) { + if(bound.isEmpty()) { + assert(rectList.size() == 0); + bound.x = rect.x; + bound.y = rect.y; + bound.width = rect.width; + bound.height = rect.height; + + rectList.add(rect); + return true; + } + + Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y- 1, rect.width + 2, rect.height + 2); + if(!bound.intersects(rcInflated)) + return false; + + for(Rectangle r : rectList) { + if(r.intersects(rcInflated)) { + if(!r.contains(rect)) { + enlargeBound(rect); + rectList.add(rect); + return true; + } + } + } + return false; + } + + private void enlargeBound(Rectangle rect) { + int boundLeft = Math.min(bound.x, rect.x); + int boundTop = Math.min(bound.y, rect.y); + int boundRight = Math.max(bound.x + bound.width, rect.x + rect.width); + int boundBottom = Math.max(bound.y + bound.height, rect.y + rect.height); + + bound.x = boundLeft; + bound.y = boundTop; + bound.width = boundRight - boundLeft; + bound.height = boundBottom - boundTop; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/RegionClassifier.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/RegionClassifier.java new file mode 100644 index 00000000000..9faa04ea9eb --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/RegionClassifier.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class RegionClassifier { + private List regionList; + + public RegionClassifier() { + regionList = new ArrayList(); + } + + public void add(Rectangle rect) { + boolean newRegion = true; + Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2); + for(Region region : regionList) { + if(region.getBound().intersects(rcInflated)) { + newRegion = false; + break; + } + } + + if(newRegion) { + regionList.add(new Region(rect)); + } else { + for(Region region : regionList) { + if(region.add(rect)) + return; + } + regionList.add(new Region(rect)); + } + } + + public List getRegionList() { + return regionList; + } + + public void clear() { + regionList.clear(); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/TileInfo.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/TileInfo.java new file mode 100644 index 00000000000..933a55a55b0 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/TileInfo.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; + +public class TileInfo { + private int row; + private int col; + private Rectangle tileRect; + + public TileInfo(int row, int col, Rectangle tileRect) { + this.row = row; + this.col = col; + this.tileRect = tileRect; + } + + public int getRow() { + return row; + } + + public void setRow(int row) { + this.row = row; + } + + public int getCol() { + return col; + } + + public void setCol(int col) { + this.col = col; + } + + public Rectangle getTileRect() { + return tileRect; + } + + public void setTileRect(Rectangle tileRect) { + this.tileRect = tileRect; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/util/TileTracker.java b/services/console-proxy/server/src/com/cloud/consoleproxy/util/TileTracker.java new file mode 100644 index 00000000000..b3facb2f88f --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/util/TileTracker.java @@ -0,0 +1,269 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.util; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class TileTracker { + + // 2 dimension tile status snapshot, a true value means the corresponding tile has been invalidated + private boolean[][] snapshot; + + private int tileWidth = 0; + private int tileHeight = 0; + private int trackWidth = 0; + private int trackHeight = 0; + + public TileTracker() { + } + + public int getTileWidth() { + return tileWidth; + } + + public void setTileWidth(int tileWidth) { + this.tileWidth = tileWidth; + } + + public int getTileHeight() { + return tileHeight; + } + + public void setTileHeight(int tileHeight) { + this.tileHeight = tileHeight; + } + + public int getTrackWidth() { + return trackWidth; + } + + public void setTrackWidth(int trackWidth) { + this.trackWidth = trackWidth; + } + + public int getTrackHeight() { + return trackHeight; + } + + public void setTrackHeight(int trackHeight) { + this.trackHeight = trackHeight; + } + + public void initTracking(int tileWidth, int tileHeight, int trackWidth, int trackHeight) { + assert(tileWidth > 0); + assert(tileHeight > 0); + assert(trackWidth > 0); + assert(trackHeight > 0); + assert(tileWidth <= trackWidth); + assert(tileHeight <= trackHeight); + + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.trackWidth = trackWidth; + this.trackHeight = trackHeight; + + int cols = getTileCols(); + int rows = getTileRows(); + snapshot = new boolean[rows][cols]; + for(int i = 0; i < rows; i++) + for(int j = 0; j < cols; j++) + snapshot[i][j] = false; + } + + public synchronized void resize(int trackWidth, int trackHeight) { + assert(tileWidth > 0); + assert(tileHeight > 0); + assert(trackWidth > 0); + assert(trackHeight > 0); + + this.trackWidth = trackWidth; + this.trackHeight = trackHeight; + + int cols = getTileCols(); + int rows = getTileRows(); + snapshot = new boolean[rows][cols]; + for(int i = 0; i < rows; i++) + for(int j = 0; j < cols; j++) + snapshot[i][j] = true; + } + + public void invalidate(Rectangle rect) { + setTileFlag(rect, true); + } + + public void validate(Rectangle rect) { + setTileFlag(rect, false); + } + + public List scan(boolean init) { + List l = new ArrayList(); + + synchronized(this) { + for(int i = 0; i < getTileRows(); i++) { + for(int j = 0; j < getTileCols(); j++) { + if(init || snapshot[i][j]) { + Rectangle rect = new Rectangle(); + rect.y = i*tileHeight; + rect.x = j*tileWidth; + rect.width = Math.min(trackWidth - rect.x, tileWidth); + rect.height = Math.min(trackHeight - rect.y, tileHeight); + + l.add(new TileInfo(i, j, rect)); + snapshot[i][j] = false; + } + } + } + + return l; + } + } + + public boolean hasFullCoverage() { + synchronized(this) { + for(int i = 0; i < getTileRows(); i++) { + for(int j = 0; j < getTileCols(); j++) { + if(!snapshot[i][j]) + return false; + } + } + } + return true; + } + + + + public void initCoverageTest() { + synchronized(this) { + for(int i = 0; i < getTileRows(); i++) { + for(int j = 0; j < getTileCols(); j++) { + snapshot[i][j] = false; + } + } + } + } + + // listener will be called while holding the object lock, use it + // with care to avoid deadlock condition being formed + public synchronized void scan(int nStartRow, int nStartCol, ITileScanListener listener) { + assert(listener != null); + + int cols = getTileCols(); + int rows = getTileRows(); + + nStartRow = nStartRow % rows; + nStartCol = nStartCol % cols; + + int nPos = nStartRow*cols + nStartCol; + int nUnits = rows*cols; + int nStartPos = nPos; + int nRow; + int nCol; + do { + nRow = nPos / cols; + nCol = nPos % cols; + + if(snapshot[nRow][nCol]) { + int nEndCol = nCol; + for(; nEndCol < cols && snapshot[nRow][nEndCol]; nEndCol++) { + snapshot[nRow][nEndCol] = false; + } + + Rectangle rect = new Rectangle(); + rect.y = nRow*tileHeight; + rect.height = tileHeight; + rect.x = nCol*tileWidth; + rect.width = (nEndCol - nCol)*tileWidth; + + if(!listener.onTileChange(rect, nRow, nEndCol)) + break; + } + + nPos = (nPos + 1) % nUnits; + } while(nPos != nStartPos); + } + + public void capture(ITileScanListener listener) { + assert(listener != null); + + int cols = getTileCols(); + int rows = getTileRows(); + + RegionClassifier classifier = new RegionClassifier(); + int left, top, right, bottom; + + synchronized(this) { + for(int i = 0; i < rows; i++) { + top = i*tileHeight; + bottom = Math.min(top + tileHeight, trackHeight); + for(int j = 0; j < cols; j++) { + left = j*tileWidth; + right = Math.min(left + tileWidth, trackWidth); + + if(snapshot[i][j]) { + snapshot[i][j] = false; + classifier.add(new Rectangle(left, top, right - left, bottom - top)); + } + } + } + } + listener.onRegionChange(classifier.getRegionList()); + } + + private synchronized void setTileFlag(Rectangle rect, boolean flag) { + int nStartTileRow; + int nStartTileCol; + int nEndTileRow; + int nEndTileCol; + + int cols = getTileCols(); + int rows = getTileRows(); + + if(rect != null) { + nStartTileRow = Math.min(getTileYPos(rect.y), rows - 1); + nStartTileCol = Math.min(getTileXPos(rect.x), cols - 1); + nEndTileRow = Math.min(getTileYPos(rect.y + rect.height - 1), rows -1); + nEndTileCol = Math.min(getTileXPos(rect.x + rect.width - 1), cols -1); + } else { + nStartTileRow = 0; + nStartTileCol = 0; + nEndTileRow = rows - 1; + nEndTileCol = cols - 1; + } + + for(int i = nStartTileRow; i <= nEndTileRow; i++) + for(int j = nStartTileCol; j <= nEndTileCol; j++) + snapshot[i][j] = flag; + } + + private int getTileRows() { + return (trackHeight + tileHeight - 1) / tileHeight; + } + + private int getTileCols() { + return (trackWidth + tileWidth - 1) / tileWidth; + } + + private int getTileXPos(int x) { + return x / tileWidth; + } + + public int getTileYPos(int y) { + return y / tileHeight; + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java new file mode 100644 index 00000000000..f2fb4bb6b69 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java @@ -0,0 +1,150 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.List; + +import com.cloud.consoleproxy.util.ImageHelper; +import com.cloud.consoleproxy.util.TileInfo; + +/** + * A BuffereImageCanvas component represents frame buffer image on + * the screen. It also notifies its subscribers when screen is repainted. + */ +public class BufferedImageCanvas extends Canvas implements FrameBufferCanvas { + private static final long serialVersionUID = 1L; + + // Offline screen buffer + private BufferedImage offlineImage; + + // Cached Graphics2D object for offline screen buffer + private Graphics2D graphics; + + private PaintNotificationListener listener; + + public BufferedImageCanvas(PaintNotificationListener listener, int width, int height) { + super(); + this.listener = listener; + + setBackground(Color.black); + + setFocusable(true); + + // Don't intercept TAB key + setFocusTraversalKeysEnabled(false); + + setCanvasSize(width, height); + } + + public void setCanvasSize(int width, int height) { + this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + graphics = offlineImage.createGraphics(); + + setSize(offlineImage.getWidth(), offlineImage.getHeight()); + } + + @Override + public void update(Graphics g) { + // Call paint() directly, without clearing screen first + paint(g); + } + + @Override + public void paint(Graphics g) { + // Only part of image, requested with repaint(Rectangle), will be + // painted on screen. + synchronized (offlineImage) { + g.drawImage(offlineImage, 0, 0, this); + } + // Notify server that update is painted on screen + listener.imagePaintedOnScreen(); + } + + public BufferedImage getOfflineImage() { + return offlineImage; + } + + public Graphics2D getOfflineGraphics() { + return graphics; + } + + public void copyTile(Graphics2D g, int x, int y, Rectangle rc) { + synchronized (offlineImage) { + g.drawImage(offlineImage, x, y, x + rc.width, y + rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); + } + } + + @Override + public Image getFrameBufferScaledImage(int width, int height) { + if (offlineImage != null) + return offlineImage.getScaledInstance(width, height, Image.SCALE_DEFAULT); + return null; + } + + @Override + public byte[] getFrameBufferJpeg() { + int width = 800; + int height = 600; + + width = offlineImage.getWidth(); + height = offlineImage.getHeight(); + + BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = bufferedImage.createGraphics(); + synchronized (offlineImage) { + g.drawImage(offlineImage, 0, 0, width, height, 0, 0, width, height, null); + } + + byte[] imgBits = null; + try { + imgBits = ImageHelper.jpegFromImage(bufferedImage); + } catch (IOException e) { + } + return imgBits; + } + + @Override + public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight) { + int width = Math.max(tileWidth, tileWidth * tileList.size()); + BufferedImage bufferedImage = new BufferedImage(width, tileHeight, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = bufferedImage.createGraphics(); + + synchronized (offlineImage) { + int i = 0; + for (TileInfo tile : tileList) { + Rectangle rc = tile.getTileRect(); + g.drawImage(offlineImage, i * tileWidth, 0, i * tileWidth + rc.width, rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null); + i++; + } + } + + byte[] imgBits = null; + try { + imgBits = ImageHelper.jpegFromImage(bufferedImage); + } catch (IOException e) { + } + return imgBits; + } +} \ No newline at end of file diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java new file mode 100644 index 00000000000..dcf11461360 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferCanvas.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +import java.awt.Image; +import java.util.List; + +import com.cloud.consoleproxy.util.TileInfo; + +public interface FrameBufferCanvas { + Image getFrameBufferScaledImage(int width, int height); + + public byte[] getFrameBufferJpeg(); + + public byte[] getTilesMergedJpeg(List tileList, int tileWidth, int tileHeight); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java new file mode 100644 index 00000000000..b8527c5261a --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/FrameBufferUpdateListener.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +public interface FrameBufferUpdateListener { + + /** + * Notify listener, that frame buffer update packet is received, so client + * is permitted (but not obligated) to ask server to send another update. + */ + void frameBufferPacketReceived(); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java new file mode 100644 index 00000000000..aaefacb99f2 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/PaintNotificationListener.java @@ -0,0 +1,27 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +public interface PaintNotificationListener { + + /** + * Notify subscriber that screen is updated, so client can send another + * frame buffer update request to server. + */ + void imagePaintedOnScreen(); + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/RfbConstants.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/RfbConstants.java new file mode 100644 index 00000000000..18bf47ec1d4 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/RfbConstants.java @@ -0,0 +1,82 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +import java.nio.charset.Charset; + +public interface RfbConstants { + + public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003."; + // public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; + public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; + public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR; + + /** + * Server message types. + */ + final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3; + + /** + * Client message types. + */ + public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3, CLIENT_KEYBOARD_EVENT = 4, + CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6; + + /** + * Server authorization type + */ + public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2; + + /** + * Server authorization reply. + */ + public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2; + + /** + * Encodings. + */ + public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16; + + /** + * Pseudo-encodings. + */ + public final static int ENCODING_CURSOR = -239 /* 0xFFFFFF11 */, ENCODING_DESKTOP_SIZE = -223 /* 0xFFFFFF21 */; + + /** + * Encodings, which we support. + */ + public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE }; + + /** + * Frame buffer update request type: update of whole screen or partial + * update. + */ + public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1; + + public static final int KEY_UP = 0, KEY_DOWN = 1; + + public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1; + + public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1; + + public static final int PALETTE = 0, TRUE_COLOR = 1; + + /** + * Default charset to use when communicating with server. + */ + public static final Charset CHARSET = Charset.availableCharsets().get("US-ASCII"); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClient.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClient.java new file mode 100644 index 00000000000..194eec17414 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClient.java @@ -0,0 +1,451 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +import java.awt.Frame; +import java.awt.ScrollPane; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.spec.KeySpec; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; + +import com.cloud.consoleproxy.ConsoleProxyClientListener; +import com.cloud.consoleproxy.util.Logger; +import com.cloud.consoleproxy.util.RawHTTP; +import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; +import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; + +public class VncClient { + private static final Logger s_logger = Logger.getLogger(VncClient.class); + + private Socket socket; + private DataInputStream is; + private DataOutputStream os; + + private VncScreenDescription screen = new VncScreenDescription(); + + private VncClientPacketSender sender; + private VncServerPacketReceiver receiver; + + private boolean noUI = false; + private ConsoleProxyClientListener clientListener = null; + + public static void main(String args[]) { + if (args.length < 3) { + printHelpMessage(); + System.exit(1); + } + + String host = args[0]; + String port = args[1]; + String password = args[2]; + + try { + new VncClient(host, Integer.parseInt(port), password, false, null); + } catch (NumberFormatException e) { + s_logger.error("Incorrect VNC server port number: " + port + "."); + System.exit(1); + } catch (UnknownHostException e) { + s_logger.error("Incorrect VNC server host name: " + host + "."); + System.exit(1); + } catch (IOException e) { + s_logger.error("Cannot communicate with VNC server: " + e.getMessage()); + System.exit(1); + } catch (Throwable e) { + s_logger.error("An error happened: " + e.getMessage()); + System.exit(1); + } + System.exit(0); + } + + private static void printHelpMessage() { + /* LOG */s_logger.info("Usage: HOST PORT PASSWORD."); + } + + public VncClient(ConsoleProxyClientListener clientListener) { + this.noUI = true; + this.clientListener = clientListener; + } + + public VncClient(String host, int port, String password, boolean noUI, ConsoleProxyClientListener clientListener) throws UnknownHostException, IOException { + + this.noUI = noUI; + this.clientListener = clientListener; + connectTo(host, port, password); + } + + public void shutdown() { + if (sender != null) + sender.closeConnection(); + + if (receiver != null) + receiver.closeConnection(); + + if (is != null) { + try { + is.close(); + } catch (Throwable e) { + } + } + + if (os != null) { + try { + os.close(); + } catch (Throwable e) { + } + } + + if (socket != null) { + try { + socket.close(); + } catch (Throwable e) { + } + } + } + + public ConsoleProxyClientListener getClientListener() { + return clientListener; + } + + public void connectTo(String host, int port, String path, String session, boolean useSSL, String sid) throws UnknownHostException, IOException { + if (port < 0) { + if (useSSL) + port = 443; + else + port = 80; + } + + RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL); + this.socket = tunnel.connect(); + doConnect(sid); + } + + public void connectTo(String host, int port, String password) throws UnknownHostException, IOException { + // Connect to server + s_logger.info("Connecting to VNC server " + host + ":" + port + "..."); + this.socket = new Socket(host, port); + doConnect(password); + } + + private void doConnect(String password) throws IOException { + is = new DataInputStream(socket.getInputStream()); + os = new DataOutputStream(socket.getOutputStream()); + + // Initialize connection + handshake(); + authenticate(password); + initialize(); + + s_logger.info("Connecting to VNC server succeeded, start session"); + + // Run client-to-server packet sender + sender = new VncClientPacketSender(os, screen, this); + + // Create buffered image canvas + BufferedImageCanvas canvas = new BufferedImageCanvas(sender, screen.getFramebufferWidth(), screen.getFramebufferHeight()); + + // Subscribe packet sender to various events + canvas.addMouseListener(sender); + canvas.addMouseMotionListener(sender); + canvas.addKeyListener(sender); + + Frame frame = null; + if (!noUI) + frame = createVncClientMainWindow(canvas, screen.getDesktopName()); + + new Thread(sender).start(); + + // Run server-to-client packet receiver + receiver = new VncServerPacketReceiver(is, canvas, screen, this, sender, clientListener); + try { + receiver.run(); + } finally { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + this.shutdown(); + } + } + + private Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title) { + // Create AWT windows + final Frame frame = new Frame(title + " - VNCle"); + + // Use scrolling pane to support screens, which are larger than ours + ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + scroller.add(canvas); + scroller.setSize(screen.getFramebufferWidth(), screen.getFramebufferHeight()); + + frame.add(scroller); + frame.pack(); + frame.setVisible(true); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + frame.setVisible(false); + shutdown(); + } + }); + + return frame; + } + + /** + * Handshake with VNC server. + */ + private void handshake() throws IOException { + + // Read protocol version + byte[] buf = new byte[12]; + is.readFully(buf); + String rfbProtocol = new String(buf); + + // Server should use RFB protocol 3.x + if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) { + s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); + throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); + } + + // Send response: we support RFB 3.3 only + String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n"; + os.write(ourProtocolString.getBytes()); + os.flush(); + } + + /** + * VNC authentication. + */ + private void authenticate(String password) throws IOException { + // Read security type + int authType = is.readInt(); + + switch (authType) { + case RfbConstants.CONNECTION_FAILED: { + // Server forbids to connect. Read reason and throw exception + + int length = is.readInt(); + byte[] buf = new byte[length]; + is.readFully(buf); + String reason = new String(buf, RfbConstants.CHARSET); + + s_logger.error("Authentication to VNC server is failed. Reason: " + reason); + throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason); + } + + case RfbConstants.NO_AUTH: { + // Client can connect without authorization. Nothing to do. + break; + } + + case RfbConstants.VNC_AUTH: { + s_logger.info("VNC server requires password authentication"); + doVncAuth(password); + break; + } + + default: + s_logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); + throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); + } + } + + /** + * Encode client password and send it to server. + */ + private void doVncAuth(String password) throws IOException { + + // Read challenge + byte[] challenge = new byte[16]; + is.readFully(challenge); + + // Encode challenge with password + byte[] response; + try { + response = encodePassword(challenge, password); + } catch (Exception e) { + s_logger.error("Cannot encrypt client password to send to server: " + e.getMessage()); + throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage()); + } + + // Send encoded challenge + os.write(response); + os.flush(); + + // Read security result + int authResult = is.readInt(); + + switch (authResult) { + case RfbConstants.VNC_AUTH_OK: { + // Nothing to do + break; + } + + case RfbConstants.VNC_AUTH_TOO_MANY: + s_logger.error("Connection to VNC server failed: too many wrong attempts."); + throw new RuntimeException("Connection to VNC server failed: too many wrong attempts."); + + case RfbConstants.VNC_AUTH_FAILED: + s_logger.error("Connection to VNC server failed: wrong password."); + throw new RuntimeException("Connection to VNC server failed: wrong password."); + + default: + s_logger.error("Connection to VNC server failed, reason code: " + authResult); + throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult); + } + } + + /** + * Encode password using DES encryption with given challenge. + * + * @param challenge + * a random set of bytes. + * @param password + * a password + * @return DES hash of password and challenge + */ + public byte[] encodePassword(byte[] challenge, String password) throws Exception { + // VNC password consist of up to eight ASCII characters. + byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding + byte[] passwordAsciiBytes = password.getBytes(RfbConstants.CHARSET); + System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8)); + + // Flip bytes (reverse bits) in key + for (int i = 0; i < key.length; i++) { + key[i] = flipByte(key[i]); + } + + KeySpec desKeySpec = new DESKeySpec(key); + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec); + Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + byte[] response = cipher.doFinal(challenge); + return response; + } + + /** + * Reverse bits in byte, so least significant bit will be most significant + * bit. E.g. 01001100 will become 00110010. + * + * See also: http://www.vidarholen.net/contents/junk/vnc.html , + * http://bytecrafter + * .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html + * + * @param b + * a byte + * @return byte in reverse order + */ + private static byte flipByte(byte b) { + int b1_8 = (b & 0x1) << 7; + int b2_7 = (b & 0x2) << 5; + int b3_6 = (b & 0x4) << 3; + int b4_5 = (b & 0x8) << 1; + int b5_4 = (b & 0x10) >>> 1; + int b6_3 = (b & 0x20) >>> 3; + int b7_2 = (b & 0x40) >>> 5; + int b8_1 = (b & 0x80) >>> 7; + byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1); + return c; + } + + private void initialize() throws IOException { + // Send client initialization message + { + // Send shared flag + os.writeByte(RfbConstants.EXCLUSIVE_ACCESS); + os.flush(); + } + + // Read server initialization message + { + // Read frame buffer size + int framebufferWidth = is.readUnsignedShort(); + int framebufferHeight = is.readUnsignedShort(); + screen.setFramebufferSize(framebufferWidth, framebufferHeight); + if (clientListener != null) + clientListener.onFramebufferSizeChange(framebufferWidth, framebufferHeight); + } + + // Read pixel format + { + int bitsPerPixel = is.readUnsignedByte(); + int depth = is.readUnsignedByte(); + + int bigEndianFlag = is.readUnsignedByte(); + int trueColorFlag = is.readUnsignedByte(); + + int redMax = is.readUnsignedShort(); + int greenMax = is.readUnsignedShort(); + int blueMax = is.readUnsignedShort(); + + int redShift = is.readUnsignedByte(); + int greenShift = is.readUnsignedByte(); + int blueShift = is.readUnsignedByte(); + + // Skip padding + is.skipBytes(3); + + screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColorFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift); + } + + // Read desktop name + { + int length = is.readInt(); + byte buf[] = new byte[length]; + is.readFully(buf); + String desktopName = new String(buf, RfbConstants.CHARSET); + screen.setDesktopName(desktopName); + } + } + + public FrameBufferCanvas getFrameBufferCanvas() { + if (receiver != null) + return receiver.getCanvas(); + + return null; + } + + public void requestUpdate(boolean fullUpdate) { + if (fullUpdate) + sender.requestFullScreenUpdate(); + else + sender.imagePaintedOnScreen(); + } + + public void sendClientKeyboardEvent(int event, int code, int modifiers) { + sender.sendClientPacket(new KeyboardEventPacket(event, code)); + } + + public void sendClientMouseEvent(int event, int x, int y, int code, int modifiers) { + sender.sendClientPacket(new MouseEventPacket(event, x, y)); + } + + public boolean isHostConnected() { + return receiver != null && receiver.isConnectionAlive(); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java new file mode 100644 index 00000000000..d27b76d3468 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncClientPacketSender.java @@ -0,0 +1,258 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.DataOutputStream; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import com.cloud.consoleproxy.util.Logger; +import com.cloud.consoleproxy.vnc.packet.client.ClientPacket; +import com.cloud.consoleproxy.vnc.packet.client.FramebufferUpdateRequestPacket; +import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; +import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; +import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket; +import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket; + +public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener { + private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class); + + // Queue for outgoing packets + private final BlockingQueue queue = new ArrayBlockingQueue(30); + + private final DataOutputStream os; + private final VncScreenDescription screen; + private final VncClient vncConnection; + + private boolean connectionAlive = true; + + // Don't send update request again until we receive next frame buffer update + private boolean updateRequestSent = false; + + public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) { + this.os = os; + this.screen = screen; + this.vncConnection = vncConnection; + + sendSetPixelFormat(); + sendSetEncodings(); + requestFullScreenUpdate(); + } + + public void sendClientPacket(ClientPacket packet) { + queue.add(packet); + } + + @Override + public void run() { + try { + while (connectionAlive) { + ClientPacket packet = queue.poll(1, TimeUnit.SECONDS); + if (packet != null) { + packet.write(os); + os.flush(); + } + } + } catch (Throwable e) { + s_logger.error("Unexpected exception: ", e); + if (connectionAlive) { + closeConnection(); + vncConnection.shutdown(); + } + } + } + + private void sendSetEncodings() { + queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY)); + } + + private void sendSetPixelFormat() { + if (!screen.isRGB888_32_LE()) { + queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0)); + } + } + + public void closeConnection() { + connectionAlive = false; + } + + public void requestFullScreenUpdate() { + queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight())); + updateRequestSent = true; + } + + @Override + public void imagePaintedOnScreen() { + if (!updateRequestSent) { + queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight())); + updateRequestSent = true; + } + } + + @Override + public void frameBufferPacketReceived() { + updateRequestSent = false; + } + + @Override + public void mouseDragged(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseMoved(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseClicked(MouseEvent e) { + // Nothing to do + } + + @Override + public void mousePressed(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseReleased(MouseEvent e) { + queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); + } + + @Override + public void mouseEntered(MouseEvent e) { + // Nothing to do + } + + @Override + public void mouseExited(MouseEvent e) { + // Nothing to do + } + + /** + * Current state of buttons 1 to 8 are represented by bits 0 to 7 of + * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a + * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and + * right buttons on the mouse. On a wheel mouse, each step of the wheel + * upwards is represented by a press and release of button 4, and each step + * downwards is represented by a press and release of button 5. + * + * @param modifiers + * extended modifiers from AWT mouse event + * @return VNC mouse button mask + */ + public static int mapAwtModifiersToVncButtonMask(int modifiers) { + int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) + | (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0); + return mask; + } + + @Override + public void keyTyped(KeyEvent e) { + // Do nothing + } + + @Override + public void keyPressed(KeyEvent e) { + ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode())); + queue.add(request); + } + + @Override + public void keyReleased(KeyEvent e) { + ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode())); + queue.add(request); + } + + private int mapAwtKeyToVncKey(int key) { + switch (key) { + case KeyEvent.VK_BACK_SPACE: + return 0xff08; + case KeyEvent.VK_TAB: + return 0xff09; + case KeyEvent.VK_ENTER: + return 0xff0d; + case KeyEvent.VK_ESCAPE: + return 0xff1b; + case KeyEvent.VK_INSERT: + return 0xff63; + case KeyEvent.VK_DELETE: + return 0xffff; + case KeyEvent.VK_HOME: + return 0xff50; + case KeyEvent.VK_END: + return 0xff57; + case KeyEvent.VK_PAGE_UP: + return 0xff55; + case KeyEvent.VK_PAGE_DOWN: + return 0xff56; + case KeyEvent.VK_LEFT: + return 0xff51; + case KeyEvent.VK_UP: + return 0xff52; + case KeyEvent.VK_RIGHT: + return 0xff53; + case KeyEvent.VK_DOWN: + return 0xff54; + case KeyEvent.VK_F1: + return 0xffbe; + case KeyEvent.VK_F2: + return 0xffbf; + case KeyEvent.VK_F3: + return 0xffc0; + case KeyEvent.VK_F4: + return 0xffc1; + case KeyEvent.VK_F5: + return 0xffc2; + case KeyEvent.VK_F6: + return 0xffc3; + case KeyEvent.VK_F7: + return 0xffc4; + case KeyEvent.VK_F8: + return 0xffc5; + case KeyEvent.VK_F9: + return 0xffc6; + case KeyEvent.VK_F10: + return 0xffc7; + case KeyEvent.VK_F11: + return 0xffc8; + case KeyEvent.VK_F12: + return 0xffc9; + case KeyEvent.VK_SHIFT: + return 0xffe1; + case KeyEvent.VK_CONTROL: + return 0xffe3; + case KeyEvent.VK_META: + return 0xffe7; + case KeyEvent.VK_ALT: + return 0xffe9; + case KeyEvent.VK_ALT_GRAPH: + return 0xffea; + case KeyEvent.VK_BACK_QUOTE: + return 0x0060; + } + + return key; + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java new file mode 100644 index 00000000000..44b2a34769b --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncScreenDescription.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +/** + * VncScreenDescription - contains information about remote VNC screen. + */ +public class VncScreenDescription { + + // Frame buffer size + private int framebufferWidth = -1; + private int framebufferHeight = -1; + + // Desktop name + private String desktopName; + + // Bytes per pixel + private int bytesPerPixel; + + // Indicates that screen uses format which we want to use: + // RGB 24bit packed into 32bit little-endian int. + private boolean rgb888_32_le = false; + + public VncScreenDescription() { + } + + /** + * Store information about server pixel format. + */ + public void setPixelFormat(int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift, int blueShift) { + + bytesPerPixel = (bitsPerPixel + 7) / 8; + + rgb888_32_le = (depth == 24 && bitsPerPixel == 32 && redShift == 16 && greenShift == 8 && blueShift == 0 && redMax == 255 && greenMax == 255 && blueMax == 255 + && bigEndianFlag == RfbConstants.LITTLE_ENDIAN && trueColorFlag == RfbConstants.TRUE_COLOR); + } + + /** + * Store information about server screen size. + */ + public void setFramebufferSize(int framebufferWidth, int framebufferHeight) { + this.framebufferWidth = framebufferWidth; + this.framebufferHeight = framebufferHeight; + } + + /** + * Store server desktop name. + */ + public void setDesktopName(String desktopName) { + this.desktopName = desktopName; + } + + // Getters for variables, as usual + + public String getDesktopName() { + return desktopName; + } + + public int getBytesPerPixel() { + return bytesPerPixel; + } + + public int getFramebufferHeight() { + return framebufferHeight; + } + + public int getFramebufferWidth() { + return framebufferWidth; + } + + public boolean isRGB888_32_LE() { + return rgb888_32_le; + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java new file mode 100644 index 00000000000..57c8ff8efe2 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc; + +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; +import java.io.DataInputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.ConsoleProxyClientListener; +import com.cloud.consoleproxy.util.Logger; +import com.cloud.consoleproxy.vnc.packet.server.FramebufferUpdatePacket; +import com.cloud.consoleproxy.vnc.packet.server.ServerCutText; + +public class VncServerPacketReceiver implements Runnable { + private static final Logger s_logger = Logger.getLogger(VncServerPacketReceiver.class); + + private final VncScreenDescription screen; + private BufferedImageCanvas canvas; + private DataInputStream is; + + private boolean connectionAlive = true; + private VncClient vncConnection; + private final FrameBufferUpdateListener fburListener; + private final ConsoleProxyClientListener clientListener; + + public VncServerPacketReceiver(DataInputStream is, BufferedImageCanvas canvas, VncScreenDescription screen, VncClient vncConnection, FrameBufferUpdateListener fburListener, + ConsoleProxyClientListener clientListener) { + this.screen = screen; + this.canvas = canvas; + this.is = is; + this.vncConnection = vncConnection; + this.fburListener = fburListener; + this.clientListener = clientListener; + } + + public BufferedImageCanvas getCanvas() { + return canvas; + } + + @Override + public void run() { + try { + while (connectionAlive) { + + // Read server message type + int messageType = is.readUnsignedByte(); + + // Invoke packet handler by packet type. + switch (messageType) { + + case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: { + // Notify sender that frame buffer update is received, + // so it can send another frame buffer update request + fburListener.frameBufferPacketReceived(); + // Handle frame buffer update + new FramebufferUpdatePacket(canvas, screen, is, clientListener); + break; + } + + case RfbConstants.SERVER_BELL: { + serverBell(); + break; + } + + case RfbConstants.SERVER_CUT_TEXT: { + serverCutText(is); + break; + } + + default: + throw new RuntimeException("Unknown server packet type: " + messageType + "."); + } + } + } catch (Throwable e) { + s_logger.error("Unexpected exception: ", e); + if (connectionAlive) { + closeConnection(); + vncConnection.shutdown(); + } + } + } + + public void closeConnection() { + connectionAlive = false; + } + + public boolean isConnectionAlive() { + return connectionAlive; + } + + /** + * Handle server bell packet. + */ + private void serverBell() { + Toolkit.getDefaultToolkit().beep(); + } + + /** + * Handle packet with server clip-board. + */ + private void serverCutText(DataInputStream is) throws IOException { + ServerCutText clipboardContent = new ServerCutText(is); + StringSelection contents = new StringSelection(clipboardContent.getContent()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null); + + s_logger.info("Server clipboard buffer: " + clipboardContent.getContent()); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java new file mode 100644 index 00000000000..873b8c0825e --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/ClientPacket.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.client; + +import java.io.DataOutputStream; +import java.io.IOException; + +public interface ClientPacket { + + void write(DataOutputStream os) throws IOException; + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java new file mode 100644 index 00000000000..d3a6e40e961 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/FramebufferUpdateRequestPacket.java @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.client; + +import java.io.DataOutputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.vnc.RfbConstants; + +/** + * FramebufferUpdateRequestPacket + * + * @author Volodymyr M. Lisivka + */ +public class FramebufferUpdateRequestPacket implements ClientPacket { + + private final int incremental; + private final int x, y, width, height; + + public FramebufferUpdateRequestPacket(int incremental, int x, int y, int width, int height) { + this.incremental = incremental; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST); + + os.writeByte(incremental); + os.writeShort(x); + os.writeShort(y); + os.writeShort(width); + os.writeShort(height); + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java new file mode 100644 index 00000000000..8efbab123a5 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/KeyboardEventPacket.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.client; + +import java.io.DataOutputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.vnc.RfbConstants; + +public class KeyboardEventPacket implements ClientPacket { + + private final int downFlag, key; + + public KeyboardEventPacket(int downFlag, int key) { + this.downFlag = downFlag; + this.key = key; + } + + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT); + + os.writeByte(downFlag); + os.writeShort(0); // padding + os.writeInt(key); + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java new file mode 100644 index 00000000000..b42191cbadc --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/MouseEventPacket.java @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.client; + +import java.io.DataOutputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.vnc.RfbConstants; + +public class MouseEventPacket implements ClientPacket { + + private final int buttonMask, x, y; + + public MouseEventPacket(int buttonMask, int x, int y) { + this.buttonMask = buttonMask; + this.x = x; + this.y = y; + } + + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_POINTER_EVENT); + + os.writeByte(buttonMask); + os.writeShort(x); + os.writeShort(y); + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java new file mode 100644 index 00000000000..3d8cfcb09a6 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetEncodingsPacket.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.client; + +import java.io.DataOutputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.vnc.RfbConstants; + +public class SetEncodingsPacket implements ClientPacket { + + private final int[] encodings; + + public SetEncodingsPacket(int[] encodings) { + this.encodings = encodings; + } + + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_SET_ENCODINGS); + + os.writeByte(0);// padding + + os.writeShort(encodings.length); + + for (int i = 0; i < encodings.length; i++) { + os.writeInt(encodings[i]); + } + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java new file mode 100644 index 00000000000..7a25bfab9f3 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/client/SetPixelFormatPacket.java @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.client; + +import java.io.DataOutputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.vnc.RfbConstants; +import com.cloud.consoleproxy.vnc.VncScreenDescription; + +public class SetPixelFormatPacket implements ClientPacket { + + private final int bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift; + + private final VncScreenDescription screen; + + public SetPixelFormatPacket(VncScreenDescription screen, int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift, + int blueShift) { + this.screen = screen; + this.bitsPerPixel = bitsPerPixel; + this.depth = depth; + this.bigEndianFlag = bigEndianFlag; + this.trueColourFlag = trueColorFlag; + this.redMax = redMax; + this.greenMax = greenMax; + this.blueMax = blueMax; + this.redShift = redShift; + this.greenShift = greenShift; + this.blueShift = blueShift; + } + + @Override + public void write(DataOutputStream os) throws IOException { + os.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT); + + // Padding + os.writeByte(0); + os.writeByte(0); + os.writeByte(0); + + // Send pixel format + os.writeByte(bitsPerPixel); + os.writeByte(depth); + os.writeByte(bigEndianFlag); + os.writeByte(trueColourFlag); + os.writeShort(redMax); + os.writeShort(greenMax); + os.writeShort(blueMax); + os.writeByte(redShift); + os.writeByte(greenShift); + os.writeByte(blueShift); + + // Padding + os.writeByte(0); + os.writeByte(0); + os.writeByte(0); + + screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift); + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java new file mode 100644 index 00000000000..a380e9cd62a --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +public abstract class AbstractRect implements Rect { + + protected final int x; + protected final int y; + protected final int width; + protected final int height; + + public AbstractRect(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + +} \ No newline at end of file diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java new file mode 100644 index 00000000000..caaecb58864 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/CopyRect.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.DataInputStream; +import java.io.IOException; + +public class CopyRect extends AbstractRect { + + private final int srcX, srcY; + + public CopyRect(int x, int y, int width, int height, DataInputStream is) throws IOException { + super(x, y, width, height); + + srcX = is.readUnsignedShort(); + srcY = is.readUnsignedShort(); + } + + @Override + public void paint(BufferedImage image, Graphics2D graphics) { + graphics.copyArea(srcX, srcY, width, height, x - srcX, y - srcY); + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java new file mode 100644 index 00000000000..18f6987eab4 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FrameBufferSizeChangeRequest.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import com.cloud.consoleproxy.vnc.BufferedImageCanvas; + +public class FrameBufferSizeChangeRequest extends AbstractRect { + + private final BufferedImageCanvas canvas; + + public FrameBufferSizeChangeRequest(BufferedImageCanvas canvas, int width, int height) { + super(0, 0, width, height); + this.canvas = canvas; + canvas.setCanvasSize(width, height); + } + + @Override + public void paint(BufferedImage offlineImage, Graphics2D graphics) { + canvas.setCanvasSize(width, height); + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java new file mode 100644 index 00000000000..3bc43fdded1 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/FramebufferUpdatePacket.java @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +import java.io.DataInputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.ConsoleProxyClientListener; +import com.cloud.consoleproxy.vnc.BufferedImageCanvas; +import com.cloud.consoleproxy.vnc.RfbConstants; +import com.cloud.consoleproxy.vnc.VncScreenDescription; +import com.cloud.consoleproxy.vnc.packet.server.CopyRect; +import com.cloud.consoleproxy.vnc.packet.server.RawRect; +import com.cloud.consoleproxy.vnc.packet.server.Rect; + +public class FramebufferUpdatePacket { + + private final VncScreenDescription screen; + private final BufferedImageCanvas canvas; + private final ConsoleProxyClientListener clientListener; + + public FramebufferUpdatePacket(BufferedImageCanvas canvas, VncScreenDescription screen, DataInputStream is, ConsoleProxyClientListener clientListener) throws IOException { + + this.screen = screen; + this.canvas = canvas; + this.clientListener = clientListener; + readPacketData(is); + } + + private void readPacketData(DataInputStream is) throws IOException { + is.skipBytes(1);// Skip padding + + // Read number of rectangles + int numberOfRectangles = is.readUnsignedShort(); + + // For all rectangles + for (int i = 0; i < numberOfRectangles; i++) { + + // Read coordinate of rectangle + int x = is.readUnsignedShort(); + int y = is.readUnsignedShort(); + int width = is.readUnsignedShort(); + int height = is.readUnsignedShort(); + + int encodingType = is.readInt(); + + // Process rectangle + Rect rect; + switch (encodingType) { + + case RfbConstants.ENCODING_RAW: { + rect = new RawRect(screen, x, y, width, height, is); + break; + } + + case RfbConstants.ENCODING_COPY_RECT: { + rect = new CopyRect(x, y, width, height, is); + break; + } + + case RfbConstants.ENCODING_DESKTOP_SIZE: { + rect = new FrameBufferSizeChangeRequest(canvas, width, height); + if (this.clientListener != null) + this.clientListener.onFramebufferSizeChange(width, height); + break; + } + + default: + throw new RuntimeException("Unsupported ecnoding: " + encodingType); + } + + paint(rect, canvas); + + if (this.clientListener != null) + this.clientListener.onFramebufferUpdate(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + + } + + public void paint(Rect rect, BufferedImageCanvas canvas) { + // Draw rectangle on offline buffer + rect.paint(canvas.getOfflineImage(), canvas.getOfflineGraphics()); + + // Request update of repainted area + canvas.repaint(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + } + +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java new file mode 100644 index 00000000000..978a4c2755d --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/RawRect.java @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.io.DataInputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.vnc.VncScreenDescription; + +public class RawRect extends AbstractRect { + private final int[] buf; + + public RawRect(VncScreenDescription screen, int x, int y, int width, int height, DataInputStream is) throws IOException { + super(x, y, width, height); + + byte[] bbuf = new byte[width * height * screen.getBytesPerPixel()]; + is.readFully(bbuf); + + // Convert array of bytes to array of int + int size = width * height; + buf = new int[size]; + for (int i = 0, j = 0; i < size; i++, j += 4) { + buf[i] = (bbuf[j + 0] & 0xFF) | ((bbuf[j + 1] & 0xFF) << 8) | ((bbuf[j + 2] & 0xFF) << 16) | ((bbuf[j + 3] & 0xFF) << 24); + } + + } + + @Override + public void paint(BufferedImage image, Graphics2D graphics) { + + DataBuffer dataBuf = image.getRaster().getDataBuffer(); + + switch (dataBuf.getDataType()) { + + case DataBuffer.TYPE_INT: { + // We chose RGB888 model, so Raster will use DataBufferInt type + DataBufferInt dataBuffer = (DataBufferInt) dataBuf; + + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + + // Paint rectangle directly on buffer, line by line + int[] imageBuffer = dataBuffer.getData(); + for (int srcLine = 0, dstLine = y; srcLine < height && dstLine < imageHeight; srcLine++, dstLine++) { + try { + System.arraycopy(buf, srcLine * width, imageBuffer, x + dstLine * imageWidth, width); + } catch (IndexOutOfBoundsException e) { + } + } + break; + } + + default: + throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: " + dataBuf.getClass().getSimpleName()); + } + } +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java new file mode 100644 index 00000000000..51edf12d0d6 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/Rect.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +public interface Rect { + + void paint(BufferedImage offlineImage, Graphics2D graphics); + + int getX(); + + int getY(); + + int getWidth(); + + int getHeight(); +} diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java new file mode 100644 index 00000000000..044f9589b23 --- /dev/null +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.consoleproxy.vnc.packet.server; + +import java.io.DataInputStream; +import java.io.IOException; + +import com.cloud.consoleproxy.util.Logger; +import com.cloud.consoleproxy.vnc.RfbConstants; + +public class ServerCutText { + private static final Logger s_logger = Logger.getLogger(ServerCutText.class); + + private String content; + + public String getContent() { + return content; + } + + public ServerCutText(DataInputStream is) throws IOException { + readPacketData(is); + } + + private void readPacketData(DataInputStream is) throws IOException { + is.skipBytes(3);// Skip padding + int length = is.readInt(); + byte buf[] = new byte[length]; + is.readFully(buf); + + content = new String(buf, RfbConstants.CHARSET); + + /* LOG */s_logger.info("Clippboard content: " + content); + } + +} diff --git a/services/console-proxy/server/systemvm-descriptor.xml b/services/console-proxy/server/systemvm-descriptor.xml new file mode 100644 index 00000000000..7efe7fdfcb0 --- /dev/null +++ b/services/console-proxy/server/systemvm-descriptor.xml @@ -0,0 +1,113 @@ + + + systemvm + + zip + + false + + + + + + + + ../scripts/storage/secondary/ + scripts/storage/secondary + 555 + 555 + + + ../scripts/storage/secondary/ + scripts/storage/secondary + 555 + 555 + + + scripts + + 555 + 555 + + + conf + conf + 555 + 555 + + log4j-cloud.xml + consoleproxy.properties + agent.properties + + + + ../console-proxy/images + images + 555 + 555 + + *.jpg + *.gif + *.png + *.cur + + + + ../console-proxy/js + js + 555 + 555 + + *.js + + + + ../console-proxy/ui + ui + 555 + 555 + + *.ftl + + + + ../console-proxy/css + css + 555 + 555 + + *.css + + + + ../console-proxy/certs + certs + 555 + 555 + + *.keystore + *.crt + *.key + + + + diff --git a/services/console-proxy/server/ui/viewer-bad-sid.ftl b/services/console-proxy/server/ui/viewer-bad-sid.ftl new file mode 100644 index 00000000000..2f30ec36015 --- /dev/null +++ b/services/console-proxy/server/ui/viewer-bad-sid.ftl @@ -0,0 +1,29 @@ + + + + + + +
+

Unable to start console session as access is denied because of bad sid

+
+ + + diff --git a/services/console-proxy/server/ui/viewer-connect-failed.ftl b/services/console-proxy/server/ui/viewer-connect-failed.ftl new file mode 100644 index 00000000000..9d907cacb43 --- /dev/null +++ b/services/console-proxy/server/ui/viewer-connect-failed.ftl @@ -0,0 +1,29 @@ + + + + + + +
+

Unable to start console session as connection is refused by the machine you are accessing

+
+ + + diff --git a/services/console-proxy/server/ui/viewer-update.ftl b/services/console-proxy/server/ui/viewer-update.ftl new file mode 100644 index 00000000000..6bf9ab35ce7 --- /dev/null +++ b/services/console-proxy/server/ui/viewer-update.ftl @@ -0,0 +1,24 @@ +<#-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> +tileMap = [ ${tileSequence} ]; +<#if resized == true> + ajaxViewer.resize('main_panel', ${width}, ${height}, ${tileWidth}, ${tileHeight}); + +ajaxViewer.refresh('${imgUrl}', tileMap, false); + diff --git a/services/console-proxy/server/ui/viewer.ftl b/services/console-proxy/server/ui/viewer.ftl new file mode 100644 index 00000000000..62de193cf7b --- /dev/null +++ b/services/console-proxy/server/ui/viewer.ftl @@ -0,0 +1,60 @@ + + + + + + + + +${title} + + + + +
+ + + + + diff --git a/services/console-proxy/server/vm-script/vmops b/services/console-proxy/server/vm-script/vmops new file mode 100644 index 00000000000..a9f70c83925 --- /dev/null +++ b/services/console-proxy/server/vm-script/vmops @@ -0,0 +1,119 @@ +#!/bin/bash +# +# vmops Script to start and stop the VMOps Agent. +# +# Author: Chiradeep Vittal +# chkconfig: 2345 99 01 +# description: Start up the VMOps agent + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +# Source function library. +if [ -f /etc/init.d/functions ] +then + . /etc/init.d/functions +fi + +_success() { + if [ -f /etc/init.d/functions ] + then + success + else + echo "Success" + fi +} + +_failure() { + if [ -f /etc/init.d/functions ] + then + failure + else + echo "Failed" + fi +} +RETVAL=$? +VMOPS_HOME="/usr/local/vmops" + +mkdir -p /var/log/vmops + +get_pids() { + local i + for i in $(ps -ef| grep java | grep -v grep | awk '{print $2}'); + do + echo $(pwdx $i) | grep "$VMOPS_HOME" | grep -i console | awk -F: '{print $1}'; + done +} + +start() { + local pid=$(get_pids) + echo -n "Starting VMOps Console Proxy: " + if [ -f $VMOPS_HOME/consoleproxy/run.sh ]; + then + if [ "$pid" == "" ] + then + (cd $VMOPS_HOME/consoleproxy; nohup ./run.sh > /var/log/vmops/vmops.out 2>&1 & ) + pid=$(get_pids) + echo $pid > /var/run/vmops.pid + fi + _success + else + _failure + fi + echo +} + +stop() { + local pid + echo -n "Stopping VMOps agent: " + for pid in $(get_pids) + do + kill $pid + done + _success + echo +} + +status() { + local pids=$(get_pids) + if [ "$pids" == "" ] + then + echo "VMOps agent is not running" + return 1 + fi + echo "VMOps agent is running: process id: $pids" + return 0 +} + + +case "$1" in + start) start + ;; + stop) stop + ;; + status) status + ;; + restart) stop + start + ;; + *) echo $"Usage: $0 {start|stop|status|restart}" + exit 1 + ;; +esac + +exit $RETVAL diff --git a/services/pom.xml b/services/pom.xml new file mode 100644 index 00000000000..26488513999 --- /dev/null +++ b/services/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + cloud-services + Apache CloudStack Cloud Services + pom + + org.apache.cloudstack + cloudstack + 4.1.0-SNAPSHOT + ../pom.xml + + + install + + + console-proxy + + From 06a733a89fe67ea700505c5efccbe16c1ed6d7a0 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Tue, 12 Feb 2013 15:50:58 -0800 Subject: [PATCH 23/42] CLOUDSTACK-618: cloudstack UI - API request throttling - for async job action, make frequency of calling queryAsyncJobResult API based on listCapabilities response. --- ui/scripts/cloudStack.js | 16 +++++++++++++++- ui/scripts/sharedFunctions.js | 1 + ui/scripts/ui/widgets/notifications.js | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js index fab6f7d839d..69813667c5c 100644 --- a/ui/scripts/cloudStack.js +++ b/ui/scripts/cloudStack.js @@ -157,6 +157,13 @@ g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion; + if(json.listcapabilitiesresponse.capability.apilimitinterval != null && json.listcapabilitiesresponse.capability.apilimitmax != null) { + var intervalLimit = ((json.listcapabilitiesresponse.capability.apilimitinterval * 1000) / json.listcapabilitiesresponse.capability.apilimitmax ) * 3; //multiply 3 to be on safe side + //intervalLimit = 9999; //this line is for testing only, comment it before check in + if(intervalLimit > g_queryAsyncJobResultInterval) + g_queryAsyncJobResultInterval = intervalLimit; + } + userValid = true; }, error: function(xmlHTTP) { @@ -283,7 +290,14 @@ $.cookie('userProjectsEnabled', g_userProjectsEnabled, { expires: 1 }); g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion; - + + if(json.listcapabilitiesresponse.capability.apilimitinterval != null && json.listcapabilitiesresponse.capability.apilimitmax != null) { + var intervalLimit = ((json.listcapabilitiesresponse.capability.apilimitinterval * 1000) / json.listcapabilitiesresponse.capability.apilimitmax ) * 3; //multiply 3 to be on safe side + //intervalLimit = 8888; //this line is for testing only, comment it before check in + if(intervalLimit > g_queryAsyncJobResultInterval) + g_queryAsyncJobResultInterval = intervalLimit; + } + args.response.success({ data: { user: $.extend(true, {}, loginresponse, { diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 51c4fdb5902..026089f6ea4 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -26,6 +26,7 @@ var g_timezone = null; var g_supportELB = null; var g_userPublicTemplateEnabled = "true"; var g_cloudstackversion = null; +var g_queryAsyncJobResultInterval = 6000; //keyboard keycode var keycode_Enter = 13; diff --git a/ui/scripts/ui/widgets/notifications.js b/ui/scripts/ui/widgets/notifications.js index e98d4d80fc1..02996031b82 100644 --- a/ui/scripts/ui/widgets/notifications.js +++ b/ui/scripts/ui/widgets/notifications.js @@ -93,7 +93,7 @@ $total.parent().addClass('pending'); $item.addClass('pending'); - // Setup timer + // Setup timer var pollTimer = setInterval(function() { args.poll({ _custom: _custom, @@ -281,7 +281,7 @@ cloudStack.ui.event.call('addNotification', { section: notification.section, desc: notification.desc, - interval: notification.interval ? notification.interval : 5000, + interval: notification.interval ? notification.interval : g_queryAsyncJobResultInterval, _custom: notification._custom, poll: function(args) { var complete = args.complete; From 2776ad14c8d5c090956867a68473ce28316e0286 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 12 Feb 2013 20:09:44 -0800 Subject: [PATCH 24/42] CLOUDSTACK-1242: Fix inline mode Network refactor dropped many inline mode codes. I've added at least part of them back. Now I can create LB rule for inline mode. --- .../com/cloud/network/NetworkManagerImpl.java | 9 ++++++--- .../src/com/cloud/network/NetworkModelImpl.java | 17 ++++++++++++++--- .../com/cloud/network/NetworkServiceImpl.java | 12 ++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 7a6ac276c50..f5868658751 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -98,6 +98,7 @@ import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.IpDeployingRequester; import com.cloud.network.element.LoadBalancingServiceProvider; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.StaticNatServiceProvider; @@ -536,9 +537,11 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } IpDeployer deployer = null; NetworkElement element = _networkModel.getElementImplementingProvider(provider.getName()); - if (element instanceof IpDeployer) { - deployer = (IpDeployer) element; - } else { + if (!(element instanceof IpDeployingRequester)) { + throw new CloudRuntimeException("Element " + element + " is not a IpDeployingRequester!"); + } + deployer = ((IpDeployingRequester)element).getIpDeployer(network); + if (deployer == null) { throw new CloudRuntimeException("Fail to get ip deployer for element: " + element); } Set services = new HashSet(); diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index ce48e84eb78..ca7a900e39c 100644 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -75,6 +75,8 @@ import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.dao.UserIpv6AddressDao; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.IpDeployingRequester; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.rules.FirewallRule.Purpose; @@ -390,9 +392,18 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { throw new InvalidParameterException("There is no new provider for IP " + publicIp.getAddress() + " of service " + service.getName() + "!"); } Provider newProvider = (Provider) newProviders.toArray()[0]; - if (!oldProvider.equals(newProvider)) { - throw new InvalidParameterException("There would be multiple providers for IP " + publicIp.getAddress() + "!"); - } + Network network = _networksDao.findById(networkId); + NetworkElement oldElement = getElementImplementingProvider(oldProvider.getName()); + NetworkElement newElement = getElementImplementingProvider(newProvider.getName()); + if (oldElement instanceof IpDeployingRequester && newElement instanceof IpDeployingRequester) { + IpDeployer oldIpDeployer = ((IpDeployingRequester)oldElement).getIpDeployer(network); + IpDeployer newIpDeployer = ((IpDeployingRequester)newElement).getIpDeployer(network); + if (!oldIpDeployer.getProvider().getName().equals(newIpDeployer.getProvider().getName())) { + throw new InvalidParameterException("There would be multiple providers for IP " + publicIp.getAddress() + "!"); + } + } else { + throw new InvalidParameterException("Ip cannot be applied for new provider!"); + } return true; } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 050a1feb412..70f0fa818bd 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -333,6 +333,18 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { private boolean canIpsUseOffering(List publicIps, long offeringId) { Map> ipToServices = getIpToServices(publicIps, false, true); Map> serviceToProviders = _networkModel.getNetworkOfferingServiceProvidersMap(offeringId); + NetworkOfferingVO offering = _networkOfferingDao.findById(offeringId); + //For inline mode checking, using firewall provider for LB instead, because public ip would apply on firewall provider + if (offering.isInline()) { + Provider firewallProvider = null; + if (serviceToProviders.containsKey(Service.Firewall)) { + firewallProvider = (Provider)serviceToProviders.get(Service.Firewall).toArray()[0]; + } + Set p = new HashSet(); + p.add(firewallProvider); + serviceToProviders.remove(Service.Lb); + serviceToProviders.put(Service.Lb, p); + } for (PublicIp ip : ipToServices.keySet()) { Set services = ipToServices.get(ip); Provider provider = null; From 15e466c56dd195d3a4a5d9d63c320fdca4b8a836 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 12 Feb 2013 20:21:20 -0800 Subject: [PATCH 25/42] Add missing break to applyRules in FirewallManager Otherwise it would falling through PortForwarding rule. Seems like a typo after network refactor. --- server/src/com/cloud/network/firewall/FirewallManagerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index 0d17ba720a9..080f7b0edf6 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -545,6 +545,7 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, if (handled) break; } + break; case PortForwarding: for (PortForwardingServiceProvider element: _pfElements) { Network.Provider provider = element.getProvider(); From ae2b673e031e5cacdcece275985cdc4c9ee491ac Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Tue, 12 Feb 2013 23:14:24 -0700 Subject: [PATCH 26/42] Summary: fix cloud-agent KVM calls to cloudstack-agent for centos6.3 BUG-ID: CLOUDSTACK-1234 Bugfix-for: 4.1, master Reported-by: Rayees Namathponnan Signed-off-by: Marcus Sorensen 1360736064 -0700 --- scripts/vm/hypervisor/kvm/setup_agent.sh | 12 ++++++------ .../kvm/discoverer/KvmServerDiscoverer.java | 2 +- .../src/com/cloud/resource/ResourceManagerImpl.java | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/setup_agent.sh b/scripts/vm/hypervisor/kvm/setup_agent.sh index c9753939550..243f4a2b96c 100755 --- a/scripts/vm/hypervisor/kvm/setup_agent.sh +++ b/scripts/vm/hypervisor/kvm/setup_agent.sh @@ -19,12 +19,12 @@ -# Did cloud-agent installed +# Did cloudstack-agent installed #set -x install_cloud_agent() { local dev=$1 local retry=10 - which cloud-setup-agent + which cloudstack-setup-agent if [ $? -gt 0 ] then # download the repo @@ -51,7 +51,7 @@ install_cloud_agent() { while [ "$retry" -gt "0" ] do yum clean all - yum install cloud-agent -y + yum install cloudstack-agent -y if [ $? -gt 0 ] then let retry=retry-1 @@ -64,7 +64,7 @@ install_cloud_agent() { while [ "$retry" -gt "0" ] do yum clean all - yum update cloud-agent -y + yum update cloudstack-agent -y if [ $? -gt 0 ] then let retry=retry-1 @@ -155,7 +155,7 @@ cloud_agent_setup() { sed -i 's/\(SELINUX\)\(.*\)/\1=permissive/' /etc/selinux/config setenforce 0 fi - cloud-setup-agent --host=$host --zone=$zone --pod=$pod --cluster=$cluster --guid=$guid -a > /dev/null + cloudstack-setup-agent --host=$host --zone=$zone --pod=$pod --cluster=$cluster --guid=$guid -a > /dev/null } cloud_consoleP_setup() { @@ -224,5 +224,5 @@ then setenforce 0 fi -cloud-setup-agent --host=$host --zone=$zone --pod=$pod --cluster=$cluster --guid=$guid $paramters -a > /dev/null +cloudstack-setup-agent --host=$host --zone=$zone --pod=$pod --cluster=$cluster --guid=$guid $paramters -a > /dev/null #cloud_consoleP_setup $host $zone $pod diff --git a/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java b/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java index 76626205f45..75e00adbfe4 100644 --- a/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java +++ b/server/src/com/cloud/hypervisor/kvm/discoverer/KvmServerDiscoverer.java @@ -212,7 +212,7 @@ Listener, ResourceStateAdapter { parameters += " --prvNic=" + kvmPrivateNic; parameters += " --guestNic=" + kvmGuestNic; - SSHCmdHelper.sshExecuteCmd(sshConnection, "cloud-setup-agent " + parameters, 3); + SSHCmdHelper.sshExecuteCmd(sshConnection, "cloudstack-setup-agent " + parameters, 3); KvmDummyResourceBase kvmResource = new KvmDummyResourceBase(); Map params = new HashMap(); diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 82013d4380d..79ccdb30198 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -2361,7 +2361,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, ResourceState.Event.AdminCancelMaintenance, _nodeId); _agentMgr.pullAgentOutMaintenance(hostId); - // for kvm, need to log into kvm host, restart cloud-agent + // for kvm, need to log into kvm host, restart cloudstack-agent if (host.getHypervisorType() == HypervisorType.KVM) { _hostDao.loadDetails(host); String password = host.getDetail("password"); @@ -2382,7 +2382,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, try { SSHCmdHelper.sshExecuteCmdOneShot(connection, - "service cloud-agent restart"); + "service cloudstack-agent restart"); } catch (sshException e) { return false; } From 9fd09a7f5fe732a888f10c1dfffb9de28e485ae1 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 12:40:30 +0530 Subject: [PATCH 27/42] console-proxy: Fix broken build system due to console-proxy moves Signed-off-by: Rohit Yadav --- packaging/centos63/cloud.spec | 4 ++-- pom.xml | 2 +- services/console-proxy/server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 2577cd0e58b..a90fde95871 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -181,8 +181,8 @@ mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms mkdir -p ${RPM_BUILD_ROOT}%{_libdir}/python2.6/site-packages/ cp -r scripts/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts -install -D console-proxy/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso -install -D console-proxy/dist/systemvm.zip ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.zip +install -D services/console-proxy/server/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso +install -D services/console-proxy/server/dist/systemvm.zip ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.zip install python/lib/cloud_utils.py ${RPM_BUILD_ROOT}%{_libdir}/python2.6/site-packages/cloud_utils.py cp -r python/lib/cloudutils ${RPM_BUILD_ROOT}%{_libdir}/python2.6/site-packages/ python -m py_compile ${RPM_BUILD_ROOT}%{_libdir}/python2.6/site-packages/cloud_utils.py diff --git a/pom.xml b/pom.xml index f7aaf95f5d1..a9209c14260 100644 --- a/pom.xml +++ b/pom.xml @@ -361,7 +361,7 @@ **/target/** **/.vagrant build/build.number - console-proxy/js/jquery.js + services/console-proxy/server/js/jquery.js debian/compat debian/control debian/dirs diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index 58fd62a71a3..71e83933ca9 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -111,7 +111,7 @@ - ../patches/systemvm/debian/config/root/.ssh + ../../../patches/systemvm/debian/config/root/.ssh authorized_keys From eaf7767c8521e88f9d5b6f2865acafd34f9430fe Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Wed, 13 Feb 2013 12:59:54 +0530 Subject: [PATCH 28/42] Persistent Networks Documentation:Reviewed by - Jessica Tomechak and Likitha Shetty --- docs/en-US/creating-network-offerings.xml | 347 +++++++++++++--------- docs/en-US/networks.xml | 3 +- docs/en-US/persistent-network.xml | 99 ++++++ 3 files changed, 306 insertions(+), 143 deletions(-) create mode 100644 docs/en-US/persistent-network.xml diff --git a/docs/en-US/creating-network-offerings.xml b/docs/en-US/creating-network-offerings.xml index 0269ce024cb..07f5a9e7cdf 100644 --- a/docs/en-US/creating-network-offerings.xml +++ b/docs/en-US/creating-network-offerings.xml @@ -22,146 +22,209 @@ under the License. -->
- Creating a New Network Offering - To create a network offering: - - Log in with admin privileges to the &PRODUCT; UI. - In the left navigation bar, click Service Offerings. - In Select Offering, choose Network Offering. - Click Add Network Offering. - In the dialog, make the following choices: - - Name. Any desired name for the network offering - Description. A short description of the offering that can be - displayed to users - Network Rate. Allowed data transfer rate in MB per - second - Guest Type. Choose whether the guest network is isolated or - shared. For a description of these terms, see - - Specify VLAN. (Isolated guest networks only) Indicate whether - a VLAN should be specified when this offering is used - Supported Services. Select one or more of the possible - network services. For some services, you must also choose the service - provider; for example, if you select Load Balancer, you can choose the - &PRODUCT; virtual router or any other load balancers that have been - configured in the cloud. Depending on which services you choose, additional - fields may appear in the rest of the dialog box.Based on the guest network type selected, you can see the following supported services: - - - - Supported Services - Description - Isolated - Shared - - - - - DHCP - For more information, see . - Supported - Supported - - - DNS - For more information, see . - Supported - Supported - - - Load Balancer - If you select Load Balancer, you can choose the &PRODUCT; virtual router or any other load - balancers that have been configured in the cloud. - Supported - Supported - - - Source NAT - If you select Source NAT, you can choose the &PRODUCT; virtual router or any other Source - NAT providers that have been configured in the - cloud. - Supported - Supported - - - Static NAT - If you select Static NAT, you can choose the &PRODUCT; virtual router or any other Static - NAT providers that have been configured in the - cloud. - Supported - Supported - - - Port Forwarding - If you select Port Forwarding, you can choose the &PRODUCT; virtual router or any other - Port Forwarding providers that have been configured in - the cloud. - Supported - Not Supported - - - VPN - For more information, see . - Supported - Not Supported - - - User Data - For more information, see . - Not Supported - Supported - - - Network ACL - For more information, see . - Supported - Not Supported - - - Security Groups - For more information, see . - Not Supported - Supported - - - - - - System Offering. If the service provider for any of the - services selected in Supported Services is a virtual router, the System - Offering field appears. Choose the system service offering that you want - virtual routers to use in this network. For example, if you selected Load - Balancer in Supported Services and selected a virtual router to provide load - balancing, the System Offering field appears so you can choose between the - &PRODUCT; default system service offering and any custom system service - offerings that have been defined by the &PRODUCT; root administrator. - For more information, see System Service Offerings. - Redundant router capability. Available - only when Virtual Router is selected as the Source NAT provider. Select this - option if you want to use two virtual routers in the network for - uninterrupted connection: one operating as the master virtual router and the - other as the backup. The master virtual router receives requests from and - sends responses to the user’s VM. The backup virtual router is activated - only when the master is down. After the failover, the backup becomes the - master virtual router. &PRODUCT; deploys the routers on different hosts - to ensure reliability if one host is down. - Conserve mode. Indicate whether to use conserve mode. In this - mode, network resources are allocated only when the first virtual machine - starts in the network. When the conservative mode is off, the public IP can - only be used for a single service. For example, a public IP used for a port - forwarding rule cannot be used for defining other services, such as SaticNAT - or load balancing. When the conserve mode is on, you can define more than - one service on the same public IP. - If StaticNAT is enabled, irrespective of the status of the conserve mode, no port forwarding - or load balancing rule can be created for the IP. However, you can add - the firewall rules by using the createFirewallRule command. - Tags. Network tag to specify which physical network to - use. - - Click Add. - - - + Creating a New Network Offering + To create a network offering: + + + Log in with admin privileges to the &PRODUCT; UI. + + + In the left navigation bar, click Service Offerings. + + + In Select Offering, choose Network Offering. + + + Click Add Network Offering. + + + In the dialog, make the following choices: + + + Name. Any desired name for the network + offering. + + + Description. A short description of the offering + that can be displayed to users. + + + Network Rate. Allowed data transfer rate in MB per + second. + + + Guest Type. Choose whether the guest network is + isolated or shared. + For a description of this term, see . + For a description of this term, see the Administration Guide. + + + + Persistent. Indicate whether the guest network is + persistent or not. The network that you can provision without having to deploy a VM on + it is termed persistent network. For more information, see . + + + Specify VLAN. (Isolated guest networks only) + Indicate whether a VLAN should be specified when this offering is used. + + + VPC. This option indicate whether the guest network + is Virtual Private Cloud-enabled. A Virtual Private Cloud (VPC) is a private, isolated + part of &PRODUCT;. A VPC can have its own virtual network topology that resembles a + traditional physical network. For more information on VPCs, see . + + + Supported Services. Select one or more of the + possible network services. For some services, you must also choose the service provider; + for example, if you select Load Balancer, you can choose the &PRODUCT; virtual router or + any other load balancers that have been configured in the cloud. Depending on which + services you choose, additional fields may appear in the rest of the dialog box. + Based on the guest network type selected, you can see the following supported + services: + + + + + Supported Services + Description + Isolated + Shared + + + + + DHCP + For more information, see . + Supported + Supported + + + DNS + For more information, see . + Supported + Supported + + + Load Balancer + If you select Load Balancer, you can choose the &PRODUCT; virtual + router or any other load balancers that have been configured in the + cloud. + Supported + Supported + + + Firewall + For more information, see . + For more information, see the Administration + Guide. + Supported + Supported + + + Source NAT + If you select Source NAT, you can choose the &PRODUCT; virtual router + or any other Source NAT providers that have been configured in the + cloud. + Supported + Supported + + + Static NAT + If you select Static NAT, you can choose the &PRODUCT; virtual router + or any other Static NAT providers that have been configured in the + cloud. + Supported + Supported + + + Port Forwarding + If you select Port Forwarding, you can choose the &PRODUCT; virtual + router or any other Port Forwarding providers that have been configured in the + cloud. + Supported + Not Supported + + + VPN + For more information, see . + Supported + Not Supported + + + User Data + For more information, see . + For more information, see the Administration + Guide. + Not Supported + Supported + + + Network ACL + For more information, see . + Supported + Not Supported + + + Security Groups + For more information, see . + Not Supported + Supported + + + + + + + System Offering. If the service provider for any of + the services selected in Supported Services is a virtual router, the System Offering + field appears. Choose the system service offering that you want virtual routers to use + in this network. For example, if you selected Load Balancer in Supported Services and + selected a virtual router to provide load balancing, the System Offering field appears + so you can choose between the &PRODUCT; default system service offering and any custom + system service offerings that have been defined by the &PRODUCT; root + administrator. + For more information, see . + For more information, see the Administration Guide. + + + Redundant router capability. Available only when + Virtual Router is selected as the Source NAT provider. Select this option if you want to + use two virtual routers in the network for uninterrupted connection: one operating as + the master virtual router and the other as the backup. The master virtual router + receives requests from and sends responses to the user’s VM. The backup virtual router + is activated only when the master is down. After the failover, the backup becomes the + master virtual router. &PRODUCT; deploys the routers on different hosts to ensure + reliability if one host is down. + + + Conserve mode. Indicate whether to use conserve + mode. In this mode, network resources are allocated only when the first virtual machine + starts in the network. When conservative mode is off, the public IP can only be used for + a single service. For example, a public IP used for a port forwarding rule cannot be + used for defining other services, such as SaticNAT or load balancing. When the conserve + mode is on, you can define more than one service on the same public IP. + + If StaticNAT is enabled, irrespective of the status of the conserve mode, no port + forwarding or load balancing rule can be created for the IP. However, you can add + firewall rules by using the createFirewallRule command. + + + + Tags. Network tag to specify which physical network + to use. + + + + + Click Add. + +
diff --git a/docs/en-US/networks.xml b/docs/en-US/networks.xml index a7b9ea12466..830576902b1 100644 --- a/docs/en-US/networks.xml +++ b/docs/en-US/networks.xml @@ -45,4 +45,5 @@ - \ No newline at end of file + + diff --git a/docs/en-US/persistent-network.xml b/docs/en-US/persistent-network.xml new file mode 100644 index 00000000000..e3719405c2d --- /dev/null +++ b/docs/en-US/persistent-network.xml @@ -0,0 +1,99 @@ + + +%BOOK_ENTITIES; +]> + + +
+ Persistent Networks + The network that you can provision without having to deploy any VMs on it is called a + persistent network. A persistent network can be part of a VPC or a non-VPC environment. + When you create other types of network, a network is only a database entry until the first + VM is created on that network. When the first VM is created, a VLAN ID is assigned and the + network is provisioned. Also, when the last VM is destroyed, the VLAN ID is released and the + network is no longer available. With the addition of persistent network, you will have the + ability to create a network in &PRODUCT; in which physical devices can be deployed without + having to run any VMs. Additionally, you can deploy physical devices on that network. + One of the advantages of having a persistent network is that you can create a VPC with a tier + consisting of only physical devices. For example, you might create a VPC for a three-tier + application, deploy VMs for Web and Application tier, and use physical machines for the + Database tier. Another use case is that if you are providing services by using physical + hardware, you can define the network as persistent and therefore even if all its VMs are + destroyed the services will not be discontinued. +
+ Persistent Network Considerations + + + Persistent network is designed for isolated networks. + + + All default network offerings are non-persistent. + + + A network offering cannot be editable because changing it affects the behavior of the + existing networks that were created using this network offering. + + + When you create a guest network, the network offering that you select defines the + network persistence. This in turn depends on whether persistent network is enabled in the + selected network offering. + + + An existing network can be made persistent by changing its network offering to an + offering that has the Persistent option enabled. While setting this property, even if the + network has no running VMs, the network is provisioned. + + + An existing network can be made non-persistent by changing its network offering to an + offering that has the Persistent option disabled. If the network has no running VMs, + during the next network garbage collection run the network is shut down. + + + When the last VM on a network is destroyed, the network garbage collector checks if + the network offering associated with the network is persistent, and shuts down the network + only if it is non-persistent. + + +
+
+ Creating a Persistent Guest Network + To create a persistent network, perform the following: + + + Create a network offering with the Persistent option enabled. + See . + + + Select Network from the left navigation pane. + + + Select the guest network that you want to offer this network service to. + + + Click the Edit button. + + + From the Network Offering drop-down, select the persistent network offering you have + just created. + + + Click OK. + + +
+
From 93096efc7727934ef1ec96f346bc5eaf2204e57e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 13:47:54 +0530 Subject: [PATCH 29/42] maven: build client at the end, fixes path error for console-proxy build target Signed-off-by: Rohit Yadav --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index a9209c14260..820e9380cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -163,11 +163,11 @@ deps/XenServerJava plugins patches - client - test engine framework - services + services + test + client From 0a9af54c9f1a659d685d6f2ff4a0eeb9670f0ddf Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 14:32:33 +0530 Subject: [PATCH 30/42] rat: Fix license for ResourceLimitManagerImplTest.java Signed-off-by: Rohit Yadav --- .../ResourceLimitManagerImplTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java index 0cf0459f532..d311ad38170 100644 --- a/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java +++ b/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -1,3 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. package com.cloud.resourcelimit; import javax.inject.Inject; From f23ec61a3212e2a298a2d1d781a202d3a4926f5b Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Wed, 13 Feb 2013 14:47:50 +0530 Subject: [PATCH 31/42] Revert "Persistent Networks Documentation:Reviewed by - Jessica Tomechak and Likitha Shetty" This reverts commit eaf7767c8521e88f9d5b6f2865acafd34f9430fe. --- docs/en-US/creating-network-offerings.xml | 347 +++++++++------------- docs/en-US/networks.xml | 3 +- docs/en-US/persistent-network.xml | 99 ------ 3 files changed, 143 insertions(+), 306 deletions(-) delete mode 100644 docs/en-US/persistent-network.xml diff --git a/docs/en-US/creating-network-offerings.xml b/docs/en-US/creating-network-offerings.xml index 07f5a9e7cdf..0269ce024cb 100644 --- a/docs/en-US/creating-network-offerings.xml +++ b/docs/en-US/creating-network-offerings.xml @@ -22,209 +22,146 @@ under the License. -->
- Creating a New Network Offering - To create a network offering: - - - Log in with admin privileges to the &PRODUCT; UI. - - - In the left navigation bar, click Service Offerings. - - - In Select Offering, choose Network Offering. - - - Click Add Network Offering. - - - In the dialog, make the following choices: - - - Name. Any desired name for the network - offering. - - - Description. A short description of the offering - that can be displayed to users. - - - Network Rate. Allowed data transfer rate in MB per - second. - - - Guest Type. Choose whether the guest network is - isolated or shared. - For a description of this term, see . - For a description of this term, see the Administration Guide. - - - - Persistent. Indicate whether the guest network is - persistent or not. The network that you can provision without having to deploy a VM on - it is termed persistent network. For more information, see . - - - Specify VLAN. (Isolated guest networks only) - Indicate whether a VLAN should be specified when this offering is used. - - - VPC. This option indicate whether the guest network - is Virtual Private Cloud-enabled. A Virtual Private Cloud (VPC) is a private, isolated - part of &PRODUCT;. A VPC can have its own virtual network topology that resembles a - traditional physical network. For more information on VPCs, see . - - - Supported Services. Select one or more of the - possible network services. For some services, you must also choose the service provider; - for example, if you select Load Balancer, you can choose the &PRODUCT; virtual router or - any other load balancers that have been configured in the cloud. Depending on which - services you choose, additional fields may appear in the rest of the dialog box. - Based on the guest network type selected, you can see the following supported - services: - - - - - Supported Services - Description - Isolated - Shared - - - - - DHCP - For more information, see . - Supported - Supported - - - DNS - For more information, see . - Supported - Supported - - - Load Balancer - If you select Load Balancer, you can choose the &PRODUCT; virtual - router or any other load balancers that have been configured in the - cloud. - Supported - Supported - - - Firewall - For more information, see . - For more information, see the Administration - Guide. - Supported - Supported - - - Source NAT - If you select Source NAT, you can choose the &PRODUCT; virtual router - or any other Source NAT providers that have been configured in the - cloud. - Supported - Supported - - - Static NAT - If you select Static NAT, you can choose the &PRODUCT; virtual router - or any other Static NAT providers that have been configured in the - cloud. - Supported - Supported - - - Port Forwarding - If you select Port Forwarding, you can choose the &PRODUCT; virtual - router or any other Port Forwarding providers that have been configured in the - cloud. - Supported - Not Supported - - - VPN - For more information, see . - Supported - Not Supported - - - User Data - For more information, see . - For more information, see the Administration - Guide. - Not Supported - Supported - - - Network ACL - For more information, see . - Supported - Not Supported - - - Security Groups - For more information, see . - Not Supported - Supported - - - - - - - System Offering. If the service provider for any of - the services selected in Supported Services is a virtual router, the System Offering - field appears. Choose the system service offering that you want virtual routers to use - in this network. For example, if you selected Load Balancer in Supported Services and - selected a virtual router to provide load balancing, the System Offering field appears - so you can choose between the &PRODUCT; default system service offering and any custom - system service offerings that have been defined by the &PRODUCT; root - administrator. - For more information, see . - For more information, see the Administration Guide. - - - Redundant router capability. Available only when - Virtual Router is selected as the Source NAT provider. Select this option if you want to - use two virtual routers in the network for uninterrupted connection: one operating as - the master virtual router and the other as the backup. The master virtual router - receives requests from and sends responses to the user’s VM. The backup virtual router - is activated only when the master is down. After the failover, the backup becomes the - master virtual router. &PRODUCT; deploys the routers on different hosts to ensure - reliability if one host is down. - - - Conserve mode. Indicate whether to use conserve - mode. In this mode, network resources are allocated only when the first virtual machine - starts in the network. When conservative mode is off, the public IP can only be used for - a single service. For example, a public IP used for a port forwarding rule cannot be - used for defining other services, such as SaticNAT or load balancing. When the conserve - mode is on, you can define more than one service on the same public IP. - - If StaticNAT is enabled, irrespective of the status of the conserve mode, no port - forwarding or load balancing rule can be created for the IP. However, you can add - firewall rules by using the createFirewallRule command. - - - - Tags. Network tag to specify which physical network - to use. - - - - - Click Add. - - + Creating a New Network Offering + To create a network offering: + + Log in with admin privileges to the &PRODUCT; UI. + In the left navigation bar, click Service Offerings. + In Select Offering, choose Network Offering. + Click Add Network Offering. + In the dialog, make the following choices: + + Name. Any desired name for the network offering + Description. A short description of the offering that can be + displayed to users + Network Rate. Allowed data transfer rate in MB per + second + Guest Type. Choose whether the guest network is isolated or + shared. For a description of these terms, see + + Specify VLAN. (Isolated guest networks only) Indicate whether + a VLAN should be specified when this offering is used + Supported Services. Select one or more of the possible + network services. For some services, you must also choose the service + provider; for example, if you select Load Balancer, you can choose the + &PRODUCT; virtual router or any other load balancers that have been + configured in the cloud. Depending on which services you choose, additional + fields may appear in the rest of the dialog box.Based on the guest network type selected, you can see the following supported services: + + + + Supported Services + Description + Isolated + Shared + + + + + DHCP + For more information, see . + Supported + Supported + + + DNS + For more information, see . + Supported + Supported + + + Load Balancer + If you select Load Balancer, you can choose the &PRODUCT; virtual router or any other load + balancers that have been configured in the cloud. + Supported + Supported + + + Source NAT + If you select Source NAT, you can choose the &PRODUCT; virtual router or any other Source + NAT providers that have been configured in the + cloud. + Supported + Supported + + + Static NAT + If you select Static NAT, you can choose the &PRODUCT; virtual router or any other Static + NAT providers that have been configured in the + cloud. + Supported + Supported + + + Port Forwarding + If you select Port Forwarding, you can choose the &PRODUCT; virtual router or any other + Port Forwarding providers that have been configured in + the cloud. + Supported + Not Supported + + + VPN + For more information, see . + Supported + Not Supported + + + User Data + For more information, see . + Not Supported + Supported + + + Network ACL + For more information, see . + Supported + Not Supported + + + Security Groups + For more information, see . + Not Supported + Supported + + + + + + System Offering. If the service provider for any of the + services selected in Supported Services is a virtual router, the System + Offering field appears. Choose the system service offering that you want + virtual routers to use in this network. For example, if you selected Load + Balancer in Supported Services and selected a virtual router to provide load + balancing, the System Offering field appears so you can choose between the + &PRODUCT; default system service offering and any custom system service + offerings that have been defined by the &PRODUCT; root administrator. + For more information, see System Service Offerings. + Redundant router capability. Available + only when Virtual Router is selected as the Source NAT provider. Select this + option if you want to use two virtual routers in the network for + uninterrupted connection: one operating as the master virtual router and the + other as the backup. The master virtual router receives requests from and + sends responses to the user’s VM. The backup virtual router is activated + only when the master is down. After the failover, the backup becomes the + master virtual router. &PRODUCT; deploys the routers on different hosts + to ensure reliability if one host is down. + Conserve mode. Indicate whether to use conserve mode. In this + mode, network resources are allocated only when the first virtual machine + starts in the network. When the conservative mode is off, the public IP can + only be used for a single service. For example, a public IP used for a port + forwarding rule cannot be used for defining other services, such as SaticNAT + or load balancing. When the conserve mode is on, you can define more than + one service on the same public IP. + If StaticNAT is enabled, irrespective of the status of the conserve mode, no port forwarding + or load balancing rule can be created for the IP. However, you can add + the firewall rules by using the createFirewallRule command. + Tags. Network tag to specify which physical network to + use. + + Click Add. + + +
diff --git a/docs/en-US/networks.xml b/docs/en-US/networks.xml index 830576902b1..a7b9ea12466 100644 --- a/docs/en-US/networks.xml +++ b/docs/en-US/networks.xml @@ -45,5 +45,4 @@ - - + \ No newline at end of file diff --git a/docs/en-US/persistent-network.xml b/docs/en-US/persistent-network.xml deleted file mode 100644 index e3719405c2d..00000000000 --- a/docs/en-US/persistent-network.xml +++ /dev/null @@ -1,99 +0,0 @@ - - -%BOOK_ENTITIES; -]> - - -
- Persistent Networks - The network that you can provision without having to deploy any VMs on it is called a - persistent network. A persistent network can be part of a VPC or a non-VPC environment. - When you create other types of network, a network is only a database entry until the first - VM is created on that network. When the first VM is created, a VLAN ID is assigned and the - network is provisioned. Also, when the last VM is destroyed, the VLAN ID is released and the - network is no longer available. With the addition of persistent network, you will have the - ability to create a network in &PRODUCT; in which physical devices can be deployed without - having to run any VMs. Additionally, you can deploy physical devices on that network. - One of the advantages of having a persistent network is that you can create a VPC with a tier - consisting of only physical devices. For example, you might create a VPC for a three-tier - application, deploy VMs for Web and Application tier, and use physical machines for the - Database tier. Another use case is that if you are providing services by using physical - hardware, you can define the network as persistent and therefore even if all its VMs are - destroyed the services will not be discontinued. -
- Persistent Network Considerations - - - Persistent network is designed for isolated networks. - - - All default network offerings are non-persistent. - - - A network offering cannot be editable because changing it affects the behavior of the - existing networks that were created using this network offering. - - - When you create a guest network, the network offering that you select defines the - network persistence. This in turn depends on whether persistent network is enabled in the - selected network offering. - - - An existing network can be made persistent by changing its network offering to an - offering that has the Persistent option enabled. While setting this property, even if the - network has no running VMs, the network is provisioned. - - - An existing network can be made non-persistent by changing its network offering to an - offering that has the Persistent option disabled. If the network has no running VMs, - during the next network garbage collection run the network is shut down. - - - When the last VM on a network is destroyed, the network garbage collector checks if - the network offering associated with the network is persistent, and shuts down the network - only if it is non-persistent. - - -
-
- Creating a Persistent Guest Network - To create a persistent network, perform the following: - - - Create a network offering with the Persistent option enabled. - See . - - - Select Network from the left navigation pane. - - - Select the guest network that you want to offer this network service to. - - - Click the Edit button. - - - From the Network Offering drop-down, select the persistent network offering you have - just created. - - - Click OK. - - -
-
From fcb4b7a6a7e646c5e3cd7961336258a3d30d6abc Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 15:17:49 +0530 Subject: [PATCH 32/42] CLOUDSTACK-1237: Fix an overriding method that would help find template adapter Signed-off-by: Rohit Yadav --- .../cloud/baremetal/manager/BareMetalTemplateAdapter.java | 5 +++++ .../src/com/cloud/baremetal/BareMetalTemplateAdapter.java | 7 ++++++- .../src/com/cloud/template/HyervisorTemplateAdapter.java | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java index ba5e811eeae..33725f63b1c 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java @@ -58,6 +58,11 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem private final static Logger s_logger = Logger.getLogger(BareMetalTemplateAdapter.class); @Inject HostDao _hostDao; @Inject ResourceManager _resourceMgr; + + @Override + public String getName() { + return TemplateAdapterType.BareMetal.getName(); + } @Override public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocationException { diff --git a/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java b/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java index 965c912a41e..4440b7a3a10 100755 --- a/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java +++ b/server/src/com/cloud/baremetal/BareMetalTemplateAdapter.java @@ -54,7 +54,12 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem private final static Logger s_logger = Logger.getLogger(BareMetalTemplateAdapter.class); @Inject HostDao _hostDao; @Inject ResourceManager _resourceMgr; - + + @Override + public String getName() { + return TemplateAdapterType.BareMetal.getName(); + } + @Override public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocationException { TemplateProfile profile = super.prepare(cmd); diff --git a/server/src/com/cloud/template/HyervisorTemplateAdapter.java b/server/src/com/cloud/template/HyervisorTemplateAdapter.java index 089f6508d7e..fe6bc2a86f0 100755 --- a/server/src/com/cloud/template/HyervisorTemplateAdapter.java +++ b/server/src/com/cloud/template/HyervisorTemplateAdapter.java @@ -70,6 +70,11 @@ public class HyervisorTemplateAdapter extends TemplateAdapterBase implements Tem @Inject DownloadMonitor _downloadMonitor; @Inject SecondaryStorageVmManager _ssvmMgr; @Inject AgentManager _agentMgr; + + @Override + public String getName() { + return TemplateAdapterType.Hypervisor.getName(); + } private String validateUrl(String url) { try { From 7401b06cb199037006a3991b944e0fbde730e04c Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 15:53:20 +0530 Subject: [PATCH 33/42] CLOUDSTACK-1070: Don't create HyervisorTemplateAdapter bean, it's already a @Component The issue was completely different than a spring injection issue Signed-off-by: Rohit Yadav --- client/tomcatconf/componentContext.xml.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/tomcatconf/componentContext.xml.in b/client/tomcatconf/componentContext.xml.in index 43d31fb4b95..c45ab1bd91b 100644 --- a/client/tomcatconf/componentContext.xml.in +++ b/client/tomcatconf/componentContext.xml.in @@ -119,10 +119,6 @@ - - - - From 8234dfa544625cd300559a7380fee7e9828870f8 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 16:51:48 +0530 Subject: [PATCH 34/42] usage: Fix classpath issue for usage server Signed-off-by: Rohit Yadav --- usage/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usage/pom.xml b/usage/pom.xml index 4e418ec9051..bf2001b432c 100644 --- a/usage/pom.xml +++ b/usage/pom.xml @@ -166,8 +166,8 @@ - pid - $$ + catalina.home + ${project.parent.basedir}/utils From 2e2ee2f3ed389b1b1a8a89eae8a641314417ca83 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 13 Feb 2013 18:42:38 +0530 Subject: [PATCH 35/42] CLOUDSTACK-1088: EnableStaticNat error will clear the data in database The issue occur in two conditions (1) If I use two sessions or browsers to EnableStaticNat on CloudStack UI. one is successful, the other is failed. However, there is no ip in database. (2) If I use API call EnableStaticNat several times The first time succeed, the second failed, the third succeed. the result is success-fail-success-fail-success-fail, which it is not correct. Reported-by: Wei Zhou Reviewed-by: https://reviews.apache.org/r/9254/ Signed-off-by: Prasanna Santhanam --- .../cloud/network/rules/RulesManagerImpl.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 0a00d22b42a..614d30820b4 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -412,7 +412,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules // Verify input parameters boolean performedIpAssoc = false; - boolean result = false; + boolean isOneToOneNat = ipAddress.isOneToOneNat(); + Long associatedWithVmId = ipAddress.getAssociatedWithVmId(); try { Network network = _networkModel.getNetwork(networkId); if (network == null) { @@ -476,28 +477,26 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules // enable static nat on the backend s_logger.trace("Enabling static nat for ip address " + ipAddress + " and vm id=" + vmId + " on the backend"); if (applyStaticNatForIp(ipId, false, caller, false)) { - result = true; + performedIpAssoc = false; // ignor unassignIPFromVpcNetwork in finally block + return true; } else { s_logger.warn("Failed to enable static nat rule for ip address " + ipId + " on the backend"); + ipAddress.setOneToOneNat(isOneToOneNat); + ipAddress.setAssociatedWithVmId(associatedWithVmId); + _ipAddressDao.update(ipAddress.getId(), ipAddress); } } else { s_logger.warn("Failed to update ip address " + ipAddress + " in the DB as a part of enableStaticNat"); } } finally { - if (!result) { - ipAddress.setOneToOneNat(false); - ipAddress.setAssociatedWithVmId(null); - _ipAddressDao.update(ipAddress.getId(), ipAddress); - - if (performedIpAssoc) { - //if the rule is the last one for the ip address assigned to VPC, unassign it from the network - IpAddress ip = _ipAddressDao.findById(ipAddress.getId()); - _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), networkId); - } + if (performedIpAssoc) { + //if the rule is the last one for the ip address assigned to VPC, unassign it from the network + IpAddress ip = _ipAddressDao.findById(ipAddress.getId()); + _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), networkId); } } - return result; + return false; } protected void isIpReadyForStaticNat(long vmId, IPAddressVO ipAddress, Account caller, long callerUserId) From 3a0c99b0a430b12a492f2b93dec8d110603b43b4 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Feb 2013 22:45:32 +0530 Subject: [PATCH 36/42] CLOUDSTACK-710: CitrixResourceBase, if dest dir is not found, mkdir -m 700 -p This is a security failsafe, so even if destination does not exist we mkdir the path with 0700 permission. If path exists mkdir -m 700 -p won't do anything. Signed-off-by: Rohit Yadav --- .../com/cloud/hypervisor/xen/resource/CitrixResourceBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index c7ff5c7b4bf..b3cb54f783e 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -4763,7 +4763,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe s_logger.debug("Copying " + f + " to " + d + " on " + hr.address + " with permission " + p); } try { - session.execCommand("mkdir -p " + d); + session.execCommand("mkdir -m 700 -p " + d); } catch (IOException e) { s_logger.debug("Unable to create destination path: " + d + " on " + hr.address + " but trying anyway"); From 9a12756ae42f0bf372dcc9d86b2a60710104075a Mon Sep 17 00:00:00 2001 From: Mice Xia Date: Thu, 14 Feb 2013 01:12:35 +0800 Subject: [PATCH 37/42] CLOUDSTACK-684 support vm snapshot --- .../agent/api/CreateVMSnapshotAnswer.java | 62 ++ .../agent/api/CreateVMSnapshotCommand.java | 42 + .../api/CreateVolumeFromVMSnapshotAnswer.java | 54 ++ .../CreateVolumeFromVMSnapshotCommand.java | 88 ++ .../agent/api/DeleteVMSnapshotAnswer.java | 49 ++ .../agent/api/DeleteVMSnapshotCommand.java | 28 + .../agent/api/RevertToVMSnapshotAnswer.java | 63 ++ .../agent/api/RevertToVMSnapshotCommand.java | 29 + .../agent/api/VMSnapshotBaseCommand.java | 74 ++ api/src/com/cloud/agent/api/VMSnapshotTO.java | 90 ++ api/src/com/cloud/agent/api/to/VolumeTO.java | 4 + api/src/com/cloud/event/EventTypes.java | 5 + api/src/com/cloud/server/ResourceTag.java | 3 +- api/src/com/cloud/vm/VirtualMachine.java | 6 +- api/src/com/cloud/vm/snapshot/VMSnapshot.java | 110 +++ .../cloud/vm/snapshot/VMSnapshotService.java | 48 + .../apache/cloudstack/api/ApiConstants.java | 5 + .../cloudstack/api/ApiConstants.java.orig | 468 ++++++++++ .../org/apache/cloudstack/api/BaseCmd.java | 2 + .../cloudstack/api/ResponseGenerator.java | 5 +- .../user/vmsnapshot/CreateVMSnapshotCmd.java | 125 +++ .../user/vmsnapshot/DeleteVMSnapshotCmd.java | 85 ++ .../user/vmsnapshot/ListVMSnapshotCmd.java | 89 ++ .../user/vmsnapshot/RevertToSnapshotCmd.java | 92 ++ .../api/response/VMSnapshotResponse.java | 220 +++++ .../classes/resources/messages.properties | 14 + client/tomcatconf/commands.properties.in | 6 + .../vmware/manager/VmwareStorageManager.java | 16 +- .../manager/VmwareStorageManagerImpl.java | 332 ++++++- .../vmware/resource/VmwareResource.java | 50 +- .../xen/resource/CitrixResourceBase.java | 302 +++++++ scripts/vm/hypervisor/xenserver/vmopsSnapshot | 31 +- server/src/com/cloud/api/ApiDBUtils.java | 12 +- .../src/com/cloud/api/ApiResponseHelper.java | 29 +- .../cloud/capacity/CapacityManagerImpl.java | 43 +- .../src/com/cloud/configuration/Config.java | 11 +- .../cloud/server/ManagementServerImpl.java | 8 + .../storage/snapshot/SnapshotManagerImpl.java | 14 +- .../cloud/tags/TaggedResourceManagerImpl.java | 4 + .../src/com/cloud/vm/UserVmManagerImpl.java | 74 +- .../cloud/vm/VirtualMachineManagerImpl.java | 65 +- .../cloud/vm/snapshot/VMSnapshotManager.java | 47 + .../vm/snapshot/VMSnapshotManagerImpl.java | 833 ++++++++++++++++++ .../cloud/vm/snapshot/dao/VMSnapshotDao.java | 39 + .../vm/snapshot/dao/VMSnapshotDaoImpl.java | 161 ++++ .../vm/snapshot/VMSnapshotManagerTest.java | 186 ++++ setup/db/create-schema.sql | 30 + ui/css/cloudstack3.css | 2 +- ui/dictionary.jsp | 12 +- ui/index.jsp | 2 + ui/scripts/instances.js | 259 +++++- ui/scripts/ui/widgets/detailView.js | 13 +- ui/scripts/vm_snapshots.js | 196 +++++ .../cloud/hypervisor/vmware/mo/HostMO.java | 18 + .../vmware/mo/VirtualMachineMO.java | 20 + 55 files changed, 4595 insertions(+), 80 deletions(-) create mode 100644 api/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java create mode 100644 api/src/com/cloud/agent/api/CreateVMSnapshotCommand.java create mode 100644 api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotAnswer.java create mode 100644 api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotCommand.java create mode 100644 api/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java create mode 100644 api/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java create mode 100644 api/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java create mode 100644 api/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java create mode 100644 api/src/com/cloud/agent/api/VMSnapshotBaseCommand.java create mode 100644 api/src/com/cloud/agent/api/VMSnapshotTO.java create mode 100644 api/src/com/cloud/vm/snapshot/VMSnapshot.java create mode 100644 api/src/com/cloud/vm/snapshot/VMSnapshotService.java create mode 100644 api/src/org/apache/cloudstack/api/ApiConstants.java.orig create mode 100644 api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java create mode 100644 api/src/org/apache/cloudstack/api/response/VMSnapshotResponse.java create mode 100644 server/src/com/cloud/vm/snapshot/VMSnapshotManager.java create mode 100644 server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java create mode 100644 server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java create mode 100644 server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java create mode 100644 server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java create mode 100644 ui/scripts/vm_snapshots.js diff --git a/api/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java b/api/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java new file mode 100644 index 00000000000..f9fb1642b3f --- /dev/null +++ b/api/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java @@ -0,0 +1,62 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; + +public class CreateVMSnapshotAnswer extends Answer { + + private List volumeTOs; + private VMSnapshotTO vmSnapshotTo; + + + public List getVolumeTOs() { + return volumeTOs; + } + + public void setVolumeTOs(List volumeTOs) { + this.volumeTOs = volumeTOs; + } + + public VMSnapshotTO getVmSnapshotTo() { + return vmSnapshotTo; + } + + public void setVmSnapshotTo(VMSnapshotTO vmSnapshotTo) { + this.vmSnapshotTo = vmSnapshotTo; + } + + public CreateVMSnapshotAnswer() { + + } + + public CreateVMSnapshotAnswer(CreateVMSnapshotCommand cmd, boolean success, + String result) { + super(cmd, success, result); + } + + public CreateVMSnapshotAnswer(CreateVMSnapshotCommand cmd, + VMSnapshotTO vmSnapshotTo, List volumeTOs) { + super(cmd, true, ""); + this.vmSnapshotTo = vmSnapshotTo; + this.volumeTOs = volumeTOs; + } + +} diff --git a/api/src/com/cloud/agent/api/CreateVMSnapshotCommand.java b/api/src/com/cloud/agent/api/CreateVMSnapshotCommand.java new file mode 100644 index 00000000000..478987d993b --- /dev/null +++ b/api/src/com/cloud/agent/api/CreateVMSnapshotCommand.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.vm.VirtualMachine; + +public class CreateVMSnapshotCommand extends VMSnapshotBaseCommand { + + public CreateVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, VirtualMachine.State vmState) { + super(vmName, snapshot, volumeTOs, guestOSType); + this.vmState = vmState; + } + + private VirtualMachine.State vmState; + + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + +} diff --git a/api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotAnswer.java b/api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotAnswer.java new file mode 100644 index 00000000000..ed3bc62ccba --- /dev/null +++ b/api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotAnswer.java @@ -0,0 +1,54 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.agent.api; + +import com.cloud.agent.api.to.VolumeTO; + +public class CreateVolumeFromVMSnapshotAnswer extends Answer { + private String path; + private VolumeTO volumeTo; + + public VolumeTO getVolumeTo() { + return volumeTo; + } + + public CreateVolumeFromVMSnapshotAnswer( + CreateVolumeFromVMSnapshotCommand cmd, VolumeTO volumeTo) { + super(cmd, true, ""); + this.volumeTo = volumeTo; + } + + public String getPath() { + return path; + } + + protected CreateVolumeFromVMSnapshotAnswer() { + + } + + public CreateVolumeFromVMSnapshotAnswer( + CreateVolumeFromVMSnapshotCommand cmd, String path) { + super(cmd, true, ""); + this.path = path; + } + + public CreateVolumeFromVMSnapshotAnswer( + CreateVolumeFromVMSnapshotCommand cmd, boolean result, String string) { + super(cmd, result, string); + } +} diff --git a/api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotCommand.java b/api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotCommand.java new file mode 100644 index 00000000000..634e15c9f04 --- /dev/null +++ b/api/src/com/cloud/agent/api/CreateVolumeFromVMSnapshotCommand.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api; + +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.vm.DiskProfile; + +public class CreateVolumeFromVMSnapshotCommand extends Command { + + protected String path; + protected String name; + protected Boolean fullClone; + protected String storagePoolUuid; + private StorageFilerTO pool; + private DiskProfile diskProfile; + private Long volumeId; + + public DiskProfile getDskch() { + return diskProfile; + } + + public String getPath() { + return path; + } + + public Long getVolumeId() { + return volumeId; + } + + protected CreateVolumeFromVMSnapshotCommand() { + + } + + public CreateVolumeFromVMSnapshotCommand(String path, String name, + Boolean fullClone, String storagePoolUuid) { + this.path = path; + this.name = name; + this.fullClone = fullClone; + this.storagePoolUuid = storagePoolUuid; + } + + public CreateVolumeFromVMSnapshotCommand(String path, String name, + Boolean fullClone, String storagePoolUuid, StorageFilerTO pool, + DiskProfile diskProfile, Long volumeId) { + this.path = path; + this.name = name; + this.fullClone = fullClone; + this.storagePoolUuid = storagePoolUuid; + this.pool = pool; + this.diskProfile = diskProfile; + this.volumeId = volumeId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getName() { + return name; + } + + public Boolean getFullClone() { + return fullClone; + } + + public String getStoragePoolUuid() { + return storagePoolUuid; + } + + public StorageFilerTO getPool() { + return pool; + } +} diff --git a/api/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java b/api/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java new file mode 100644 index 00000000000..8f4ecad3d80 --- /dev/null +++ b/api/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; + +public class DeleteVMSnapshotAnswer extends Answer { + private List volumeTOs; + + public DeleteVMSnapshotAnswer() { + } + + public DeleteVMSnapshotAnswer(DeleteVMSnapshotCommand cmd, boolean result, + String message) { + super(cmd, result, message); + } + + public DeleteVMSnapshotAnswer(DeleteVMSnapshotCommand cmd, + List volumeTOs) { + super(cmd, true, ""); + this.volumeTOs = volumeTOs; + } + + public List getVolumeTOs() { + return volumeTOs; + } + + public void setVolumeTOs(List volumeTOs) { + this.volumeTOs = volumeTOs; + } + + +} diff --git a/api/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java b/api/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java new file mode 100644 index 00000000000..c213448bf9c --- /dev/null +++ b/api/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; + + +public class DeleteVMSnapshotCommand extends VMSnapshotBaseCommand { + public DeleteVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { + super( vmName, snapshot, volumeTOs, guestOSType); + } +} diff --git a/api/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java b/api/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java new file mode 100644 index 00000000000..848ffc0ebf8 --- /dev/null +++ b/api/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.vm.VirtualMachine; + +public class RevertToVMSnapshotAnswer extends Answer { + + private List volumeTOs; + private VirtualMachine.State vmState; + + public RevertToVMSnapshotAnswer(RevertToVMSnapshotCommand cmd, boolean result, + String message) { + super(cmd, result, message); + } + + public RevertToVMSnapshotAnswer() { + super(); + } + + public RevertToVMSnapshotAnswer(RevertToVMSnapshotCommand cmd, + List volumeTOs, + VirtualMachine.State vmState) { + super(cmd, true, ""); + this.volumeTOs = volumeTOs; + this.vmState = vmState; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public List getVolumeTOs() { + return volumeTOs; + } + + public void setVolumeTOs(List volumeTOs) { + this.volumeTOs = volumeTOs; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + +} diff --git a/api/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java b/api/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java new file mode 100644 index 00000000000..429a186e0dc --- /dev/null +++ b/api/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; + +public class RevertToVMSnapshotCommand extends VMSnapshotBaseCommand { + + public RevertToVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { + super(vmName, snapshot, volumeTOs, guestOSType); + } + +} diff --git a/api/src/com/cloud/agent/api/VMSnapshotBaseCommand.java b/api/src/com/cloud/agent/api/VMSnapshotBaseCommand.java new file mode 100644 index 00000000000..2120f2f73b1 --- /dev/null +++ b/api/src/com/cloud/agent/api/VMSnapshotBaseCommand.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.to.VolumeTO; + +public class VMSnapshotBaseCommand extends Command{ + protected List volumeTOs; + protected VMSnapshotTO target; + protected String vmName; + protected String guestOSType; + + + public VMSnapshotBaseCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { + this.vmName = vmName; + this.target = snapshot; + this.volumeTOs = volumeTOs; + this.guestOSType = guestOSType; + } + + public List getVolumeTOs() { + return volumeTOs; + } + + public void setVolumeTOs(List volumeTOs) { + this.volumeTOs = volumeTOs; + } + + public VMSnapshotTO getTarget() { + return target; + } + + public void setTarget(VMSnapshotTO target) { + this.target = target; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getGuestOSType() { + return guestOSType; + } + + public void setGuestOSType(String guestOSType) { + this.guestOSType = guestOSType; + } +} diff --git a/api/src/com/cloud/agent/api/VMSnapshotTO.java b/api/src/com/cloud/agent/api/VMSnapshotTO.java new file mode 100644 index 00000000000..c7b42d25bc9 --- /dev/null +++ b/api/src/com/cloud/agent/api/VMSnapshotTO.java @@ -0,0 +1,90 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api; + +import com.cloud.vm.snapshot.VMSnapshot; + +public class VMSnapshotTO { + private Long id; + private String snapshotName; + private VMSnapshot.Type type; + private Long createTime; + private Boolean current; + private String description; + private VMSnapshotTO parent; + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public VMSnapshotTO(Long id, String snapshotName, + VMSnapshot.Type type, Long createTime, + String description, Boolean current, VMSnapshotTO parent) { + super(); + this.id = id; + this.snapshotName = snapshotName; + this.type = type; + this.createTime = createTime; + this.current = current; + this.description = description; + this.parent = parent; + } + public VMSnapshotTO() { + + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public Boolean getCurrent() { + return current; + } + public void setCurrent(Boolean current) { + this.current = current; + } + public Long getCreateTime() { + return createTime; + } + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + public VMSnapshot.Type getType() { + return type; + } + public void setType(VMSnapshot.Type type) { + this.type = type; + } + + public String getSnapshotName() { + return snapshotName; + } + public void setSnapshotName(String snapshotName) { + this.snapshotName = snapshotName; + } + public VMSnapshotTO getParent() { + return parent; + } + public void setParent(VMSnapshotTO parent) { + this.parent = parent; + } + +} diff --git a/api/src/com/cloud/agent/api/to/VolumeTO.java b/api/src/com/cloud/agent/api/to/VolumeTO.java index a8846b96261..4cbe82b357b 100644 --- a/api/src/com/cloud/agent/api/to/VolumeTO.java +++ b/api/src/com/cloud/agent/api/to/VolumeTO.java @@ -124,6 +124,10 @@ public class VolumeTO implements InternalIdentity { public String getOsType() { return guestOsType; } + + public void setPath(String path){ + this.path = path; + } @Override public String toString() { diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 0dd97cb438c..0087edca743 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -331,6 +331,11 @@ public class EventTypes { // tag related events public static final String EVENT_TAGS_CREATE = "CREATE_TAGS"; public static final String EVENT_TAGS_DELETE = "DELETE_TAGS"; + + // vm snapshot events + public static final String EVENT_VM_SNAPSHOT_CREATE = "VMSNAPSHOT.CREATE"; + public static final String EVENT_VM_SNAPSHOT_DELETE = "VMSNAPSHOT.DELETE"; + public static final String EVENT_VM_SNAPSHOT_REVERT = "VMSNAPSHOT.REVERTTO"; // external network device events public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD"; diff --git a/api/src/com/cloud/server/ResourceTag.java b/api/src/com/cloud/server/ResourceTag.java index 5ec9f0171cc..ee56748640c 100644 --- a/api/src/com/cloud/server/ResourceTag.java +++ b/api/src/com/cloud/server/ResourceTag.java @@ -37,7 +37,8 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit Project, Vpc, NetworkACL, - StaticRoute + StaticRoute, + VMSnapshot } /** diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index 248b98247e3..4300dd548c1 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -112,7 +112,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I s_fsm.addTransition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging); s_fsm.addTransition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging); } - + public static boolean isVmStarted(State oldState, Event e, State newState) { if (oldState == State.Starting && newState == State.Running) { return true; @@ -174,7 +174,9 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I OperationFailedToError, OperationRetry, AgentReportShutdowned, - AgentReportMigrated + AgentReportMigrated, + RevertRequested, + SnapshotRequested }; public enum Type { diff --git a/api/src/com/cloud/vm/snapshot/VMSnapshot.java b/api/src/com/cloud/vm/snapshot/VMSnapshot.java new file mode 100644 index 00000000000..f0ee7ee7e8a --- /dev/null +++ b/api/src/com/cloud/vm/snapshot/VMSnapshot.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.vm.snapshot; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import org.apache.cloudstack.acl.ControlledEntity; +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.utils.fsm.StateObject; + +public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity,StateObject { + + enum State { + Allocated("The VM snapshot is allocated but has not been created yet."), + Creating("The VM snapshot is being created."), + Ready("The VM snapshot is ready to be used."), + Reverting("The VM snapshot is being used to revert"), + Expunging("The volume is being expunging"), + Removed("The volume is destroyed, and can't be recovered."), + Error ("The volume is in error state, and can't be recovered"); + + String _description; + + private State(String description) { + _description = description; + } + + public static StateMachine2 getStateMachine() { + return s_fsm; + } + + public String getDescription() { + return _description; + } + + private final static StateMachine2 s_fsm = new StateMachine2(); + static { + s_fsm.addTransition(Allocated, Event.CreateRequested, Creating); + s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready); + s_fsm.addTransition(Creating, Event.OperationFailed, Error); + s_fsm.addTransition(Ready, Event.RevertRequested, Reverting); + s_fsm.addTransition(Reverting, Event.OperationSucceeded, Ready); + s_fsm.addTransition(Reverting, Event.OperationFailed, Ready); + s_fsm.addTransition(Ready, Event.ExpungeRequested, Expunging); + s_fsm.addTransition(Error, Event.ExpungeRequested, Expunging); + s_fsm.addTransition(Expunging, Event.ExpungeRequested, Expunging); + s_fsm.addTransition(Expunging, Event.OperationSucceeded, Removed); + } + } + + enum Type{ + Disk, DiskAndMemory + } + + enum Event { + CreateRequested, + OperationFailed, + OperationSucceeded, + RevertRequested, + ExpungeRequested, + } + + long getId(); + + public String getName(); + + public Long getVmId(); + + public State getState(); + + public Date getCreated(); + + public String getDescription(); + + public String getDisplayName(); + + public Long getParent(); + + public Boolean getCurrent(); + + public Type getType(); + + public long getUpdatedCount(); + + public void incrUpdatedCount(); + + public Date getUpdated(); + + public Date getRemoved(); + + public long getAccountId(); +} diff --git a/api/src/com/cloud/vm/snapshot/VMSnapshotService.java b/api/src/com/cloud/vm/snapshot/VMSnapshotService.java new file mode 100644 index 00000000000..83f86bc90db --- /dev/null +++ b/api/src/com/cloud/vm/snapshot/VMSnapshotService.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.vm.snapshot; + +import java.util.List; + +import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; + +public interface VMSnapshotService { + + List listVMSnapshots(ListVMSnapshotCmd cmd); + + VMSnapshot getVMSnapshotById(Long id); + + VMSnapshot creatVMSnapshot(Long vmId, Long vmSnapshotId); + + VMSnapshot allocVMSnapshot(Long vmId, String vsDisplayName, String vsDescription, Boolean snapshotMemory) + throws ResourceAllocationException; + + boolean deleteVMSnapshot(Long vmSnapshotId); + + UserVm revertToSnapshot(Long vmSnapshotId) throws InsufficientServerCapacityException, InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException; + + VirtualMachine getVMBySnapshotId(Long id); +} diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index d29408e66f2..cd7d700d2b5 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -438,6 +438,11 @@ public class ApiConstants { public static final String AUTOSCALE_USER_ID = "autoscaleuserid"; public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername"; public static final String UCS_DN = "ucsdn"; + public static final String VM_SNAPSHOT_DESCRIPTION = "description"; + public static final String VM_SNAPSHOT_DISPLAYNAME = "name"; + public static final String VM_SNAPSHOT_ID = "vmsnapshotid"; + public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids"; + public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java.orig b/api/src/org/apache/cloudstack/api/ApiConstants.java.orig new file mode 100644 index 00000000000..3801506ffaa --- /dev/null +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java.orig @@ -0,0 +1,468 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api; + + +public class ApiConstants { + public static final String ACCOUNT = "account"; + public static final String ACCOUNTS = "accounts"; + public static final String ACCOUNT_TYPE = "accounttype"; + public static final String ACCOUNT_ID = "accountid"; + public static final String ALGORITHM = "algorithm"; + public static final String ALLOCATED_ONLY = "allocatedonly"; + public static final String API_KEY = "userapikey"; + public static final String APPLIED = "applied"; + public static final String AVAILABLE = "available"; + public static final String BITS = "bits"; + public static final String BOOTABLE = "bootable"; + public static final String BIND_DN = "binddn"; + public static final String BIND_PASSWORD = "bindpass"; + public static final String CATEGORY = "category"; + public static final String CERTIFICATE = "certificate"; + public static final String PRIVATE_KEY = "privatekey"; + public static final String DOMAIN_SUFFIX = "domainsuffix"; + public static final String DNS_SEARCH_ORDER = "dnssearchorder"; + public static final String CIDR = "cidr"; + public static final String IP6_CIDR = "ip6cidr"; + public static final String CIDR_LIST = "cidrlist"; + public static final String CLEANUP = "cleanup"; + public static final String CLUSTER_ID = "clusterid"; + public static final String CLUSTER_NAME = "clustername"; + public static final String CLUSTER_TYPE = "clustertype"; + public static final String COMPONENT = "component"; + public static final String CPU_NUMBER = "cpunumber"; + public static final String CPU_SPEED = "cpuspeed"; + public static final String CREATED = "created"; + public static final String CUSTOMIZED = "customized"; + public static final String DESCRIPTION = "description"; + public static final String DESTINATION_ZONE_ID = "destzoneid"; + public static final String DETAILS = "details"; + public static final String DEVICE_ID = "deviceid"; + public static final String DISK_OFFERING_ID = "diskofferingid"; + public static final String DISK_SIZE = "disksize"; + public static final String DISPLAY_NAME = "displayname"; + public static final String DISPLAY_TEXT = "displaytext"; + public static final String DNS1 = "dns1"; + public static final String DNS2 = "dns2"; + public static final String DOMAIN = "domain"; + public static final String DOMAIN_ID = "domainid"; + public static final String DURATION = "duration"; + public static final String EMAIL = "email"; + public static final String END_DATE = "enddate"; + public static final String END_IP = "endip"; + public static final String END_IPV6 = "endipv6"; + public static final String END_PORT = "endport"; + public static final String ENTRY_TIME = "entrytime"; + public static final String FETCH_LATEST = "fetchlatest"; + public static final String FIRSTNAME = "firstname"; + public static final String FORCED = "forced"; + public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage"; + public static final String FORMAT = "format"; + public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork"; + public static final String GATEWAY = "gateway"; + public static final String IP6_GATEWAY = "ip6gateway"; + public static final String GROUP = "group"; + public static final String GROUP_ID = "groupid"; + public static final String GUEST_CIDR_ADDRESS = "guestcidraddress"; + public static final String HA_ENABLE = "haenable"; + public static final String HOST_ID = "hostid"; + public static final String HOST_NAME = "hostname"; + public static final String HYPERVISOR = "hypervisor"; + public static final String INLINE = "inline"; + public static final String INSTANCE = "instance"; + public static final String ICMP_CODE = "icmpcode"; + public static final String ICMP_TYPE = "icmptype"; + public static final String ID = "id"; + public static final String IDS = "ids"; + public static final String INTERNAL_DNS1 = "internaldns1"; + public static final String INTERNAL_DNS2 = "internaldns2"; + public static final String INTERVAL_TYPE = "intervaltype"; + public static final String IP_ADDRESS = "ipaddress"; + public static final String IP6_ADDRESS = "ip6address"; + public static final String IP_ADDRESS_ID = "ipaddressid"; + public static final String IS_ASYNC = "isasync"; + public static final String IP_AVAILABLE = "ipavailable"; + public static final String IP_LIMIT = "iplimit"; + public static final String IP_TOTAL = "iptotal"; + public static final String IS_CLEANUP_REQUIRED = "iscleanuprequired"; + public static final String IS_EXTRACTABLE = "isextractable"; + public static final String IS_FEATURED = "isfeatured"; + public static final String IS_PUBLIC = "ispublic"; + public static final String IS_PERSISTENT = "ispersistent"; + public static final String IS_READY = "isready"; + public static final String IS_RECURSIVE = "isrecursive"; + public static final String ISO_FILTER = "isofilter"; + public static final String ISO_GUEST_OS_NONE = "None"; + public static final String JOB_ID = "jobid"; + public static final String JOB_STATUS = "jobstatus"; + public static final String LASTNAME = "lastname"; + public static final String LEVEL = "level"; + public static final String LENGTH = "length"; + public static final String LIMIT_CPU_USE = "limitcpuuse"; + public static final String LOCK = "lock"; + public static final String LUN = "lun"; + public static final String LBID = "lbruleid"; + public static final String MAX = "max"; + public static final String MAX_SNAPS = "maxsnaps"; + public static final String MEMORY = "memory"; + public static final String MODE = "mode"; + public static final String NAME = "name"; + public static final String METHOD_NAME = "methodname"; + public static final String NETWORK_DOMAIN = "networkdomain"; + public static final String NETMASK = "netmask"; + public static final String NEW_NAME = "newname"; + public static final String NUM_RETRIES = "numretries"; + public static final String OFFER_HA = "offerha"; + public static final String IS_SYSTEM_OFFERING = "issystem"; + public static final String IS_DEFAULT_USE = "defaultuse"; + public static final String OP = "op"; + public static final String OS_CATEGORY_ID = "oscategoryid"; + public static final String OS_TYPE_ID = "ostypeid"; + public static final String PARAMS = "params"; + public static final String PARENT_DOMAIN_ID = "parentdomainid"; + public static final String PASSWORD = "password"; + public static final String NEW_PASSWORD = "new_password"; + public static final String PASSWORD_ENABLED = "passwordenabled"; + public static final String SSHKEY_ENABLED = "sshkeyenabled"; + public static final String PATH = "path"; + public static final String POD_ID = "podid"; + public static final String POD_IDS = "podids"; + public static final String POLICY_ID = "policyid"; + public static final String PORT = "port"; + public static final String PORTAL = "portal"; + public static final String PORT_FORWARDING_SERVICE_ID = "portforwardingserviceid"; + public static final String PRIVATE_INTERFACE = "privateinterface"; + public static final String PRIVATE_IP = "privateip"; + public static final String PRIVATE_PORT = "privateport"; + public static final String PRIVATE_START_PORT = "privateport"; + public static final String PRIVATE_END_PORT = "privateendport"; + public static final String PRIVATE_ZONE = "privatezone"; + public static final String PROTOCOL = "protocol"; + public static final String PUBLIC_INTERFACE = "publicinterface"; + public static final String PUBLIC_IP_ID = "publicipid"; + public static final String PUBLIC_IP = "publicip"; + public static final String PUBLIC_PORT = "publicport"; + public static final String PUBLIC_START_PORT = "publicport"; + public static final String PUBLIC_END_PORT = "publicendport"; + public static final String PUBLIC_ZONE = "publiczone"; + public static final String RECEIVED_BYTES = "receivedbytes"; + public static final String REQUIRES_HVM = "requireshvm"; + public static final String RESOURCE_TYPE = "resourcetype"; + public static final String RESPONSE = "response"; + public static final String QUERY_FILTER = "queryfilter"; + public static final String SCHEDULE = "schedule"; + public static final String SCOPE = "scope"; + public static final String SECRET_KEY = "usersecretkey"; + public static final String SINCE = "since"; + public static final String KEY = "key"; + public static final String SEARCH_BASE = "searchbase"; + public static final String SECURITY_GROUP_IDS = "securitygroupids"; + public static final String SECURITY_GROUP_NAMES = "securitygroupnames"; + public static final String SECURITY_GROUP_NAME = "securitygroupname"; + public static final String SECURITY_GROUP_ID = "securitygroupid"; + public static final String SENT = "sent"; + public static final String SENT_BYTES = "sentbytes"; + public static final String SERVICE_OFFERING_ID = "serviceofferingid"; + public static final String SHOW_CAPACITIES = "showcapacities"; + public static final String SIZE = "size"; + public static final String SNAPSHOT_ID = "snapshotid"; + public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid"; + public static final String SNAPSHOT_TYPE = "snapshottype"; + public static final String SOURCE_ZONE_ID = "sourcezoneid"; + public static final String START_DATE = "startdate"; + public static final String START_IP = "startip"; + public static final String START_IPV6 = "startipv6"; + public static final String START_PORT = "startport"; + public static final String STATE = "state"; + public static final String STATUS = "status"; + public static final String STORAGE_TYPE = "storagetype"; + public static final String SYSTEM_VM_TYPE = "systemvmtype"; + public static final String TAGS = "tags"; + public static final String TARGET_IQN = "targetiqn"; + public static final String TEMPLATE_FILTER = "templatefilter"; + public static final String TEMPLATE_ID = "templateid"; + public static final String ISO_ID = "isoid"; + public static final String TIMEOUT = "timeout"; + public static final String TIMEZONE = "timezone"; + public static final String TYPE = "type"; + public static final String TRUST_STORE = "truststore"; + public static final String TRUST_STORE_PASSWORD = "truststorepass"; + public static final String URL = "url"; + public static final String USAGE_INTERFACE = "usageinterface"; + public static final String USER_DATA = "userdata"; + public static final String USER_ID = "userid"; + public static final String USE_SSL = "ssl"; + public static final String USERNAME = "username"; + public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; + public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork"; + public static final String VALUE = "value"; + public static final String VIRTUAL_MACHINE_ID = "virtualmachineid"; + public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids"; + public static final String VLAN = "vlan"; + public static final String VLAN_ID = "vlanid"; + public static final String VM_AVAILABLE = "vmavailable"; + public static final String VM_LIMIT = "vmlimit"; + public static final String VM_TOTAL = "vmtotal"; + public static final String VNET = "vnet"; + public static final String VOLUME_ID = "volumeid"; + public static final String ZONE_ID = "zoneid"; + public static final String ZONE_NAME = "zonename"; + public static final String NETWORK_TYPE = "networktype"; + public static final String PAGE = "page"; + public static final String PAGE_SIZE = "pagesize"; + public static final String COUNT = "count"; + public static final String TRAFFIC_TYPE = "traffictype"; + public static final String NETWORK_OFFERING_ID = "networkofferingid"; + public static final String NETWORK_IDS = "networkids"; + public static final String NETWORK_ID = "networkid"; + public static final String NIC_ID = "nicid"; + public static final String SPECIFY_VLAN = "specifyvlan"; + public static final String IS_DEFAULT = "isdefault"; + public static final String IS_SYSTEM = "issystem"; + public static final String AVAILABILITY = "availability"; + public static final String NETWORKRATE = "networkrate"; + public static final String HOST_TAGS = "hosttags"; + public static final String SSH_KEYPAIR = "keypair"; + public static final String HOST_CPU_CAPACITY = "hostcpucapacity"; + public static final String HOST_CPU_NUM = "hostcpunum"; + public static final String HOST_MEM_CAPACITY = "hostmemcapacity"; + public static final String HOST_MAC = "hostmac"; + public static final String HOST_TAG = "hosttag"; + public static final String PXE_SERVER_TYPE = "pxeservertype"; + public static final String LINMIN_USERNAME = "linminusername"; + public static final String LINMIN_PASSWORD = "linminpassword"; + public static final String LINMIN_APID = "linminapid"; + public static final String DHCP_SERVER_TYPE = "dhcpservertype"; + public static final String LINK_LOCAL_IP = "linklocalip"; + public static final String LINK_LOCAL_MAC_ADDRESS = "linklocalmacaddress"; + public static final String LINK_LOCAL_MAC_NETMASK = "linklocalnetmask"; + public static final String LINK_LOCAL_NETWORK_ID = "linklocalnetworkid"; + public static final String PRIVATE_MAC_ADDRESS = "privatemacaddress"; + public static final String PRIVATE_NETMASK = "privatenetmask"; + public static final String PRIVATE_NETWORK_ID = "privatenetworkid"; + public static final String ALLOCATION_STATE = "allocationstate"; + public static final String MANAGED_STATE = "managedstate"; + public static final String STORAGE_ID = "storageid"; + public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip"; + public static final String PING_DIR = "pingdir"; + public static final String TFTP_DIR = "tftpdir"; + public static final String PING_CIFS_USERNAME = "pingcifsusername"; + public static final String PING_CIFS_PASSWORD = "pingcifspassword"; + public static final String CHECKSUM = "checksum"; + public static final String NETWORK_DEVICE_TYPE = "networkdevicetype"; + public static final String NETWORK_DEVICE_PARAMETER_LIST = "networkdeviceparameterlist"; + public static final String ZONE_TOKEN = "zonetoken"; + public static final String DHCP_PROVIDER = "dhcpprovider"; + public static final String RESULT = "success"; + public static final String LUN_ID = "lunId"; + public static final String IQN = "iqn"; + public static final String AGGREGATE_NAME = "aggregatename"; + public static final String POOL_NAME = "poolname"; + public static final String VOLUME_NAME = "volumename"; + public static final String SNAPSHOT_POLICY = "snapshotpolicy"; + public static final String SNAPSHOT_RESERVATION = "snapshotreservation"; + public static final String IP_NETWORK_LIST = "iptonetworklist"; + public static final String PARAM_LIST = "param"; + public static final String FOR_LOAD_BALANCING = "forloadbalancing"; + public static final String KEYBOARD = "keyboard"; + public static final String OPEN_FIREWALL = "openfirewall"; + public static final String TEMPLATE_TAG = "templatetag"; + public static final String HYPERVISOR_VERSION = "hypervisorversion"; + public static final String MAX_GUESTS_LIMIT = "maxguestslimit"; + public static final String PROJECT_ID = "projectid"; + public static final String PROJECT_IDS = "projectids"; + public static final String PROJECT = "project"; + public static final String ROLE = "role"; + public static final String USER = "user"; + public static final String ACTIVE_ONLY = "activeonly"; + public static final String TOKEN = "token"; + public static final String ACCEPT = "accept"; + public static final String SORT_KEY = "sortkey"; + public static final String ACCOUNT_DETAILS = "accountdetails"; + public static final String SERVICE_PROVIDER_LIST = "serviceproviderlist"; + public static final String SERVICE_CAPABILITY_LIST = "servicecapabilitylist"; + public static final String CAN_CHOOSE_SERVICE_CAPABILITY = "canchooseservicecapability"; + public static final String PROVIDER = "provider"; + public static final String NETWORK_SPEED = "networkspeed"; + public static final String BROADCAST_DOMAIN_RANGE = "broadcastdomainrange"; + public static final String ISOLATION_METHODS = "isolationmethods"; + public static final String PHYSICAL_NETWORK_ID = "physicalnetworkid"; + public static final String DEST_PHYSICAL_NETWORK_ID = "destinationphysicalnetworkid"; + public static final String ENABLED = "enabled"; + public static final String SERVICE_NAME = "servicename"; + public static final String DHCP_RANGE = "dhcprange"; + public static final String UUID = "uuid"; + public static final String SECURITY_GROUP_EANBLED = "securitygroupenabled"; + public static final String LOCAL_STORAGE_ENABLED = "localstorageenabled"; + public static final String GUEST_IP_TYPE = "guestiptype"; + public static final String XEN_NETWORK_LABEL = "xennetworklabel"; + public static final String KVM_NETWORK_LABEL = "kvmnetworklabel"; + public static final String VMWARE_NETWORK_LABEL = "vmwarenetworklabel"; + public static final String NETWORK_SERVICE_PROVIDER_ID = "nspid"; + public static final String SERVICE_LIST = "servicelist"; + public static final String CAN_ENABLE_INDIVIDUAL_SERVICE = "canenableindividualservice"; + public static final String SUPPORTED_SERVICES = "supportedservices"; + public static final String NSP_ID = "nspid"; + public static final String ACL_TYPE = "acltype"; + public static final String SUBDOMAIN_ACCESS = "subdomainaccess"; + public static final String LOAD_BALANCER_DEVICE_ID = "lbdeviceid"; + public static final String LOAD_BALANCER_DEVICE_NAME = "lbdevicename"; + public static final String LOAD_BALANCER_DEVICE_STATE = "lbdevicestate"; + public static final String LOAD_BALANCER_DEVICE_CAPACITY = "lbdevicecapacity"; + public static final String LOAD_BALANCER_DEVICE_DEDICATED = "lbdevicededicated"; + public static final String FIREWALL_DEVICE_ID = "fwdeviceid"; + public static final String FIREWALL_DEVICE_NAME = "fwdevicename"; + public static final String FIREWALL_DEVICE_STATE = "fwdevicestate"; + public static final String FIREWALL_DEVICE_CAPACITY = "fwdevicecapacity"; + public static final String FIREWALL_DEVICE_DEDICATED = "fwdevicededicated"; + public static final String SERVICE = "service"; + public static final String ASSOCIATED_NETWORK_ID = "associatednetworkid"; + public static final String ASSOCIATED_NETWORK_NAME = "associatednetworkname"; + public static final String SOURCE_NAT_SUPPORTED = "sourcenatsupported"; + public static final String RESOURCE_STATE = "resourcestate"; + public static final String PROJECT_INVITE_REQUIRED = "projectinviterequired"; + public static final String REQUIRED = "required"; + public static final String RESTART_REQUIRED = "restartrequired"; + public static final String ALLOW_USER_CREATE_PROJECTS = "allowusercreateprojects"; + public static final String CONSERVE_MODE = "conservemode"; + public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor"; + public static final String KEYWORD = "keyword"; + public static final String LIST_ALL = "listall"; + public static final String SPECIFY_IP_RANGES = "specifyipranges"; + public static final String IS_SOURCE_NAT = "issourcenat"; + public static final String IS_STATIC_NAT = "isstaticnat"; + public static final String SORT_BY = "sortby"; + public static final String CHANGE_CIDR = "changecidr"; + public static final String PURPOSE = "purpose"; + public static final String IS_TAGGED = "istagged"; + public static final String INSTANCE_NAME = "instancename"; + public static final String START_VM = "startvm"; + public static final String HA_HOST = "hahost"; + public static final String CUSTOM_DISK_OFF_MAX_SIZE = "customdiskofferingmaxsize"; + public static final String DEFAULT_ZONE_ID = "defaultzoneid"; + public static final String GUID = "guid"; + + public static final String EXTERNAL_SWITCH_MGMT_DEVICE_ID = "vsmdeviceid"; + public static final String EXTERNAL_SWITCH_MGMT_DEVICE_NAME = "vsmdevicename"; + public static final String EXTERNAL_SWITCH_MGMT_DEVICE_STATE = "vsmdevicestate"; + // Would we need to have a capacity field for Cisco N1KV VSM? Max hosts managed by it perhaps? May remove this later. + public static final String EXTERNAL_SWITCH_MGMT_DEVICE_CAPACITY = "vsmdevicecapacity"; + public static final String CISCO_NEXUS_VSM_NAME = "vsmname"; + public static final String VSM_USERNAME = "vsmusername"; + public static final String VSM_PASSWORD = "vsmpassword"; + public static final String VSM_IPADDRESS = "vsmipaddress"; + public static final String VSM_MGMT_VLAN_ID = "vsmmgmtvlanid"; + public static final String VSM_PKT_VLAN_ID = "vsmpktvlanid"; + public static final String VSM_CTRL_VLAN_ID = "vsmctrlvlanid"; + public static final String VSM_STORAGE_VLAN_ID = "vsmstoragevlanid"; + public static final String VSM_DOMAIN_ID = "vsmdomainid"; + public static final String VSM_CONFIG_MODE = "vsmconfigmode"; + public static final String VSM_CONFIG_STATE = "vsmconfigstate"; + public static final String VSM_DEVICE_STATE = "vsmdevicestate"; + public static final String ADD_VSM_FLAG = "addvsmflag"; + public static final String END_POINT = "endpoint"; + public static final String REGION_ID = "regionid"; + public static final String IS_PROPAGATE = "ispropagate"; + public static final String VPC_OFF_ID = "vpcofferingid"; + public static final String NETWORK = "network"; + public static final String VPC_ID = "vpcid"; + public static final String GATEWAY_ID = "gatewayid"; + public static final String CAN_USE_FOR_DEPLOY = "canusefordeploy"; + public static final String RESOURCE_IDS = "resourceids"; + public static final String RESOURCE_ID = "resourceid"; + public static final String CUSTOMER = "customer"; + public static final String S2S_VPN_GATEWAY_ID = "s2svpngatewayid"; + public static final String S2S_CUSTOMER_GATEWAY_ID = "s2scustomergatewayid"; + public static final String IPSEC_PSK = "ipsecpsk"; + public static final String GUEST_IP = "guestip"; + public static final String REMOVED = "removed"; + public static final String IKE_POLICY = "ikepolicy"; + public static final String ESP_POLICY = "esppolicy"; + public static final String IKE_LIFETIME = "ikelifetime"; + public static final String ESP_LIFETIME = "esplifetime"; + public static final String DPD = "dpd"; + public static final String FOR_VPC = "forvpc"; + public static final String SHRINK_OK = "shrinkok"; + public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; + public static final String NICIRA_NVP_TRANSPORT_ZONE_UUID = "transportzoneuuid"; + public static final String NICIRA_NVP_DEVICE_NAME = "niciradevicename"; + public static final String NICIRA_NVP_GATEWAYSERVICE_UUID = "l3gatewayserviceuuid"; + public static final String S3_ACCESS_KEY = "accesskey"; + public static final String S3_SECRET_KEY = "secretkey"; + public static final String S3_END_POINT = "endpoint"; + public static final String S3_BUCKET_NAME = "bucket"; + public static final String S3_HTTPS_FLAG = "usehttps"; + public static final String S3_CONNECTION_TIMEOUT = "connectiontimeout"; + public static final String S3_MAX_ERROR_RETRY = "maxerrorretry"; + public static final String S3_SOCKET_TIMEOUT = "sockettimeout"; + public static final String INCL_ZONES = "includezones"; + public static final String EXCL_ZONES = "excludezones"; + public static final String SOURCE = "source"; + public static final String COUNTER_ID = "counterid"; + public static final String AGGR_OPERATOR = "aggroperator"; + public static final String AGGR_FUNCTION = "aggrfunction"; + public static final String AGGR_VALUE = "aggrvalue"; + public static final String THRESHOLD = "threshold"; + public static final String RELATIONAL_OPERATOR = "relationaloperator"; + public static final String OTHER_DEPLOY_PARAMS = "otherdeployparams"; + public static final String MIN_MEMBERS = "minmembers"; + public static final String MAX_MEMBERS = "maxmembers"; + public static final String AUTOSCALE_VM_DESTROY_TIME = "destroyvmgraceperiod"; + public static final String VMPROFILE_ID = "vmprofileid"; + public static final String VMGROUP_ID = "vmgroupid"; + public static final String CS_URL = "csurl"; + public static final String SCALEUP_POLICY_IDS = "scaleuppolicyids"; + public static final String SCALEDOWN_POLICY_IDS = "scaledownpolicyids"; + public static final String SCALEUP_POLICIES = "scaleuppolicies"; + public static final String SCALEDOWN_POLICIES = "scaledownpolicies"; + public static final String INTERVAL = "interval"; + public static final String QUIETTIME = "quiettime"; + public static final String ACTION = "action"; + public static final String CONDITION_ID = "conditionid"; + public static final String CONDITION_IDS = "conditionids"; + public static final String COUNTERPARAM_LIST = "counterparam"; + public static final String AUTOSCALE_USER_ID = "autoscaleuserid"; + public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername"; +<<<<<<< HEAD + public static final String UCS_DN = "ucsdn"; +======= + public static final String VM_SNAPSHOT_DESCRIPTION = "description"; + public static final String VM_SNAPSHOT_DISPLAYNAME = "name"; + public static final String VM_SNAPSHOT_ID = "vmsnapshotid"; + public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids"; + public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory"; +>>>>>>> CLOUDSTACK-684 Support VM Snapshot + + public enum HostDetails { + all, capacity, events, stats, min; + } + + public enum VMDetails { + all, group, nics, stats, secgrp, tmpl, servoff, iso, volume, min; + } + + public enum LDAPParams { + hostname, port, usessl, queryfilter, searchbase, dn, passwd, truststore, truststorepass; + + @Override + public String toString() { + return "ldap." + name(); + } + } + + +} diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index a9ac489671b..17f789f88fa 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -71,6 +71,7 @@ import com.cloud.user.ResourceLimitService; import com.cloud.utils.Pair; import com.cloud.vm.BareMetalVmService; import com.cloud.vm.UserVmService; +import com.cloud.vm.snapshot.VMSnapshotService; public abstract class BaseCmd { private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName()); @@ -128,6 +129,7 @@ public abstract class BaseCmd { @Inject public QueryService _queryService; @Inject public UsageService _usageService; @Inject public NetworkUsageService _networkUsageService; + @Inject public VMSnapshotService _vmSnapshotService; public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 0dc85de1647..267238af37b 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -88,6 +88,7 @@ import org.apache.cloudstack.api.response.TrafficTypeResponse; import org.apache.cloudstack.api.response.UsageRecordResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; import org.apache.cloudstack.api.response.VirtualRouterProviderResponse; import org.apache.cloudstack.api.response.VlanIpRangeResponse; import org.apache.cloudstack.api.response.VolumeResponse; @@ -163,6 +164,7 @@ import com.cloud.user.UserAccount; import com.cloud.uservm.UserVm; import com.cloud.vm.InstanceGroup; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.snapshot.VMSnapshot; public interface ResponseGenerator { UserResponse createUserResponse(UserAccount user); @@ -381,5 +383,6 @@ public interface ResponseGenerator { UsageRecordResponse createUsageResponse(Usage usageRecord); - TrafficMonitorResponse createTrafficMonitorResponse(Host trafficMonitor); + TrafficMonitorResponse createTrafficMonitorResponse(Host trafficMonitor); + VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java new file mode 100644 index 00000000000..f0dbf16b250 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java @@ -0,0 +1,125 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.user.vmsnapshot; + +import java.util.logging.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; +import com.cloud.vm.snapshot.VMSnapshot; + +@APICommand(name = "createVMSnapshot", description = "Creates snapshot for a vm.", responseObject = VMSnapshotResponse.class) +public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd { + + public static final Logger s_logger = Logger + .getLogger(CreateVMSnapshotCmd.class.getName()); + private static final String s_name = "createvmsnapshotresponse"; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, required = true, entityType=UserVmResponse.class, description = "The ID of the vm") + private Long vmId; + + @Parameter(name = ApiConstants.VM_SNAPSHOT_DESCRIPTION, type = CommandType.STRING, required = false, description = "The discription of the snapshot") + private String description; + + @Parameter(name = ApiConstants.VM_SNAPSHOT_DISPLAYNAME, type = CommandType.STRING, required = false, description = "The display name of the snapshot") + private String displayName; + + @Parameter(name = ApiConstants.VM_SNAPSHOT_MEMORY, type = CommandType.BOOLEAN, required = false, description = "snapshot memory if true") + private Boolean snapshotMemory; + + public Boolean snapshotMemory() { + if (snapshotMemory == null) { + return false; + } else { + return snapshotMemory; + } + } + + public String getDisplayName() { + return displayName; + } + + public String getDescription() { + return description; + } + + public Long getVmId() { + return vmId; + } + + @Override + public void create() throws ResourceAllocationException { + VMSnapshot vmsnapshot = _vmSnapshotService.allocVMSnapshot(getVmId(),getDisplayName(),getDescription(),snapshotMemory()); + if (vmsnapshot != null) { + this.setEntityId(vmsnapshot.getId()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to create vm snapshot"); + } + } + + @Override + public String getEventDescription() { + return "creating snapshot for VM: " + getVmId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_SNAPSHOT_CREATE; + } + + @Override + public void execute() { + UserContext.current().setEventDetails("VM Id: " + getVmId()); + VMSnapshot result = _vmSnapshotService.creatVMSnapshot(getVmId(),getEntityId()); + if (result != null) { + VMSnapshotResponse response = _responseGenerator + .createVMSnapshotResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException( + ApiErrorCode.INTERNAL_ERROR, + "Failed to create vm snapshot due to an internal error creating snapshot for vm " + + getVmId()); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + UserVm userVM = _userVmService.getUserVm(vmId); + return userVM.getAccountId(); + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java new file mode 100644 index 00000000000..a2b2c08b381 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/DeleteVMSnapshotCmd.java @@ -0,0 +1,85 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.user.vmsnapshot; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.vm.snapshot.VMSnapshot; + +@APICommand(name="deleteVMSnapshot", description = "Deletes a vmsnapshot.", responseObject = SuccessResponse.class) +public class DeleteVMSnapshotCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger + .getLogger(DeleteVMSnapshotCmd.class.getName()); + private static final String s_name = "deletevmsnapshotresponse"; + + @Parameter(name=ApiConstants.VM_SNAPSHOT_ID, type=CommandType.UUID, entityType=VMSnapshotResponse.class, + required=true, description="The ID of the VM snapshot") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + VMSnapshot vmSnapshot = _entityMgr.findById(VMSnapshot.class, getId()); + if (vmSnapshot != null) { + return vmSnapshot.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + UserContext.current().setEventDetails("vmsnapshot id: " + getId()); + boolean result = _vmSnapshotService.deleteVMSnapshot(getId()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vm snapshot"); + } + } + + @Override + public String getEventDescription() { + return "Delete VM snapshot: " + getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_SNAPSHOT_DELETE; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java new file mode 100644 index 00000000000..936d348950d --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/ListVMSnapshotCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.user.vmsnapshot; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; + +import com.cloud.vm.snapshot.VMSnapshot; + +@APICommand(name="listVMSnapshot", description = "List virtual machine snapshot by conditions", responseObject = VMSnapshotResponse.class) +public class ListVMSnapshotCmd extends BaseListTaggedResourcesCmd { + + private static final String s_name = "listvmsnapshotresponse"; + + @Parameter(name=ApiConstants.VM_SNAPSHOT_ID, type=CommandType.UUID, entityType=VMSnapshotResponse.class, + description="The ID of the VM snapshot") + private Long id; + + @Parameter(name=ApiConstants.STATE, type=CommandType.STRING, description="state of the virtual machine snapshot") + private String state; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType=UserVmResponse.class, description = "the ID of the vm") + private Long vmId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists snapshot by snapshot name or display name") + private String vmSnapshotName; + + public String getState() { + return state; + } + + public String getVmSnapshotName() { + return vmSnapshotName; + } + + public Long getVmId() { + return vmId; + } + + public Long getId() { + return id; + } + + @Override + public void execute() { + List result = _vmSnapshotService + .listVMSnapshots(this); + ListResponse response = new ListResponse(); + List snapshotResponses = new ArrayList(); + for (VMSnapshot r : result) { + VMSnapshotResponse vmSnapshotResponse = _responseGenerator + .createVMSnapshotResponse(r); + vmSnapshotResponse.setObjectName("vmSnapshot"); + snapshotResponses.add(vmSnapshotResponse); + } + response.setResponses(snapshotResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java new file mode 100644 index 00000000000..d7b4599d6c4 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java @@ -0,0 +1,92 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vmsnapshot; + +import java.util.logging.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; +import com.cloud.vm.snapshot.VMSnapshot; + +@APICommand(name = "revertToSnapshot",description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class) +public class RevertToSnapshotCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger + .getLogger(RevertToSnapshotCmd.class.getName()); + private static final String s_name = "reverttosnapshotresponse"; + + @Parameter(name = ApiConstants.VM_SNAPSHOT_ID, type = CommandType.UUID, required = true,entityType=VMSnapshotResponse.class,description = "The ID of the vm snapshot") + private Long vmSnapShotId; + + public Long getVmSnapShotId() { + return vmSnapShotId; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + VMSnapshot vmSnapshot = _entityMgr.findById(VMSnapshot.class, getVmSnapShotId()); + if (vmSnapshot != null) { + return vmSnapshot.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, ConcurrentOperationException { + UserContext.current().setEventDetails( + "vmsnapshot id: " + getVmSnapShotId()); + UserVm result = _vmSnapshotService.revertToSnapshot(getVmSnapShotId()); + if (result != null) { + UserVmResponse response = _responseGenerator.createUserVmResponse( + "virtualmachine", result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,"Failed to revert VM snapshot"); + } + } + + @Override + public String getEventDescription() { + return "Revert from VM snapshot: " + getVmSnapShotId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_SNAPSHOT_REVERT; + } + +} diff --git a/api/src/org/apache/cloudstack/api/response/VMSnapshotResponse.java b/api/src/org/apache/cloudstack/api/response/VMSnapshotResponse.java new file mode 100644 index 00000000000..3b30ab61a8f --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/VMSnapshotResponse.java @@ -0,0 +1,220 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.Date; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.serializer.Param; +import com.cloud.vm.snapshot.VMSnapshot; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value=VMSnapshot.class) +public class VMSnapshotResponse extends BaseResponse implements ControlledEntityResponse{ + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the vm snapshot") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the vm snapshot") + private String name; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the state of the vm snapshot") + private VMSnapshot.State state; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the vm snapshot") + private String description; + + @SerializedName(ApiConstants.DISPLAY_NAME) + @Param(description = "the display name of the vm snapshot") + private String displayName; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the Zone ID of the vm snapshot") + private String zoneId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "the vm ID of the vm snapshot") + private String virtualMachineid; + + @SerializedName("parent") + @Param(description = "the parent ID of the vm snapshot") + private String parent; + + @SerializedName("parentName") + @Param(description = "the parent displayName of the vm snapshot") + private String parentName; + + @SerializedName("current") + @Param(description = "indiates if this is current snapshot") + private Boolean current; + + @SerializedName("type") + @Param(description = "VM Snapshot type") + private String type; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "the create date of the vm snapshot") + private Date created; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account associated with the disk volume") + private String accountName; + + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id of the vpn") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="the project name of the vpn") + private String projectName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the ID of the domain associated with the disk volume") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain associated with the disk volume") + private String domainName; + + @Override + public String getObjectId() { + return getId(); + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getVirtualMachineid() { + return virtualMachineid; + } + + public void setVirtualMachineid(String virtualMachineid) { + this.virtualMachineid = virtualMachineid; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setState(VMSnapshot.State state) { + this.state = state; + } + + public VMSnapshot.State getState() { + return state; + } + + public Boolean getCurrent() { + return current; + } + + public void setCurrent(Boolean current) { + this.current = current; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public String getParentName() { + return parentName; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + + } + + @Override + public void setProjectId(String projectId) { + this.projectId = projectId; + + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + + } +} diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index 9c4fcb35554..11b1ca11114 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -1410,6 +1410,20 @@ label.zone.step.4.title=Step 4: Add an IP range label.zone.wide=Zone-Wide label.zone=Zone +#VM snapshot label +label.vmsnapshot=VM Snapshots +label.vmsnapshot.type=Type +label.vmsnapshot.parentname=Parent +label.vmsnapshot.current=isCurrent +label.vmsnapshot.memory=Snapshot memory +message.action.vmsnapshot.delete=Please confirm that you want to delete this VM snapshot. +label.action.vmsnapshot.delete=Delete VM snapshot +label.action.vmsnapshot.revert=Revert to VM snapshot +message.action.vmsnapshot.revert=Revert VM snapshot +label.action.vmsnapshot.create=Take VM Snapshot + + + #Messages message.acquire.public.ip=Please select a zone from which you want to acquire your new IP from. message.action.cancel.maintenance.mode=Please confirm that you want to cancel this maintenance. diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index d70649bf318..e1d0fb20731 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -537,3 +537,9 @@ addRegion=1 updateRegion=1 removeRegion=1 listRegions=15 + +### VM Snapshot commands +listVMSnapshot=15 +createVMSnapshot=15 +deleteVMSnapshot=15 +revertToSnapshot=15 diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java index f27e0269a64..a2e517d1fdb 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java @@ -20,15 +20,21 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; +import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; +import com.cloud.agent.api.DeleteVMSnapshotCommand; +import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; public interface VmwareStorageManager { Answer execute(VmwareHostService hostService, PrimaryStorageDownloadCommand cmd); - Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd); - Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd); - Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd); - Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd); - Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd); + Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd); + Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd); + Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd); + Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd); + Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd); + Answer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd); + Answer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd); + Answer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd); } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index 8650274719e..c7b7cd3bcba 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -22,6 +22,7 @@ import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.rmi.RemoteException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -32,18 +33,27 @@ import com.cloud.agent.api.BackupSnapshotAnswer; import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; +import com.cloud.agent.api.DeleteVMSnapshotAnswer; +import com.cloud.agent.api.DeleteVMSnapshotCommand; +import com.cloud.agent.api.RevertToVMSnapshotAnswer; +import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.VolumeTO; import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; import com.cloud.hypervisor.vmware.mo.DatacenterMO; import com.cloud.hypervisor.vmware.mo.DatastoreMO; +import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; +import com.cloud.hypervisor.vmware.mo.TaskMO; import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; import com.cloud.hypervisor.vmware.util.VmwareContext; @@ -57,7 +67,11 @@ import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.Ternary; import com.cloud.utils.script.Script; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.snapshot.VMSnapshot; import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.TaskEvent; +import com.vmware.vim25.TaskInfo; import com.vmware.vim25.VirtualDeviceConfigSpec; import com.vmware.vim25.VirtualDeviceConfigSpecOperation; import com.vmware.vim25.VirtualDisk; @@ -222,8 +236,12 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } } finally { - if(vmMo != null) - vmMo.removeAllSnapshots(); + if(vmMo != null){ + ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid); + if (snapshotMor != null){ + vmMo.removeSnapshot(snapshotUuid, false); + } + } try { if (workerVm != null) { @@ -377,47 +395,47 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } @Override - public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) { + public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) { - String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel(); - Long accountId = cmd.getAccountId(); - Long volumeId = cmd.getVolumeId(); - String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); - String backedUpSnapshotUuid = cmd.getSnapshotUuid(); + String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel(); + Long accountId = cmd.getAccountId(); + Long volumeId = cmd.getVolumeId(); + String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); + String backedUpSnapshotUuid = cmd.getSnapshotUuid(); - String details = null; - boolean success = false; - String newVolumeName = UUID.randomUUID().toString().replaceAll("-", ""); + String details = null; + boolean success = false; + String newVolumeName = UUID.randomUUID().toString().replaceAll("-", ""); - VmwareContext context = hostService.getServiceContext(cmd); - try { - VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); - - ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStorageNameLabel); - if (morPrimaryDs == null) { - String msg = "Unable to find datastore: " + primaryStorageNameLabel; - s_logger.error(msg); - throw new Exception(msg); - } + VmwareContext context = hostService.getServiceContext(cmd); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, + primaryStorageNameLabel); + if (morPrimaryDs == null) { + String msg = "Unable to find datastore: " + primaryStorageNameLabel; + s_logger.error(msg); + throw new Exception(msg); + } - DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs); - details = createVolumeFromSnapshot(hyperHost, primaryDsMo, - newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid); - if (details == null) { - success = true; - } - } catch (Throwable e) { - if (e instanceof RemoteException) { - hostService.invalidateServiceContext(context); - } + DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs); + details = createVolumeFromSnapshot(hyperHost, primaryDsMo, + newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid); + if (details == null) { + success = true; + } + } catch (Throwable e) { + if (e instanceof RemoteException) { + hostService.invalidateServiceContext(context); + } + + s_logger.error("Unexpecpted exception ", e); + details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e); + } - s_logger.error("Unexpecpted exception ", e); - details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e); - } + return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName); + } - return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName); - } - // templateName: name in secondary storage // templateUuid: will be used at hypervisor layer private void copyTemplateFromSecondaryToPrimary(VmwareHypervisorHost hyperHost, DatastoreMO datastoreMo, String secondaryStorageUrl, @@ -881,4 +899,244 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { private static String getSnapshotRelativeDirInSecStorage(long accountId, long volumeId) { return "snapshots/" + accountId + "/" + volumeId; } + + @Override + public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) { + List volumeTOs = cmd.getVolumeTOs(); + String vmName = cmd.getVmName(); + String vmSnapshotName = cmd.getTarget().getSnapshotName(); + String vmSnapshotDesc = cmd.getTarget().getDescription(); + boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory; + VirtualMachineMO vmMo = null; + VmwareContext context = hostService.getServiceContext(cmd); + Map mapNewDisk = new HashMap(); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + + // wait if there are already VM snapshot task running + ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager(); + ManagedObjectReference[] tasks = (ManagedObjectReference[]) context.getServiceUtil().getDynamicProperty(taskmgr, "recentTask"); + for (ManagedObjectReference taskMor : tasks) { + TaskInfo info = (TaskInfo) (context.getServiceUtil().getDynamicProperty(taskMor, "info")); + if(info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("CreateSnapshot_Task")){ + s_logger.debug("There is already a VM snapshot task running, wait for it"); + context.getServiceUtil().waitForTask(taskMor); + } + } + + vmMo = hyperHost.findVmOnHyperHost(vmName); + if(vmMo == null) + vmMo = hyperHost.findVmOnPeerHyperHost(vmName); + if (vmMo == null) { + String msg = "Unable to find VM for CreateVMSnapshotCommand"; + s_logger.debug(msg); + return new CreateVMSnapshotAnswer(cmd, false, msg); + } else { + if (vmMo.getSnapshotMor(vmSnapshotName) != null){ + s_logger.debug("VM snapshot " + vmSnapshotName + " already exists"); + }else if (!vmMo.createSnapshot(vmSnapshotName, vmSnapshotDesc, snapshotMemory, true)) { + return new CreateVMSnapshotAnswer(cmd, false, + "Unable to create snapshot due to esxi internal failed"); + } + // find VM disk file path after creating snapshot + VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); + for (int i = 0; i < vdisks.length; i ++){ + @SuppressWarnings("deprecation") + List> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false); + for(Pair fileItem : vmdkFiles) { + String vmdkName = fileItem.first().split(" ")[1]; + if ( vmdkName.endsWith(".vmdk")){ + vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); + } + String[] s = vmdkName.split("-"); + mapNewDisk.put(s[0], vmdkName); + } + } + + // update volume path using maps + for (VolumeTO volumeTO : volumeTOs) { + String parentUUID = volumeTO.getPath(); + String[] s = parentUUID.split("-"); + String key = s[0]; + volumeTO.setPath(mapNewDisk.get(key)); + } + return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs); + } + } catch (Exception e) { + String msg = e.getMessage(); + s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg); + try { + if (vmMo.getSnapshotMor(vmSnapshotName) != null) { + vmMo.removeSnapshot(vmSnapshotName, false); + } + } catch (Exception e1) { + } + return new CreateVMSnapshotAnswer(cmd, false, e.getMessage()); + } + } + + @Override + public DeleteVMSnapshotAnswer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd) { + List listVolumeTo = cmd.getVolumeTOs(); + VirtualMachineMO vmMo = null; + VmwareContext context = hostService.getServiceContext(cmd); + Map mapNewDisk = new HashMap(); + String vmName = cmd.getVmName(); + String vmSnapshotName = cmd.getTarget().getSnapshotName(); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + vmMo = hyperHost.findVmOnHyperHost(vmName); + if(vmMo == null) + vmMo = hyperHost.findVmOnPeerHyperHost(vmName); + if (vmMo == null) { + String msg = "Unable to find VM for RevertToVMSnapshotCommand"; + s_logger.debug(msg); + return new DeleteVMSnapshotAnswer(cmd, false, msg); + } else { + if (vmMo.getSnapshotMor(vmSnapshotName) == null) { + s_logger.debug("can not find the snapshot " + vmSnapshotName + ", assume it is already removed"); + } else { + if (!vmMo.removeSnapshot(vmSnapshotName, false)) { + String msg = "delete vm snapshot " + vmSnapshotName + " due to error occured in vmware"; + s_logger.error(msg); + return new DeleteVMSnapshotAnswer(cmd, false, msg); + } + } + s_logger.debug("snapshot: " + vmSnapshotName + " is removed"); + // after removed snapshot, the volumes' paths have been changed for the VM, needs to report new paths to manager + VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); + for (int i = 0; i < vdisks.length; i++) { + @SuppressWarnings("deprecation") + List> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false); + for (Pair fileItem : vmdkFiles) { + String vmdkName = fileItem.first().split(" ")[1]; + if (vmdkName.endsWith(".vmdk")) { + vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); + } + String[] s = vmdkName.split("-"); + mapNewDisk.put(s[0], vmdkName); + } + } + for (VolumeTO volumeTo : listVolumeTo) { + String key = null; + String parentUUID = volumeTo.getPath(); + String[] s = parentUUID.split("-"); + key = s[0]; + volumeTo.setPath(mapNewDisk.get(key)); + } + return new DeleteVMSnapshotAnswer(cmd, listVolumeTo); + } + } catch (Exception e) { + String msg = e.getMessage(); + s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg); + return new DeleteVMSnapshotAnswer(cmd, false, msg); + } + } + + @Override + public RevertToVMSnapshotAnswer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd) { + String snapshotName = cmd.getTarget().getSnapshotName(); + String vmName = cmd.getVmName(); + Boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory; + List listVolumeTo = cmd.getVolumeTOs(); + VirtualMachine.State vmState = VirtualMachine.State.Running; + VirtualMachineMO vmMo = null; + VmwareContext context = hostService.getServiceContext(cmd); + Map mapNewDisk = new HashMap(); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + + // wait if there are already VM revert task running + ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager(); + ManagedObjectReference[] tasks = (ManagedObjectReference[]) context.getServiceUtil().getDynamicProperty(taskmgr, "recentTask"); + for (ManagedObjectReference taskMor : tasks) { + TaskInfo info = (TaskInfo) (context.getServiceUtil().getDynamicProperty(taskMor, "info")); + if(info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("RevertToSnapshot_Task")){ + s_logger.debug("There is already a VM snapshot task running, wait for it"); + context.getServiceUtil().waitForTask(taskMor); + } + } + + HostMO hostMo = (HostMO) hyperHost; + vmMo = hyperHost.findVmOnHyperHost(vmName); + if(vmMo == null) + vmMo = hyperHost.findVmOnPeerHyperHost(vmName); + if (vmMo == null) { + String msg = "Unable to find VM for RevertToVMSnapshotCommand"; + s_logger.debug(msg); + return new RevertToVMSnapshotAnswer(cmd, false, msg); + } else { + boolean result = false; + if (snapshotName != null) { + ManagedObjectReference morSnapshot = vmMo.getSnapshotMor(snapshotName); + result = hostMo.revertToSnapshot(morSnapshot); + } else { + return new RevertToVMSnapshotAnswer(cmd, false, "Unable to find the snapshot by name " + snapshotName); + } + + if (result) { + VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); + // build a map + for (int i = 0; i < vdisks.length; i++) { + @SuppressWarnings("deprecation") + List> vmdkFiles = vmMo.getDiskDatastorePathChain( + vdisks[i], false); + for (Pair fileItem : vmdkFiles) { + String vmdkName = fileItem.first().split(" ")[1]; + if (vmdkName.endsWith(".vmdk")) { + vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); + } + String[] s = vmdkName.split("-"); + mapNewDisk.put(s[0], vmdkName); + } + } + String key = null; + for (VolumeTO volumeTo : listVolumeTo) { + String parentUUID = volumeTo.getPath(); + String[] s = parentUUID.split("-"); + key = s[0]; + volumeTo.setPath(mapNewDisk.get(key)); + } + if (!snapshotMemory) { + vmState = VirtualMachine.State.Stopped; + } + return new RevertToVMSnapshotAnswer(cmd, listVolumeTo, vmState); + } else { + return new RevertToVMSnapshotAnswer(cmd, false, + "Error while reverting to snapshot due to execute in esxi"); + } + } + } catch (Exception e) { + String msg = "revert vm " + vmName + " to snapshot " + snapshotName + " failed due to " + e.getMessage(); + s_logger.error(msg); + return new RevertToVMSnapshotAnswer(cmd, false, msg); + } + } + + + private VirtualMachineMO createWorkingVM(DatastoreMO dsMo, VmwareHypervisorHost hyperHost) throws Exception { + String uniqueName = UUID.randomUUID().toString(); + VirtualMachineMO workingVM = null; + VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); + vmConfig.setName(uniqueName); + vmConfig.setMemoryMB((long) 4); + vmConfig.setNumCPUs(1); + vmConfig.setGuestId(VirtualMachineGuestOsIdentifier._otherGuest.toString()); + VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); + fileInfo.setVmPathName(String.format("[%s]", dsMo.getName())); + vmConfig.setFiles(fileInfo); + + VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); + scsiController.setSharedBus(VirtualSCSISharing.noSharing); + scsiController.setBusNumber(0); + scsiController.setKey(1); + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec.setDevice(scsiController); + scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.add); + + vmConfig.setDeviceChange(new VirtualDeviceConfigSpec[] { scsiControllerSpec }); + hyperHost.createVm(vmConfig); + workingVM = hyperHost.findVmOnHyperHost(uniqueName); + return workingVM; + } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 841a535bbce..5cac253fc0c 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -65,9 +65,13 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.CreateStoragePoolCommand; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.DeleteStoragePoolCommand; +import com.cloud.agent.api.DeleteVMSnapshotAnswer; +import com.cloud.agent.api.DeleteVMSnapshotCommand; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.GetHostStatsAnswer; @@ -103,6 +107,8 @@ import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.RevertToVMSnapshotAnswer; +import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.SetupAnswer; import com.cloud.agent.api.SetupCommand; import com.cloud.agent.api.SetupGuestNetworkAnswer; @@ -445,7 +451,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((SetSourceNatCommand) cmd); } else if (clz == SetNetworkACLCommand.class) { answer = execute((SetNetworkACLCommand) cmd); - } else if (clz == SetPortForwardingRulesVpcCommand.class) { + } else if (cmd instanceof CreateVMSnapshotCommand) { + return execute((CreateVMSnapshotCommand)cmd); + } else if(cmd instanceof DeleteVMSnapshotCommand){ + return execute((DeleteVMSnapshotCommand)cmd); + } else if(cmd instanceof RevertToVMSnapshotCommand){ + return execute((RevertToVMSnapshotCommand)cmd); + }else if (clz == SetPortForwardingRulesVpcCommand.class) { answer = execute((SetPortForwardingRulesVpcCommand) cmd); } else if (clz == Site2SiteVpnCfgCommand.class) { answer = execute((Site2SiteVpnCfgCommand) cmd); @@ -2799,7 +2811,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // before we stop VM, remove all possible snapshots on the VM to let // disk chain be collapsed s_logger.info("Remove all snapshot before stopping VM " + cmd.getVmName()); - vmMo.removeAllSnapshots(); if (vmMo.safePowerOff(_shutdown_waitMs)) { state = State.Stopped; return new StopAnswer(cmd, "Stop VM " + cmd.getVmName() + " Succeed", 0, true); @@ -3351,7 +3362,42 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + protected Answer execute(CreateVMSnapshotCommand cmd) { + try { + VmwareContext context = getServiceContext(); + VmwareManager mgr = context + .getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + return mgr.getStorageManager().execute(this, cmd); + } catch (Exception e) { + e.printStackTrace(); + return new CreateVMSnapshotAnswer(cmd, false, ""); + } + } + + protected Answer execute(DeleteVMSnapshotCommand cmd) { + try { + VmwareContext context = getServiceContext(); + VmwareManager mgr = context + .getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + + return mgr.getStorageManager().execute(this, cmd); + } catch (Exception e) { + e.printStackTrace(); + return new DeleteVMSnapshotAnswer(cmd, false, ""); + } + } + + protected Answer execute(RevertToVMSnapshotCommand cmd){ + try{ + VmwareContext context = getServiceContext(); + VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + return mgr.getStorageManager().execute(this, cmd); + }catch (Exception e){ + e.printStackTrace(); + return new RevertToVMSnapshotAnswer(cmd,false,""); + } + } protected Answer execute(CreateVolumeFromSnapshotCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource CreateVolumeFromSnapshotCommand: " + _gson.toJson(cmd)); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index b3cb54f783e..22f4ba9cb80 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -90,9 +90,13 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.CreateStoragePoolCommand; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.DeleteStoragePoolCommand; +import com.cloud.agent.api.DeleteVMSnapshotAnswer; +import com.cloud.agent.api.DeleteVMSnapshotCommand; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.GetHostStatsAnswer; @@ -129,6 +133,8 @@ import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.RevertToVMSnapshotAnswer; +import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.SecurityGroupRuleAnswer; import com.cloud.agent.api.SecurityGroupRulesCmd; import com.cloud.agent.api.SetupAnswer; @@ -237,6 +243,7 @@ import com.cloud.utils.net.NetUtils; import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.snapshot.VMSnapshot; import com.trilead.ssh2.SCPClient; import com.xensource.xenapi.Bond; import com.xensource.xenapi.Connection; @@ -256,6 +263,10 @@ import com.xensource.xenapi.Types; import com.xensource.xenapi.Types.BadServerResponse; import com.xensource.xenapi.Types.ConsoleProtocol; import com.xensource.xenapi.Types.IpConfigurationMode; +import com.xensource.xenapi.Types.OperationNotAllowed; +import com.xensource.xenapi.Types.SrFull; +import com.xensource.xenapi.Types.VbdType; +import com.xensource.xenapi.Types.VmBadPowerState; import com.xensource.xenapi.Types.VmPowerState; import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VBD; @@ -579,11 +590,109 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((CheckS2SVpnConnectionsCommand) cmd); } else if (cmd instanceof StorageSubSystemCommand) { return this.storageResource.handleStorageCommands((StorageSubSystemCommand)cmd); + } else if (clazz == CreateVMSnapshotCommand.class) { + return execute((CreateVMSnapshotCommand)cmd); + } else if (clazz == DeleteVMSnapshotCommand.class) { + return execute((DeleteVMSnapshotCommand)cmd); + } else if (clazz == RevertToVMSnapshotCommand.class) { + return execute((RevertToVMSnapshotCommand)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } + + + private Answer execute(RevertToVMSnapshotCommand cmd) { + String vmName = cmd.getVmName(); + List listVolumeTo = cmd.getVolumeTOs(); + VMSnapshot.Type vmSnapshotType = cmd.getTarget().getType(); + Boolean snapshotMemory = vmSnapshotType == VMSnapshot.Type.DiskAndMemory; + Connection conn = getConnection(); + VirtualMachine.State vmState = null; + VM vm = null; + try { + + // remove vm from s_vms, for delta sync + s_vms.remove(_cluster, _name, vmName); + + Set vmSnapshots = VM.getByNameLabel(conn, cmd.getTarget().getSnapshotName()); + if(vmSnapshots.size() == 0) + return new RevertToVMSnapshotAnswer(cmd, false, "Cannot find vmSnapshot with name: " + cmd.getTarget().getSnapshotName()); + + VM vmSnapshot = vmSnapshots.iterator().next(); + + // find target VM or creating a work VM + try { + vm = getVM(conn, vmName); + } catch (Exception e) { + vm = createWorkingVM(conn, vmName, cmd.getGuestOSType(), listVolumeTo); + } + + if (vm == null) { + return new RevertToVMSnapshotAnswer(cmd, false, + "Revert to VM Snapshot Failed due to can not find vm: " + vmName); + } + + // call plugin to execute revert + revertToSnapshot(conn, vmSnapshot, vmName, vm.getUuid(conn), snapshotMemory, _host.uuid); + vm = getVM(conn, vmName); + Set vbds = vm.getVBDs(conn); + Map vdiMap = new HashMap(); + // get vdi:vbdr to a map + for (VBD vbd : vbds) { + VBD.Record vbdr = vbd.getRecord(conn); + if (vbdr.type == Types.VbdType.DISK) { + VDI vdi = vbdr.VDI; + vdiMap.put(vbdr.userdevice, vdi); + } + } + + if (!snapshotMemory) { + vm.destroy(conn); + vmState = VirtualMachine.State.Stopped; + } else { + s_vms.put(_cluster, _name, vmName, State.Running); + vmState = VirtualMachine.State.Running; + } + + // after revert, VM's volumes path have been changed, need to report to manager + for (VolumeTO volumeTo : listVolumeTo) { + Long deviceId = volumeTo.getDeviceId(); + VDI vdi = vdiMap.get(deviceId.toString()); + volumeTo.setPath(vdi.getUuid(conn)); + } + + return new RevertToVMSnapshotAnswer(cmd, listVolumeTo,vmState); + } catch (Exception e) { + s_logger.error("revert vm " + vmName + + " to snapshot " + cmd.getTarget().getSnapshotName() + " failed due to " + e.getMessage()); + return new RevertToVMSnapshotAnswer(cmd, false, e.getMessage()); + } + } + + private String revertToSnapshot(Connection conn, VM vmSnapshot, + String vmName, String oldVmUuid, Boolean snapshotMemory, String hostUUID) + throws XenAPIException, XmlRpcException { + + String results = callHostPluginAsync(conn, "vmopsSnapshot", + "revert_memory_snapshot", 10 * 60 * 1000, "snapshotUUID", + vmSnapshot.getUuid(conn), "vmName", vmName, "oldVmUuid", + oldVmUuid, "snapshotMemory", snapshotMemory.toString(), "hostUUID", hostUUID); + String errMsg = null; + if (results == null || results.isEmpty()) { + errMsg = "revert_memory_snapshot return null"; + } else { + if (results.equals("0")) { + return results; + } else { + errMsg = "revert_memory_snapshot exception"; + } + } + s_logger.warn(errMsg); + throw new CloudRuntimeException(errMsg); + } + protected XsLocalNetwork getNativeNetworkForTraffic(Connection conn, TrafficType type, String name) throws XenAPIException, XmlRpcException { if (name != null) { if (s_logger.isDebugEnabled()) { @@ -6167,6 +6276,199 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } + protected Answer execute(final CreateVMSnapshotCommand cmd) { + String vmName = cmd.getVmName(); + String vmSnapshotName = cmd.getTarget().getSnapshotName(); + List listVolumeTo = cmd.getVolumeTOs(); + VirtualMachine.State vmState = cmd.getVmState(); + String guestOSType = cmd.getGuestOSType(); + + boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory; + long timeout = 600; + + Connection conn = getConnection(); + VM vm = null; + VM vmSnapshot = null; + boolean success = false; + + try { + // check if VM snapshot already exists + Set vmSnapshots = VM.getByNameLabel(conn, cmd.getTarget().getSnapshotName()); + if(vmSnapshots.size() > 0) + return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs()); + + // check if there is already a task for this VM snapshot + Task task = null; + Set tasks = Task.getByNameLabel(conn, "Async.VM.snapshot"); + tasks.addAll(Task.getByNameLabel(conn, "Async.VM.checkpoint")); + for (Task taskItem : tasks) { + if(taskItem.getOtherConfig(conn).containsKey("CS_VM_SNAPSHOT_KEY")){ + String vmSnapshotTaskName = taskItem.getOtherConfig(conn).get("CS_VM_SNAPSHOT_KEY"); + if(vmSnapshotTaskName != null && vmSnapshotTaskName.equals(cmd.getTarget().getSnapshotName())){ + task = taskItem; + } + } + } + + // create a new task if there is no existing task for this VM snapshot + if(task == null){ + try { + vm = getVM(conn, vmName); + } catch (Exception e) { + if (!snapshotMemory) { + vm = createWorkingVM(conn, vmName, guestOSType, listVolumeTo); + } + } + + if (vm == null) { + return new CreateVMSnapshotAnswer(cmd, false, + "Creating VM Snapshot Failed due to can not find vm: " + + vmName); + } + + // call Xenserver API + if (!snapshotMemory) { + task = vm.snapshotAsync(conn, vmSnapshotName); + } else { + Set vbds = vm.getVBDs(conn); + Pool pool = Pool.getByUuid(conn, _host.pool); + for (VBD vbd: vbds){ + VBD.Record vbdr = vbd.getRecord(conn); + if (vbdr.userdevice.equals("0")){ + VDI vdi = vbdr.VDI; + SR sr = vdi.getSR(conn); + // store memory image on the same SR with ROOT volume + pool.setSuspendImageSR(conn, sr); + } + } + task = vm.checkpointAsync(conn, vmSnapshotName); + } + task.addToOtherConfig(conn, "CS_VM_SNAPSHOT_KEY", vmSnapshotName); + } + + waitForTask(conn, task, 1000, timeout * 1000); + checkForSuccess(conn, task); + String result = task.getResult(conn); + + // extract VM snapshot ref from result + String ref = result.substring("".length(), result.length() - "".length()); + vmSnapshot = Types.toVM(ref); + + success = true; + return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs()); + } catch (Exception e) { + String msg = e.getMessage(); + s_logger.error("Creating VM Snapshot " + cmd.getTarget().getSnapshotName() + " failed due to: " + msg); + return new CreateVMSnapshotAnswer(cmd, false, msg); + } finally { + try { + if (!success) { + if (vmSnapshot != null) { + s_logger.debug("Delete exsisting VM Snapshot " + + vmSnapshotName + + " after making VolumeTO failed"); + Set vbds = vmSnapshot.getVBDs(conn); + for (VBD vbd : vbds) { + VBD.Record vbdr = vbd.getRecord(conn); + if (vbdr.type == VbdType.DISK) { + VDI vdi = vbdr.VDI; + vdi.destroy(conn); + } + } + vmSnapshot.destroy(conn); + } + } + if (vmState == VirtualMachine.State.Stopped) { + if (vm != null) { + vm.destroy(conn); + } + } + } catch (Exception e2) { + s_logger.error("delete snapshot error due to " + + e2.getMessage()); + } + } + } + + private VM createWorkingVM(Connection conn, String vmName, + String guestOSType, List listVolumeTo) + throws BadServerResponse, VmBadPowerState, SrFull, + OperationNotAllowed, XenAPIException, XmlRpcException { + String guestOsTypeName = getGuestOsType(guestOSType, false); + if (guestOsTypeName == null) { + String msg = " Hypervisor " + this.getClass().getName() + + " doesn't support guest OS type " + guestOSType + + ". you can choose 'Other install media' to run it as HVM"; + s_logger.warn(msg); + throw new CloudRuntimeException(msg); + } + VM template = getVM(conn, guestOsTypeName); + VM vm = template.createClone(conn, vmName); + vm.setIsATemplate(conn, false); + Map vdiMap = new HashMap(); + for (VolumeTO volume : listVolumeTo) { + String vdiUuid = volume.getPath(); + try { + VDI vdi = VDI.getByUuid(conn, vdiUuid); + vdiMap.put(vdi, volume); + } catch (Types.UuidInvalid e) { + s_logger.warn("Unable to find vdi by uuid: " + vdiUuid + + ", skip it"); + } + } + for (VDI vdi : vdiMap.keySet()) { + VolumeTO volumeTO = vdiMap.get(vdi); + VBD.Record vbdr = new VBD.Record(); + vbdr.VM = vm; + vbdr.VDI = vdi; + if (volumeTO.getType() == Volume.Type.ROOT) { + vbdr.bootable = true; + vbdr.unpluggable = false; + } else { + vbdr.bootable = false; + vbdr.unpluggable = true; + } + vbdr.userdevice = new Long(volumeTO.getDeviceId()).toString(); + vbdr.mode = Types.VbdMode.RW; + vbdr.type = Types.VbdType.DISK; + VBD.create(conn, vbdr); + } + return vm; + } + + protected Answer execute(final DeleteVMSnapshotCommand cmd) { + String snapshotName = cmd.getTarget().getSnapshotName(); + Connection conn = getConnection(); + + try { + List vdiList = new ArrayList(); + Set snapshots = VM.getByNameLabel(conn, snapshotName); + if(snapshots.size() == 0){ + s_logger.warn("VM snapshot with name " + snapshotName + " does not exist, assume it is already deleted"); + return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs()); + } + VM snapshot = snapshots.iterator().next(); + Set vbds = snapshot.getVBDs(conn); + for (VBD vbd : vbds) { + if (vbd.getType(conn) == VbdType.DISK) { + VDI vdi = vbd.getVDI(conn); + vdiList.add(vdi); + } + } + if(cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory) + vdiList.add(snapshot.getSuspendVDI(conn)); + snapshot.destroy(conn); + for (VDI vdi : vdiList) { + vdi.destroy(conn); + } + return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs()); + } catch (Exception e) { + s_logger.warn("Catch Exception: " + e.getClass().toString() + + " due to " + e.toString(), e); + return new DeleteVMSnapshotAnswer(cmd, false, e.getMessage()); + } + } + protected Answer execute(final AttachIsoCommand cmd) { Connection conn = getConnection(); boolean attach = cmd.isAttach(); diff --git a/scripts/vm/hypervisor/xenserver/vmopsSnapshot b/scripts/vm/hypervisor/xenserver/vmopsSnapshot index 39fe31c443d..6fb1b18c1df 100755 --- a/scripts/vm/hypervisor/xenserver/vmopsSnapshot +++ b/scripts/vm/hypervisor/xenserver/vmopsSnapshot @@ -556,6 +556,33 @@ def deleteSnapshotBackup(session, args): return "1" -if __name__ == "__main__": - XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir}) +@echo +def revert_memory_snapshot(session, args): + util.SMlog("Calling revert_memory_snapshot with " + str(args)) + vmName = args['vmName'] + snapshotUUID = args['snapshotUUID'] + oldVmUuid = args['oldVmUuid'] + snapshotMemory = args['snapshotMemory'] + hostUUID = args['hostUUID'] + try: + cmd = '''xe vbd-list vm-uuid=%s | grep 'vdi-uuid' | grep -v 'not in database' | sed -e 's/vdi-uuid ( RO)://g' ''' % oldVmUuid + vdiUuids = os.popen(cmd).read().split() + cmd2 = '''xe vm-param-get param-name=power-state uuid=''' + oldVmUuid + if os.popen(cmd2).read().split()[0] != 'halted': + os.system("xe vm-shutdown force=true vm=" + vmName) + os.system("xe vm-destroy uuid=" + oldVmUuid) + os.system("xe snapshot-revert snapshot-uuid=" + snapshotUUID) + if snapshotMemory == 'true': + os.system("xe vm-resume vm=" + vmName + " on=" + hostUUID) + for vdiUuid in vdiUuids: + os.system("xe vdi-destroy uuid=" + vdiUuid) + except OSError, (errno, strerror): + errMsg = "OSError while reverting vm " + vmName + " to snapshot " + snapshotUUID + " with errno: " + str(errno) + " and strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return "0" + +if __name__ == "__main__": + XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "revert_memory_snapshot":revert_memory_snapshot}) + diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 83132c6aa76..e6b1bf16a03 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -209,6 +209,8 @@ import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component public class ApiDBUtils { @@ -309,6 +311,7 @@ public class ApiDBUtils { static SnapshotPolicyDao _snapshotPolicyDao; static AsyncJobDao _asyncJobDao; static HostDetailsDao _hostDetailsDao; + static VMSnapshotDao _vmSnapshotDao; @Inject private ManagementServer ms; @Inject public AsyncJobManager asyncMgr; @@ -407,7 +410,7 @@ public class ApiDBUtils { @Inject private SnapshotPolicyDao snapshotPolicyDao; @Inject private AsyncJobDao asyncJobDao; @Inject private HostDetailsDao hostDetailsDao; - + @Inject private VMSnapshotDao vmSnapshotDao; @PostConstruct void init() { _ms = ms; @@ -505,7 +508,7 @@ public class ApiDBUtils { _snapshotPolicyDao = snapshotPolicyDao; _asyncJobDao = asyncJobDao; _hostDetailsDao = hostDetailsDao; - + _vmSnapshotDao = vmSnapshotDao; // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned _statsCollector = StatsCollector.getInstance(); } @@ -1054,6 +1057,11 @@ public class ApiDBUtils { return _networkModel.canUseForDeploy(network); } + public static VMSnapshot getVMSnapshotById(Long vmSnapshotId) { + VMSnapshot vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId); + return vmSnapshot; + } + public static String getUuid(String resourceId, TaggedResourceType resourceType) { return _taggedResourceService.getUuid(resourceId, resourceType); } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 8c9761520bb..a94e93568e2 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -93,7 +93,6 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; - import org.apache.cloudstack.api.response.S3Response; import org.springframework.stereotype.Component; @@ -195,6 +194,13 @@ import org.apache.cloudstack.region.Region; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.usage.UsageService; import org.apache.cloudstack.usage.UsageTypes; +import com.cloud.vm.VmStats; +import com.cloud.vm.dao.UserVmData; +import com.cloud.vm.dao.UserVmData.NicData; +import com.cloud.vm.dao.UserVmData.SecurityGroupData; +import com.cloud.vm.snapshot.VMSnapshot; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.VMSnapshotResponse; import org.apache.log4j.Logger; import java.text.DecimalFormat; @@ -358,6 +364,25 @@ public class ApiResponseHelper implements ResponseGenerator { return snapshotResponse; } + @Override + public VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot) { + VMSnapshotResponse vmSnapshotResponse = new VMSnapshotResponse(); + vmSnapshotResponse.setId(vmSnapshot.getUuid()); + vmSnapshotResponse.setName(vmSnapshot.getName()); + vmSnapshotResponse.setState(vmSnapshot.getState()); + vmSnapshotResponse.setCreated(vmSnapshot.getCreated()); + vmSnapshotResponse.setDescription(vmSnapshot.getDescription()); + vmSnapshotResponse.setDisplayName(vmSnapshot.getDisplayName()); + UserVm vm = ApiDBUtils.findUserVmById(vmSnapshot.getVmId()); + if(vm!=null) + vmSnapshotResponse.setVirtualMachineid(vm.getUuid()); + if(vmSnapshot.getParent() != null) + vmSnapshotResponse.setParentName(ApiDBUtils.getVMSnapshotById(vmSnapshot.getParent()).getDisplayName()); + vmSnapshotResponse.setCurrent(vmSnapshot.getCurrent()); + vmSnapshotResponse.setType(vmSnapshot.getType().toString()); + return vmSnapshotResponse; + } + @Override public SnapshotPolicyResponse createSnapshotPolicyResponse(SnapshotPolicy policy) { SnapshotPolicyResponse policyResponse = new SnapshotPolicyResponse(); @@ -3093,7 +3118,6 @@ public class ApiResponseHelper implements ResponseGenerator { response.setObjectName("vpnconnection"); return response; } - @Override public GuestOSResponse createGuestOSResponse(GuestOS guestOS) { GuestOSResponse response = new GuestOSResponse(); @@ -3133,7 +3157,6 @@ public class ApiResponseHelper implements ResponseGenerator { } - @Override public UsageRecordResponse createUsageResponse(Usage usageRecord) { UsageRecordResponse usageRecResponse = new UsageRecordResponse(); diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index d7b70535b06..4787c7bb37f 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -62,9 +62,11 @@ import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateSwiftVO; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.swift.SwiftManager; +import com.cloud.uservm.UserVm; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; @@ -78,7 +80,11 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component @Local(value = CapacityManager.class) @@ -110,7 +116,11 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, ConfigurationManager _configMgr; @Inject HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; - + @Inject + protected VMSnapshotDao _vmSnapshotDao; + @Inject + protected UserVmDao _userVMDao; + private int _vmCapacityReleaseInterval; private ScheduledExecutorService _executor; private boolean _stopped; @@ -437,12 +447,43 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, } + private long getVMSnapshotAllocatedCapacity(StoragePoolVO pool){ + List volumes = _volumeDao.findByPoolId(pool.getId()); + long totalSize = 0; + for (VolumeVO volume : volumes) { + if(volume.getInstanceId() == null) + continue; + Long vmId = volume.getInstanceId(); + UserVm vm = _userVMDao.findById(vmId); + if(vm == null) + continue; + ServiceOffering offering = _offeringsDao.findById(vm.getServiceOfferingId()); + List vmSnapshots = _vmSnapshotDao.findByVm(vmId); + long pathCount = 0; + long memorySnapshotSize = 0; + for (VMSnapshotVO vmSnapshotVO : vmSnapshots) { + if(_vmSnapshotDao.listByParent(vmSnapshotVO.getId()).size() == 0) + pathCount++; + if(vmSnapshotVO.getType() == VMSnapshot.Type.DiskAndMemory) + memorySnapshotSize += (offering.getRamSize() * 1024 * 1024); + } + if(pathCount <= 1) + totalSize = totalSize + memorySnapshotSize; + else + totalSize = totalSize + volume.getSize() * (pathCount - 1) + memorySnapshotSize; + } + return totalSize; + } + @Override public long getAllocatedPoolCapacity(StoragePoolVO pool, VMTemplateVO templateForVmCreation){ // Get size for all the volumes Pair sizes = _volumeDao.getCountAndTotalByPool(pool.getId()); long totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume; + + // Get size for VM Snapshots + totalAllocatedSize = totalAllocatedSize + getVMSnapshotAllocatedCapacity(pool); // Iterate through all templates on this storage pool boolean tmpinstalled = false; diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index b1a13083399..26499bcd4f2 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -33,6 +33,7 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.TemplateManager; import com.cloud.vm.UserVmManager; +import com.cloud.vm.snapshot.VMSnapshotManager; public enum Config { @@ -371,8 +372,16 @@ public enum Config { ApiLimitInterval("Advanced", ManagementServer.class, Integer.class, "api.throttling.interval", "1", "Time interval (in seconds) to reset API count", null), ApiLimitMax("Advanced", ManagementServer.class, Integer.class, "api.throttling.max", "25", "Max allowed number of APIs within fixed interval", null), - ApiLimitCacheSize("Advanced", ManagementServer.class, Integer.class, "api.throttling.cachesize", "50000", "Account based API count cache size", null); + ApiLimitCacheSize("Advanced", ManagementServer.class, Integer.class, "api.throttling.cachesize", "50000", "Account based API count cache size", null), + + // VMSnapshots + VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null), + VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "600", "In second, timeout for create vm snapshot", null), + VMSnapshotExpungeInterval("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.interval", "60", "The interval (in seconds) to wait before running the expunge thread.", null), + VMSnapshotExpungeWorkers("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.workers", "1", "Number of workers performing expunge ", null); + + private final String _category; private final Class _componentClass; private final Class _type; diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 0943bfc201e..d3812067118 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -99,6 +99,10 @@ import org.apache.cloudstack.api.command.user.tag.*; import org.apache.cloudstack.api.command.user.template.*; import org.apache.cloudstack.api.command.user.vm.*; import org.apache.cloudstack.api.command.user.vmgroup.*; +import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToSnapshotCmd; import org.apache.cloudstack.api.command.user.volume.*; import org.apache.cloudstack.api.command.user.vpc.*; import org.apache.cloudstack.api.command.user.vpn.*; @@ -2143,6 +2147,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ResetVpnConnectionCmd.class); cmdList.add(UpdateVpnCustomerGatewayCmd.class); cmdList.add(ListZonesByCmd.class); + cmdList.add(ListVMSnapshotCmd.class); + cmdList.add(CreateVMSnapshotCmd.class); + cmdList.add(RevertToSnapshotCmd.class); + cmdList.add(DeleteVMSnapshotCmd.class); return cmdList; } diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index c7beff08684..e06da75580c 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -104,6 +104,9 @@ import org.apache.log4j.Logger; import javax.ejb.Local; import javax.naming.ConfigurationException; import java.util.*; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component @Local(value = { SnapshotManager.class, SnapshotService.class }) @@ -167,7 +170,9 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, private ResourceTagDao _resourceTagDao; @Inject private ConfigurationDao _configDao; - + @Inject + private VMSnapshotDao _vmSnapshotDao; + String _name; private int _totalRetries; private int _pauseInterval; private int _deltaSnapshotMax; @@ -395,6 +400,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } // if volume is attached to a vm in destroyed or expunging state; disallow + // if volume is attached to a vm in taking vm snapshot; disallow if (volume.getInstanceId() != null) { UserVmVO userVm = _vmDao.findById(volume.getInstanceId()); if (userVm != null) { @@ -408,6 +414,12 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if(activeSnapshots.size() > 1) throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later"); } + List activeVMSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), + VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging); + if (activeVMSnapshots.size() > 0) { + throw new CloudRuntimeException( + "There is other active vm snapshot tasks on the instance to which the volume is attached, please try again later"); + } } } diff --git a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java index e44343dda8e..06756b617eb 100644 --- a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java @@ -73,6 +73,7 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.uuididentity.dao.IdentityDao; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component @@ -121,6 +122,8 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso VpcDao _vpcDao; @Inject StaticRouteDao _staticRouteDao; + @Inject + VMSnapshotDao _vmSnapshotDao; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -139,6 +142,7 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso _daoMap.put(TaggedResourceType.Vpc, _vpcDao); _daoMap.put(TaggedResourceType.NetworkACL, _firewallDao); _daoMap.put(TaggedResourceType.StaticRoute, _staticRouteDao); + _daoMap.put(TaggedResourceType.VMSnapshot, _vmSnapshotDao); return true; } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index d5c66ded91e..19887ff9e25 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -249,12 +249,37 @@ import com.cloud.utils.exception.ExecutionException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.*; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; +import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; +import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd; +import org.apache.cloudstack.api.command.user.vm.*; +import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; +import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; +import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.InstanceGroupVMMapDao; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotManager; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Local(value = { UserVmManager.class, UserVmService.class }) public class UserVmManagerImpl extends ManagerBase implements UserVmManager, UserVmService { @@ -392,7 +417,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use protected GuestOSCategoryDao _guestOSCategoryDao; @Inject UsageEventDao _usageEventDao; - + @Inject + protected VMSnapshotDao _vmSnapshotDao; + @Inject + protected VMSnapshotManager _vmSnapshotMgr; + protected ScheduledExecutorService _executor = null; protected int _expungeInterval; protected int _expungeDelay; @@ -813,7 +842,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // permission check _accountMgr.checkAccess(caller, null, true, volume, vm); - // Check if volume is stored on secondary Storage. + //check if vm has snapshot, if true: can't attache volume + boolean attach = true; + checkVMSnapshots(vm, volumeId, attach); + + // Check if volume is stored on secondary Storage. + //Check if volume is stored on secondary Storage. boolean isVolumeOnSec = false; VolumeHostVO volHostVO = _volumeHostDao.findByVolumeId(volume.getId()); if (volHostVO != null) { @@ -1106,8 +1140,19 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } + private void checkVMSnapshots(UserVmVO vm, Long volumeId, boolean attach) { + // Check that if vm has any VM snapshot + Long vmId = vm.getId(); + List listSnapshot = _vmSnapshotDao.listByInstanceId(vmId, + VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging); + if (listSnapshot != null && listSnapshot.size() != 0) { + throw new InvalidParameterValueException( + "The VM has VM snapshots, do not allowed to attach volume. Please delete the VM snapshots first."); + } + } + @Override - @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) + @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "event_detaching_volume1", async = true) public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { Account caller = UserContext.current().getCaller(); if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd @@ -1167,8 +1212,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use "Please specify a VM that is either running or stopped."); } - AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor - .getCurrentExecutor(); + // Check that if the volume has snapshot + boolean attach = false; + checkVMSnapshots(vm, volumeId, attach); + AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); if (asyncExecutor != null) { AsyncJobVO job = asyncExecutor.getJob(); @@ -1314,12 +1361,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use throw new InvalidParameterValueException( "unable to find a virtual machine with id " + vmId); } - + _accountMgr.checkAccess(caller, null, true, vmInstance); // Check that the specified service offering ID is valid _itMgr.checkIfCanUpgrade(vmInstance, svcOffId); + // remove diskAndMemory VM snapshots + List vmSnapshots = _vmSnapshotDao.findByVm(vmId); + for (VMSnapshotVO vmSnapshotVO : vmSnapshots) { + if(vmSnapshotVO.getType() == VMSnapshot.Type.DiskAndMemory){ + if(!_vmSnapshotMgr.deleteAllVMSnapshots(vmId, VMSnapshot.Type.DiskAndMemory)){ + String errMsg = "Failed to remove VM snapshot during upgrading, snapshot id " + vmSnapshotVO.getId(); + s_logger.debug(errMsg); + throw new CloudRuntimeException(errMsg); + } + + } + } + _itMgr.upgradeVmDb(vmId, svcOffId); return _vmDao.findById(vmInstance.getId()); @@ -1997,8 +2057,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } volume = _volsDao.findById(snapshot.getVolumeId()); - VolumeVO snapshotVolume = _volsDao - .findByIdIncludingRemoved(snapshot.getVolumeId()); // check permissions _accountMgr.checkAccess(caller, null, true, snapshot); diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 1838ed2fbfa..bb8f8f6abd6 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -158,6 +158,10 @@ import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotManager; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Local(value = VirtualMachineManager.class) public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, Listener { @@ -227,6 +231,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac protected HypervisorGuruManager _hvGuruMgr; @Inject protected NetworkDao _networkDao; + @Inject + protected VMSnapshotDao _vmSnapshotDao; @Inject protected List _planners; @@ -236,6 +242,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject protected ResourceManager _resourceMgr; + + @Inject + protected VMSnapshotManager _vmSnapshotMgr = null; @Inject protected ConfigurationDao _configDao; @@ -590,7 +599,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac ConcurrentOperationException, ResourceUnavailableException { return advanceStart(vm, params, caller, account, null); } - + @Override public T advanceStart(T vm, Map params, User caller, Account account, DeploymentPlan planToDeploy) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { @@ -1143,6 +1152,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId) throws NoTransitionException { + // if there are active vm snapshots task, state change is not allowed + if(_vmSnapshotMgr.hasActiveVMSnapshotTasks(vm.getId())){ + s_logger.error("State transit with event: " + e + " failed due to: " + vm.getInstanceName() + " has active VM snapshots tasks"); + return false; + } + State oldState = vm.getState(); if (oldState == State.Starting) { if (e == Event.OperationSucceeded) { @@ -1177,7 +1192,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Unable to stop " + vm); return false; } - + + if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(),null)){ + s_logger.debug("Unable to delete all snapshots for " + vm); + return false; + } + try { if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) { s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); @@ -1626,6 +1646,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Found " + vms.size() + " VMs for host " + hostId); for (VMInstanceVO vm : vms) { AgentVmInfo info = infos.remove(vm.getId()); + + // sync VM Snapshots related transient states + List vmSnapshotsInTrasientStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging,VMSnapshot.State.Reverting, VMSnapshot.State.Creating); + if(vmSnapshotsInTrasientStates.size() > 1){ + s_logger.info("Found vm " + vm.getInstanceName() + " with VM snapshots in transient states, needs to sync VM snapshot state"); + if(!_vmSnapshotMgr.syncVMSnapshot(vm, hostId)){ + s_logger.warn("Failed to sync VM in a transient snapshot related state: " + vm.getInstanceName()); + continue; + }else{ + s_logger.info("Successfully sync VM with transient snapshot: " + vm.getInstanceName()); + } + } + VMInstanceVO castedVm = null; if (info == null) { info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); @@ -1743,8 +1776,27 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac for (VMInstanceVO vm : set_vms) { AgentVmInfo info = infos.remove(vm.getId()); VMInstanceVO castedVm = null; - if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting)) - || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) + + // sync VM Snapshots related transient states + List vmSnapshotsInExpungingStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Creating,VMSnapshot.State.Reverting); + if(vmSnapshotsInExpungingStates.size() > 0){ + s_logger.info("Found vm " + vm.getInstanceName() + " in state. " + vm.getState() + ", needs to sync VM snapshot state"); + Long hostId = null; + Host host = null; + if(info != null && info.getHostUuid() != null){ + host = _hostDao.findByGuid(info.getHostUuid()); + } + hostId = host == null ? (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()) : host.getId(); + if(!_vmSnapshotMgr.syncVMSnapshot(vm, hostId)){ + s_logger.warn("Failed to sync VM with transient snapshot: " + vm.getInstanceName()); + continue; + }else{ + s_logger.info("Successfully sync VM with transient snapshot: " + vm.getInstanceName()); + } + } + + if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting )) + || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) { s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent"); info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); @@ -1785,7 +1837,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } else if (info != null && (vm.getState() == State.Stopped || vm.getState() == State.Stopping - || vm.isRemoved() || vm.getState() == State.Destroyed || vm.getState() == State.Expunging)) { + || vm.isRemoved() || vm.getState() == State.Destroyed || vm.getState() == State.Expunging )) { Host host = _hostDao.findByGuid(info.getHostUuid()); if (host != null){ s_logger.warn("Stopping a VM which is stopped/stopping/destroyed/expunging " + info.name); @@ -2260,6 +2312,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac Long clusterId = agent.getClusterId(); long agentId = agent.getId(); + if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen StartupRoutingCommand startup = (StartupRoutingCommand) cmd; HashMap> allStates = startup.getClusterVMStateChanges(); @@ -2375,7 +2428,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (newServiceOffering == null) { throw new InvalidParameterValueException("Unable to find a service offering with id " + newServiceOfferingId); } - + // Check that the VM is stopped if (!vmInstance.getState().equals(State.Stopped)) { s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState()); diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManager.java b/server/src/com/cloud/vm/snapshot/VMSnapshotManager.java new file mode 100644 index 00000000000..c60900551ee --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManager.java @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.vm.snapshot; + +import com.cloud.utils.component.Manager; +import com.cloud.vm.VMInstanceVO; + +public interface VMSnapshotManager extends VMSnapshotService, Manager { + public static final int VMSNAPSHOTMAX = 10; + + + /** + * Delete all VM snapshots belonging to one VM + * @param id, VM id + * @param type, + * @return true for success, false for failure + */ + boolean deleteAllVMSnapshots(long id, VMSnapshot.Type type); + + /** + * Sync VM snapshot state when VM snapshot in reverting or snapshoting or expunging state + * Used for fullsync after agent connects + * + * @param vm, the VM in question + * @param hostId + * @return true if succeeds, false if fails + */ + boolean syncVMSnapshot(VMInstanceVO vm, Long hostId); + + boolean hasActiveVMSnapshotTasks(Long vmId); + +} diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java new file mode 100644 index 00000000000..a0335634113 --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -0,0 +1,833 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.vm.snapshot; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.CreateVMSnapshotCommand; +import com.cloud.agent.api.DeleteVMSnapshotAnswer; +import com.cloud.agent.api.DeleteVMSnapshotCommand; +import com.cloud.agent.api.RevertToVMSnapshotAnswer; +import com.cloud.agent.api.RevertToVMSnapshotCommand; +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.projects.Project.ListProjectResourcesCriteria; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.StoragePoolVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.StoragePoolDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.UserContext; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.DateUtil; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Ternary; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; + +@Component +@Local(value = { VMSnapshotManager.class, VMSnapshotService.class }) +public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotManager, VMSnapshotService { + private static final Logger s_logger = Logger.getLogger(VMSnapshotManagerImpl.class); + String _name; + @Inject VMSnapshotDao _vmSnapshotDao; + @Inject VolumeDao _volumeDao; + @Inject AccountDao _accountDao; + @Inject VMInstanceDao _vmInstanceDao; + @Inject UserVmDao _userVMDao; + @Inject HostDao _hostDao; + @Inject UserDao _userDao; + @Inject AgentManager _agentMgr; + @Inject HypervisorGuruManager _hvGuruMgr; + @Inject AccountManager _accountMgr; + @Inject GuestOSDao _guestOSDao; + @Inject StoragePoolDao _storagePoolDao; + @Inject SnapshotDao _snapshotDao; + @Inject VirtualMachineManager _itMgr; + @Inject ConfigurationDao _configDao; + int _vmSnapshotMax; + StateMachine2 _vmSnapshottateMachine ; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + if (_configDao == null) { + throw new ConfigurationException( + "Unable to get the configuration dao."); + } + + _vmSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("vmsnapshot.max"), VMSNAPSHOTMAX); + + _vmSnapshottateMachine = VMSnapshot.State.getStateMachine(); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public List listVMSnapshots(ListVMSnapshotCmd cmd) { + Account caller = getCaller(); + List permittedAccounts = new ArrayList(); + + boolean listAll = cmd.listAll(); + Long id = cmd.getId(); + Long vmId = cmd.getVmId(); + + String state = cmd.getState(); + String keyword = cmd.getKeyword(); + String name = cmd.getVmSnapshotName(); + String accountName = cmd.getAccountName(); + + Ternary domainIdRecursiveListProject = new Ternary( + cmd.getDomainId(), cmd.isRecursive(), null); + _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, listAll, + false); + Long domainId = domainIdRecursiveListProject.first(); + Boolean isRecursive = domainIdRecursiveListProject.second(); + ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + + Filter searchFilter = new Filter(VMSnapshotVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal()); + SearchBuilder sb = _vmSnapshotDao.createSearchBuilder(); + _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); + + sb.and("vm_id", sb.entity().getVmId(), SearchCriteria.Op.EQ); + sb.and("domain_id", sb.entity().getDomainId(), SearchCriteria.Op.EQ); + sb.and("status", sb.entity().getState(), SearchCriteria.Op.IN); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("display_name", sb.entity().getDisplayName(), SearchCriteria.Op.EQ); + sb.and("account_id", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); + + if (accountName != null && cmd.getDomainId() != null) { + Account account = _accountMgr.getActiveAccountByName(accountName, cmd.getDomainId()); + sc.setParameters("account_id", account.getId()); + } + + if (vmId != null) { + sc.setParameters("vm_id", vmId); + } + + if (domainId != null) { + sc.setParameters("domain_id", domainId); + } + + if (state == null) { + VMSnapshot.State[] status = { VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Allocated, + VMSnapshot.State.Error, VMSnapshot.State.Expunging, VMSnapshot.State.Reverting }; + sc.setParameters("status", (Object[]) status); + } else { + sc.setParameters("state", state); + } + + if (name != null) { + sc.setParameters("display_name", name); + } + + if (keyword != null) { + SearchCriteria ssc = _vmSnapshotDao.createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("display_name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + sc.addAnd("name", SearchCriteria.Op.SC, ssc); + } + + if (id != null) { + sc.setParameters("id", id); + } + + return _vmSnapshotDao.search(sc, searchFilter); + + } + + protected Account getCaller(){ + return UserContext.current().getCaller(); + } + + @Override + public VMSnapshot allocVMSnapshot(Long vmId, String vsDisplayName, String vsDescription, Boolean snapshotMemory) + throws ResourceAllocationException { + + Account caller = getCaller(); + + // check if VM exists + UserVmVO userVmVo = _userVMDao.findById(vmId); + if (userVmVo == null) { + throw new InvalidParameterValueException("Creating VM snapshot failed due to VM:" + vmId + " is a system VM or does not exist"); + } + + // parameter length check + if(vsDisplayName != null && vsDisplayName.length()>255) + throw new InvalidParameterValueException("Creating VM snapshot failed due to length of VM snapshot vsDisplayName should not exceed 255"); + if(vsDescription != null && vsDescription.length()>255) + throw new InvalidParameterValueException("Creating VM snapshot failed due to length of VM snapshot vsDescription should not exceed 255"); + + // VM snapshot display name must be unique for a VM + String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT); + String vmSnapshotName = userVmVo.getInstanceName() + "_VS_" + timeString; + if (vsDisplayName == null) { + vsDisplayName = vmSnapshotName; + } + if(_vmSnapshotDao.findByName(vmId,vsDisplayName) != null){ + throw new InvalidParameterValueException("Creating VM snapshot failed due to VM snapshot with name" + vsDisplayName + " already exists"); + } + + // check VM state + if (userVmVo.getState() != VirtualMachine.State.Running && userVmVo.getState() != VirtualMachine.State.Stopped) { + throw new InvalidParameterValueException("Creating vm snapshot failed due to VM:" + vmId + " is not in the running or Stopped state"); + } + + if(snapshotMemory && userVmVo.getState() == VirtualMachine.State.Stopped){ + throw new InvalidParameterValueException("Can not snapshot memory when VM is in stopped state"); + } + + // for KVM, only allow snapshot with memory when VM is in running state + if(userVmVo.getHypervisorType() == HypervisorType.KVM && userVmVo.getState() == State.Running && !snapshotMemory){ + throw new InvalidParameterValueException("KVM VM does not allow to take a disk-only snapshot when VM is in running state"); + } + + // check access + _accountMgr.checkAccess(caller, null, true, userVmVo); + + // check max snapshot limit for per VM + if (_vmSnapshotDao.findByVm(vmId).size() >= _vmSnapshotMax) { + throw new CloudRuntimeException("Creating vm snapshot failed due to a VM can just have : " + _vmSnapshotMax + + " VM snapshots. Please delete old ones"); + } + + // check if there are active volume snapshots tasks + List listVolumes = _volumeDao.findByInstance(vmId); + for (VolumeVO volume : listVolumes) { + List activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.State.Creating, + Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp); + if (activeSnapshots.size() > 0) { + throw new CloudRuntimeException( + "There is other active volume snapshot tasks on the instance to which the volume is attached, please try again later."); + } + } + + // check if there are other active VM snapshot tasks + if (hasActiveVMSnapshotTasks(vmId)) { + throw new CloudRuntimeException("There is other active vm snapshot tasks on the instance, please try again later"); + } + + VMSnapshot.Type vmSnapshotType = VMSnapshot.Type.Disk; + if(snapshotMemory && userVmVo.getState() == VirtualMachine.State.Running) + vmSnapshotType = VMSnapshot.Type.DiskAndMemory; + + try { + VMSnapshotVO vmSnapshotVo = new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName, + vsDisplayName, userVmVo.getServiceOfferingId(), vmSnapshotType, null); + VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo); + if (vmSnapshot == null) { + throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId); + } + return vmSnapshot; + } catch (Exception e) { + String msg = e.getMessage(); + s_logger.error("Create vm snapshot record failed for vm: " + vmId + " due to: " + msg); + } + return null; + } + + @Override + public String getName() { + return _name; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_CREATE, eventDescription = "creating VM snapshot", async = true) + public VMSnapshot creatVMSnapshot(Long vmId, Long vmSnapshotId) { + UserVmVO userVm = _userVMDao.findById(vmId); + if (userVm == null) { + throw new InvalidParameterValueException("Create vm to snapshot failed due to vm: " + vmId + " is not found"); + } + VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId); + if(vmSnapshot == null){ + throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found"); + } + Long hostId = pickRunningHost(vmId); + try { + vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.CreateRequested); + } catch (NoTransitionException e) { + throw new CloudRuntimeException(e.getMessage()); + } + return createVmSnapshotInternal(userVm, vmSnapshot, hostId); + } + + protected VMSnapshot createVmSnapshotInternal(UserVmVO userVm, VMSnapshotVO vmSnapshot, Long hostId) { + try { + CreateVMSnapshotAnswer answer = null; + GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId()); + + // prepare snapshotVolumeTos + List volumeTOs = getVolumeTOList(userVm.getId()); + + // prepare target snapshotTO and its parent snapshot (current snapshot) + VMSnapshotTO current = null; + VMSnapshotVO currentSnapshot = _vmSnapshotDao.findCurrentSnapshotByVmId(userVm.getId()); + if (currentSnapshot != null) + current = getSnapshotWithParents(currentSnapshot); + VMSnapshotTO target = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), null, vmSnapshot.getDescription(), false, + current); + if (current == null) + vmSnapshot.setParent(null); + else + vmSnapshot.setParent(current.getId()); + + CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand(userVm.getInstanceName(),target ,volumeTOs, guestOS.getDisplayName(),userVm.getState()); + + answer = (CreateVMSnapshotAnswer) sendToPool(hostId, ccmd); + if (answer != null && answer.getResult()) { + processAnswer(vmSnapshot, userVm, answer, hostId); + s_logger.debug("Create vm snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName()); + }else{ + String errMsg = answer.getDetails(); + s_logger.error("Agent reports creating vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName() + " due to " + errMsg); + vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); + } + return vmSnapshot; + } catch (Exception e) { + if(e instanceof AgentUnavailableException){ + try { + vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); + } + } + String msg = e.getMessage(); + s_logger.error("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName() + " due to " + msg); + throw new CloudRuntimeException(msg); + } finally{ + if(vmSnapshot.getState() == VMSnapshot.State.Allocated){ + s_logger.warn("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName()); + _vmSnapshotDao.remove(vmSnapshot.getId()); + } + } + } + + protected List getVolumeTOList(Long vmId) { + List volumeTOs = new ArrayList(); + List volumeVos = _volumeDao.findByInstance(vmId); + + for (VolumeVO volume : volumeVos) { + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); + VolumeTO volumeTO = new VolumeTO(volume, pool); + volumeTOs.add(volumeTO); + } + return volumeTOs; + } + + // get snapshot and its parents recursively + private VMSnapshotTO getSnapshotWithParents(VMSnapshotVO snapshot) { + Map snapshotMap = new HashMap(); + List allSnapshots = _vmSnapshotDao.findByVm(snapshot.getVmId()); + for (VMSnapshotVO vmSnapshotVO : allSnapshots) { + snapshotMap.put(vmSnapshotVO.getId(), vmSnapshotVO); + } + + VMSnapshotTO currentTO = convert2VMSnapshotTO(snapshot); + VMSnapshotTO result = currentTO; + VMSnapshotVO current = snapshot; + while (current.getParent() != null) { + VMSnapshotVO parent = snapshotMap.get(current.getParent()); + currentTO.setParent(convert2VMSnapshotTO(parent)); + current = snapshotMap.get(current.getParent()); + currentTO = currentTO.getParent(); + } + return result; + } + + private VMSnapshotTO convert2VMSnapshotTO(VMSnapshotVO vo) { + return new VMSnapshotTO(vo.getId(), vo.getName(), vo.getType(), vo.getCreated().getTime(), vo.getDescription(), + vo.getCurrent(), null); + } + + protected boolean vmSnapshotStateTransitTo(VMSnapshotVO vsnp, VMSnapshot.Event event) throws NoTransitionException { + return _vmSnapshottateMachine.transitTo(vsnp, event, null, _vmSnapshotDao); + } + + @DB + protected void processAnswer(VMSnapshotVO vmSnapshot, UserVmVO userVm, Answer as, Long hostId) { + final Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + if (as instanceof CreateVMSnapshotAnswer) { + CreateVMSnapshotAnswer answer = (CreateVMSnapshotAnswer) as; + finalizeCreate(vmSnapshot, answer.getVolumeTOs()); + vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); + } else if (as instanceof RevertToVMSnapshotAnswer) { + RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) as; + finalizeRevert(vmSnapshot, answer.getVolumeTOs()); + vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); + } else if (as instanceof DeleteVMSnapshotAnswer) { + DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) as; + finalizeDelete(vmSnapshot, answer.getVolumeTOs()); + _vmSnapshotDao.remove(vmSnapshot.getId()); + } + txn.commit(); + } catch (Exception e) { + String errMsg = "Error while process answer: " + as.getClass() + " due to " + e.getMessage(); + s_logger.error(errMsg, e); + txn.rollback(); + throw new CloudRuntimeException(errMsg); + } finally { + txn.close(); + } + } + + protected void finalizeDelete(VMSnapshotVO vmSnapshot, List VolumeTOs) { + // update volumes path + updateVolumePath(VolumeTOs); + + // update children's parent snapshots + List children= _vmSnapshotDao.listByParent(vmSnapshot.getId()); + for (VMSnapshotVO child : children) { + child.setParent(vmSnapshot.getParent()); + _vmSnapshotDao.persist(child); + } + + // update current snapshot + VMSnapshotVO current = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId()); + if(current != null && current.getId() == vmSnapshot.getId() && vmSnapshot.getParent() != null){ + VMSnapshotVO parent = _vmSnapshotDao.findById(vmSnapshot.getParent()); + parent.setCurrent(true); + _vmSnapshotDao.persist(parent); + } + vmSnapshot.setCurrent(false); + _vmSnapshotDao.persist(vmSnapshot); + } + + protected void finalizeCreate(VMSnapshotVO vmSnapshot, List VolumeTOs) { + // update volumes path + updateVolumePath(VolumeTOs); + + vmSnapshot.setCurrent(true); + + // change current snapshot + if (vmSnapshot.getParent() != null) { + VMSnapshotVO previousCurrent = _vmSnapshotDao.findById(vmSnapshot.getParent()); + previousCurrent.setCurrent(false); + _vmSnapshotDao.persist(previousCurrent); + } + _vmSnapshotDao.persist(vmSnapshot); + } + + protected void finalizeRevert(VMSnapshotVO vmSnapshot, List volumeToList) { + // update volumes path + updateVolumePath(volumeToList); + + // update current snapshot, current snapshot is the one reverted to + VMSnapshotVO previousCurrent = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId()); + if(previousCurrent != null){ + previousCurrent.setCurrent(false); + _vmSnapshotDao.persist(previousCurrent); + } + vmSnapshot.setCurrent(true); + _vmSnapshotDao.persist(vmSnapshot); + } + + private void updateVolumePath(List volumeTOs) { + for (VolumeTO volume : volumeTOs) { + if (volume.getPath() != null) { + VolumeVO volumeVO = _volumeDao.findById(volume.getId()); + volumeVO.setPath(volume.getPath()); + _volumeDao.persist(volumeVO); + } + } + } + + public VMSnapshotManagerImpl() { + + } + + protected Answer sendToPool(Long hostId, Command cmd) throws AgentUnavailableException, OperationTimedoutException { + long targetHostId = _hvGuruMgr.getGuruProcessedCommandTargetHost(hostId, cmd); + Answer answer = _agentMgr.send(targetHostId, cmd); + return answer; + } + + @Override + public boolean hasActiveVMSnapshotTasks(Long vmId){ + List activeVMSnapshots = _vmSnapshotDao.listByInstanceId(vmId, + VMSnapshot.State.Creating, VMSnapshot.State.Expunging,VMSnapshot.State.Reverting,VMSnapshot.State.Allocated); + return activeVMSnapshots.size() > 0; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_DELETE, eventDescription = "delete vm snapshots", async=true) + public boolean deleteVMSnapshot(Long vmSnapshotId) { + Account caller = getCaller(); + + VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId); + if (vmSnapshot == null) { + throw new InvalidParameterValueException("unable to find the vm snapshot with id " + vmSnapshotId); + } + + _accountMgr.checkAccess(caller, null, true, vmSnapshot); + + // check VM snapshot states, only allow to delete vm snapshots in created and error state + if (VMSnapshot.State.Ready != vmSnapshot.getState() && VMSnapshot.State.Expunging != vmSnapshot.getState() && VMSnapshot.State.Error != vmSnapshot.getState()) { + throw new InvalidParameterValueException("Can't delete the vm snapshotshot " + vmSnapshotId + " due to it is not in Created or Error, or Expunging State"); + } + + // check if there are other active VM snapshot tasks + if (hasActiveVMSnapshotTasks(vmSnapshot.getVmId())) { + List expungingSnapshots = _vmSnapshotDao.listByInstanceId(vmSnapshot.getVmId(), VMSnapshot.State.Expunging); + if(expungingSnapshots.size() > 0 && expungingSnapshots.get(0).getId() == vmSnapshot.getId()) + s_logger.debug("Target VM snapshot already in expunging state, go on deleting it: " + vmSnapshot.getDisplayName()); + else + throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); + } + + if(vmSnapshot.getState() == VMSnapshot.State.Allocated){ + return _vmSnapshotDao.remove(vmSnapshot.getId()); + }else{ + return deleteSnapshotInternal(vmSnapshot); + } + } + + @DB + protected boolean deleteSnapshotInternal(VMSnapshotVO vmSnapshot) { + UserVmVO userVm = _userVMDao.findById(vmSnapshot.getVmId()); + + try { + vmSnapshotStateTransitTo(vmSnapshot,VMSnapshot.Event.ExpungeRequested); + Long hostId = pickRunningHost(vmSnapshot.getVmId()); + + // prepare snapshotVolumeTos + List volumeTOs = getVolumeTOList(vmSnapshot.getVmId()); + + // prepare DeleteVMSnapshotCommand + String vmInstanceName = userVm.getInstanceName(); + VMSnapshotTO parent = getSnapshotWithParents(vmSnapshot).getParent(); + VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), + vmSnapshot.getCreated().getTime(), vmSnapshot.getDescription(), vmSnapshot.getCurrent(), parent); + GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId()); + DeleteVMSnapshotCommand deleteSnapshotCommand = new DeleteVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs,guestOS.getDisplayName()); + + DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand); + + if (answer != null && answer.getResult()) { + processAnswer(vmSnapshot, userVm, answer, hostId); + s_logger.debug("Delete VM snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName()); + return true; + } else { + s_logger.error("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + answer.getDetails()); + return false; + } + } catch (Exception e) { + String msg = "Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage(); + s_logger.error(msg , e); + throw new CloudRuntimeException(e.getMessage()); + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_REVERT, eventDescription = "revert to VM snapshot", async = true) + public UserVm revertToSnapshot(Long vmSnapshotId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException { + + // check if VM snapshot exists in DB + VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId); + if (vmSnapshotVo == null) { + throw new InvalidParameterValueException( + "unable to find the vm snapshot with id " + vmSnapshotId); + } + Long vmId = vmSnapshotVo.getVmId(); + UserVmVO userVm = _userVMDao.findById(vmId); + // check if VM exists + if (userVm == null) { + throw new InvalidParameterValueException("Revert vm to snapshot: " + + vmSnapshotId + " failed due to vm: " + vmId + + " is not found"); + } + + // check if there are other active VM snapshot tasks + if (hasActiveVMSnapshotTasks(vmId)) { + throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); + } + + Account caller = getCaller(); + _accountMgr.checkAccess(caller, null, true, vmSnapshotVo); + + // VM should be in running or stopped states + if (userVm.getState() != VirtualMachine.State.Running + && userVm.getState() != VirtualMachine.State.Stopped) { + throw new InvalidParameterValueException( + "VM Snapshot reverting failed due to vm is not in the state of Running or Stopped."); + } + + // if snapshot is not created, error out + if (vmSnapshotVo.getState() != VMSnapshot.State.Ready) { + throw new InvalidParameterValueException( + "VM Snapshot reverting failed due to vm snapshot is not in the state of Created."); + } + + UserVO callerUser = _userDao.findById(UserContext.current().getCallerUserId()); + + UserVmVO vm = null; + Long hostId = null; + Account owner = _accountDao.findById(vmSnapshotVo.getAccountId()); + + // start or stop VM first, if revert from stopped state to running state, or from running to stopped + if(userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory){ + try { + vm = _itMgr.advanceStart(userVm, new HashMap(), callerUser, owner); + hostId = vm.getHostId(); + } catch (Exception e) { + s_logger.error("Start VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage()); + throw new CloudRuntimeException(e.getMessage()); + } + }else { + if(userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk){ + try { + _itMgr.advanceStop(userVm, true, callerUser, owner); + } catch (Exception e) { + s_logger.error("Stop VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage()); + throw new CloudRuntimeException(e.getMessage()); + } + } + hostId = pickRunningHost(userVm.getId()); + } + + if(hostId == null) + throw new CloudRuntimeException("Can not find any host to revert snapshot " + vmSnapshotVo.getName()); + + // check if there are other active VM snapshot tasks + if (hasActiveVMSnapshotTasks(userVm.getId())) { + throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); + } + + userVm = _userVMDao.findById(userVm.getId()); + try { + vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.RevertRequested); + } catch (NoTransitionException e) { + throw new CloudRuntimeException(e.getMessage()); + } + return revertInternal(userVm, vmSnapshotVo, hostId); + } + + private UserVm revertInternal(UserVmVO userVm, VMSnapshotVO vmSnapshotVo, Long hostId) { + try { + VMSnapshotVO snapshot = _vmSnapshotDao.findById(vmSnapshotVo.getId()); + // prepare RevertToSnapshotCommand + List volumeTOs = getVolumeTOList(userVm.getId()); + String vmInstanceName = userVm.getInstanceName(); + VMSnapshotTO parent = getSnapshotWithParents(snapshot).getParent(); + VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(), + snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent); + + GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId()); + RevertToVMSnapshotCommand revertToSnapshotCommand = new RevertToVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs, guestOS.getDisplayName()); + + RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) sendToPool(hostId, revertToSnapshotCommand); + if (answer != null && answer.getResult()) { + processAnswer(vmSnapshotVo, userVm, answer, hostId); + s_logger.debug("RevertTo " + vmSnapshotVo.getName() + " succeeded for vm: " + userVm.getInstanceName()); + } else { + String errMsg = "Revert VM: " + userVm.getInstanceName() + " to snapshot: "+ vmSnapshotVo.getName() + " failed"; + if(answer != null && answer.getDetails() != null) + errMsg = errMsg + " due to " + answer.getDetails(); + s_logger.error(errMsg); + // agent report revert operation fails + vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.OperationFailed); + throw new CloudRuntimeException(errMsg); + } + } catch (Exception e) { + if(e instanceof AgentUnavailableException){ + try { + vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); + } + } + // for other exceptions, do not change VM snapshot state, leave it for snapshotSync + String errMsg = "revert vm: " + userVm.getInstanceName() + " to snapshot " + vmSnapshotVo.getName() + " failed due to " + e.getMessage(); + s_logger.error(errMsg); + throw new CloudRuntimeException(e.getMessage()); + } + return userVm; + } + + + @Override + public VMSnapshot getVMSnapshotById(Long id) { + VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id); + return vmSnapshot; + } + + protected Long pickRunningHost(Long vmId) { + UserVmVO vm = _userVMDao.findById(vmId); + // use VM's host if VM is running + if(vm.getState() == State.Running) + return vm.getHostId(); + + // check if lastHostId is available + if(vm.getLastHostId() != null){ + HostVO lastHost = _hostDao.findById(vm.getLastHostId()); + if(lastHost.getStatus() == com.cloud.host.Status.Up && !lastHost.isInMaintenanceStates()) + return lastHost.getId(); + } + + List listVolumes = _volumeDao.findByInstance(vmId); + if (listVolumes == null || listVolumes.size() == 0) { + throw new InvalidParameterValueException("vmInstance has no volumes"); + } + VolumeVO volume = listVolumes.get(0); + Long poolId = volume.getPoolId(); + if (poolId == null) { + throw new InvalidParameterValueException("pool id is not found"); + } + StoragePoolVO storagePool = _storagePoolDao.findById(poolId); + if (storagePool == null) { + throw new InvalidParameterValueException("storage pool is not found"); + } + List listHost = _hostDao.listAllUpAndEnabledNonHAHosts(Host.Type.Routing, storagePool.getClusterId(), storagePool.getPodId(), + storagePool.getDataCenterId(), null); + if (listHost == null || listHost.size() == 0) { + throw new InvalidParameterValueException("no host in up state is found"); + } + return listHost.get(0).getId(); + } + + @Override + public VirtualMachine getVMBySnapshotId(Long id) { + VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id); + if(vmSnapshot == null){ + throw new InvalidParameterValueException("unable to find the vm snapshot with id " + id); + } + Long vmId = vmSnapshot.getVmId(); + UserVmVO vm = _userVMDao.findById(vmId); + return vm; + } + + @Override + public boolean deleteAllVMSnapshots(long vmId, VMSnapshot.Type type) { + boolean result = true; + List listVmSnapshots = _vmSnapshotDao.findByVm(vmId); + if (listVmSnapshots == null || listVmSnapshots.isEmpty()) { + return true; + } + for (VMSnapshotVO snapshot : listVmSnapshots) { + VMSnapshotVO target = _vmSnapshotDao.findById(snapshot.getId()); + if(type != null && target.getType() != type) + continue; + if (!deleteSnapshotInternal(target)) { + result = false; + break; + } + } + return result; + } + + @Override + public boolean syncVMSnapshot(VMInstanceVO vm, Long hostId) { + try{ + + UserVmVO userVm = _userVMDao.findById(vm.getId()); + if(userVm == null) + return false; + + List vmSnapshotsInExpungingStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Reverting, VMSnapshot.State.Creating); + for (VMSnapshotVO vmSnapshotVO : vmSnapshotsInExpungingStates) { + if(vmSnapshotVO.getState() == VMSnapshot.State.Expunging){ + return deleteSnapshotInternal(vmSnapshotVO); + }else if(vmSnapshotVO.getState() == VMSnapshot.State.Creating){ + return createVmSnapshotInternal(userVm, vmSnapshotVO, hostId) != null; + }else if(vmSnapshotVO.getState() == VMSnapshot.State.Reverting){ + return revertInternal(userVm, vmSnapshotVO, hostId) != null; + } + } + }catch (Exception e) { + s_logger.error(e.getMessage(),e); + if(_vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging).size() == 0) + return true; + else + return false; + } + return false; + } + +} diff --git a/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java new file mode 100644 index 00000000000..7532edf03f7 --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.vm.snapshot.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.fsm.StateDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; + +public interface VMSnapshotDao extends GenericDao, StateDao { + + List findByVm(Long vmId); + + List listExpungingSnapshot(); + + List listByInstanceId(Long vmId, VMSnapshot.State... status); + + VMSnapshotVO findCurrentSnapshotByVmId(Long vmId); + + List listByParent(Long vmSnapshotId); + + VMSnapshotVO findByName(Long vm_id, String name); +} diff --git a/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java new file mode 100644 index 00000000000..7d8ace7f82d --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java @@ -0,0 +1,161 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.vm.snapshot.dao; + +import java.util.Date; +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.UpdateBuilder; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshot.Event; +import com.cloud.vm.snapshot.VMSnapshot.State; +import com.cloud.vm.snapshot.VMSnapshotVO; +@Component +@Local(value = { VMSnapshotDao.class }) +public class VMSnapshotDaoImpl extends GenericDaoBase + implements VMSnapshotDao { + private static final Logger s_logger = Logger.getLogger(VMSnapshotDaoImpl.class); + private final SearchBuilder SnapshotSearch; + private final SearchBuilder ExpungingSnapshotSearch; + private final SearchBuilder SnapshotStatusSearch; + private final SearchBuilder AllFieldsSearch; + + protected VMSnapshotDaoImpl() { + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ); + AllFieldsSearch.and("vm_id", AllFieldsSearch.entity().getVmId(), Op.EQ); + AllFieldsSearch.and("deviceId", AllFieldsSearch.entity().getVmId(), Op.EQ); + AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ); + AllFieldsSearch.and("removed", AllFieldsSearch.entity().getState(), Op.EQ); + AllFieldsSearch.and("parent", AllFieldsSearch.entity().getParent(), Op.EQ); + AllFieldsSearch.and("current", AllFieldsSearch.entity().getCurrent(), Op.EQ); + AllFieldsSearch.and("vm_snapshot_type", AllFieldsSearch.entity().getType(), Op.EQ); + AllFieldsSearch.and("updatedCount", AllFieldsSearch.entity().getUpdatedCount(), Op.EQ); + AllFieldsSearch.and("display_name", AllFieldsSearch.entity().getDisplayName(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + + SnapshotSearch = createSearchBuilder(); + SnapshotSearch.and("vm_id", SnapshotSearch.entity().getVmId(), + SearchCriteria.Op.EQ); + SnapshotSearch.done(); + + ExpungingSnapshotSearch = createSearchBuilder(); + ExpungingSnapshotSearch.and("state", ExpungingSnapshotSearch.entity() + .getState(), SearchCriteria.Op.EQ); + ExpungingSnapshotSearch.and("removed", ExpungingSnapshotSearch.entity() + .getRemoved(), SearchCriteria.Op.NULL); + ExpungingSnapshotSearch.done(); + + SnapshotStatusSearch = createSearchBuilder(); + SnapshotStatusSearch.and("vm_id", SnapshotStatusSearch.entity() + .getVmId(), SearchCriteria.Op.EQ); + SnapshotStatusSearch.and("state", SnapshotStatusSearch.entity() + .getState(), SearchCriteria.Op.IN); + SnapshotStatusSearch.done(); + } + + @Override + public List findByVm(Long vmId) { + SearchCriteria sc = SnapshotSearch.create(); + sc.setParameters("vm_id", vmId); + return listBy(sc, null); + } + + @Override + public List listExpungingSnapshot() { + SearchCriteria sc = ExpungingSnapshotSearch.create(); + sc.setParameters("state", State.Expunging); + return listBy(sc, null); + } + + @Override + public List listByInstanceId(Long vmId, State... status) { + SearchCriteria sc = SnapshotStatusSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("state", (Object[]) status); + return listBy(sc, null); + } + + @Override + public VMSnapshotVO findCurrentSnapshotByVmId(Long vmId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("current", 1); + return findOneBy(sc); + } + + @Override + public List listByParent(Long vmSnapshotId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("parent", vmSnapshotId); + sc.setParameters("state", State.Ready ); + return listBy(sc, null); + } + + @Override + public VMSnapshotVO findByName(Long vm_id, String name) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vm_id", vm_id); + sc.setParameters("display_name", name ); + return null; + } + + @Override + public boolean updateState(State currentState, Event event, State nextState, VMSnapshot vo, Object data) { + + Long oldUpdated = vo.getUpdatedCount(); + Date oldUpdatedTime = vo.getUpdated(); + + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("id", vo.getId()); + sc.setParameters("state", currentState); + sc.setParameters("updatedCount", vo.getUpdatedCount()); + + vo.incrUpdatedCount(); + + UpdateBuilder builder = getUpdateBuilder(vo); + builder.set(vo, "state", nextState); + builder.set(vo, "updated", new Date()); + + int rows = update((VMSnapshotVO)vo, sc); + if (rows == 0 && s_logger.isDebugEnabled()) { + VMSnapshotVO dbVol = findByIdIncludingRemoved(vo.getId()); + if (dbVol != null) { + StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); + str.append(": DB Data={id=").append(dbVol.getId()).append("; state=").append(dbVol.getState()).append("; updatecount=").append(dbVol.getUpdatedCount()).append(";updatedTime=").append(dbVol.getUpdated()); + str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatecount=").append(vo.getUpdatedCount()).append("; updatedTime=").append(vo.getUpdated()); + str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatecount=").append(oldUpdated).append("; updatedTime=").append(oldUpdatedTime); + } else { + s_logger.debug("Unable to update VM snapshot: id=" + vo.getId() + ", as there is no such snapshot exists in the database anymore"); + } + } + return rows > 0; + } + +} diff --git a/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java new file mode 100644 index 00000000000..6fc6404e15f --- /dev/null +++ b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -0,0 +1,186 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.vm.snapshot; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.CreateVMSnapshotCommand; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.StoragePoolDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; + + + +public class VMSnapshotManagerTest { + @Spy VMSnapshotManagerImpl _vmSnapshotMgr = new VMSnapshotManagerImpl(); + @Mock Account admin; + @Mock VMSnapshotDao _vmSnapshotDao; + @Mock VolumeDao _volumeDao; + @Mock AccountDao _accountDao; + @Mock VMInstanceDao _vmInstanceDao; + @Mock UserVmDao _userVMDao; + @Mock HostDao _hostDao; + @Mock UserDao _userDao; + @Mock AgentManager _agentMgr; + @Mock HypervisorGuruManager _hvGuruMgr; + @Mock AccountManager _accountMgr; + @Mock GuestOSDao _guestOSDao; + @Mock StoragePoolDao _storagePoolDao; + @Mock SnapshotDao _snapshotDao; + @Mock VirtualMachineManager _itMgr; + @Mock ConfigurationDao _configDao; + int _vmSnapshotMax = 10; + + private static long TEST_VM_ID = 3L; + @Mock UserVmVO vmMock; + @Mock VolumeVO volumeMock; + + @Before + public void setup(){ + MockitoAnnotations.initMocks(this); + doReturn(admin).when(_vmSnapshotMgr).getCaller(); + _vmSnapshotMgr._accountDao = _accountDao; + _vmSnapshotMgr._userVMDao = _userVMDao; + _vmSnapshotMgr._vmSnapshotDao = _vmSnapshotDao; + _vmSnapshotMgr._volumeDao = _volumeDao; + _vmSnapshotMgr._accountMgr = _accountMgr; + _vmSnapshotMgr._snapshotDao = _snapshotDao; + _vmSnapshotMgr._guestOSDao = _guestOSDao; + + doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), + any(Boolean.class), any(ControlledEntity.class)); + + _vmSnapshotMgr._vmSnapshotMax = _vmSnapshotMax; + + when(_userVMDao.findById(anyLong())).thenReturn(vmMock); + when(_vmSnapshotDao.findByName(anyLong(), anyString())).thenReturn(null); + when(_vmSnapshotDao.findByVm(anyLong())).thenReturn(new ArrayList()); + + List mockVolumeList = new ArrayList(); + mockVolumeList.add(volumeMock); + when(volumeMock.getInstanceId()).thenReturn(TEST_VM_ID); + when(_volumeDao.findByInstance(anyLong())).thenReturn(mockVolumeList); + + when(vmMock.getInstanceName()).thenReturn("i-3-VM-TEST"); + when(vmMock.getState()).thenReturn(State.Running); + + when(_guestOSDao.findById(anyLong())).thenReturn(mock(GuestOSVO.class)); + } + + // vmId null case + @Test(expected=InvalidParameterValueException.class) + public void testAllocVMSnapshotF1() throws ResourceAllocationException{ + when(_userVMDao.findById(TEST_VM_ID)).thenReturn(null); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + } + + // vm state not in [running, stopped] case + @Test(expected=InvalidParameterValueException.class) + public void testAllocVMSnapshotF2() throws ResourceAllocationException{ + when(vmMock.getState()).thenReturn(State.Starting); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + } + + // VM in stopped state & snapshotmemory case + @Test(expected=InvalidParameterValueException.class) + public void testCreateVMSnapshotF3() throws AgentUnavailableException, OperationTimedoutException, ResourceAllocationException{ + when(vmMock.getState()).thenReturn(State.Stopped); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + } + + // max snapshot limit case + @SuppressWarnings("unchecked") + @Test(expected=CloudRuntimeException.class) + public void testAllocVMSnapshotF4() throws ResourceAllocationException{ + List mockList = mock(List.class); + when(mockList.size()).thenReturn(10); + when(_vmSnapshotDao.findByVm(TEST_VM_ID)).thenReturn(mockList); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + } + + // active volume snapshots case + @SuppressWarnings("unchecked") + @Test(expected=CloudRuntimeException.class) + public void testAllocVMSnapshotF5() throws ResourceAllocationException{ + List mockList = mock(List.class); + when(mockList.size()).thenReturn(1); + when(_snapshotDao.listByInstanceId(TEST_VM_ID,Snapshot.State.Creating, + Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp)).thenReturn(mockList); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + } + + // successful creation case + @Test + public void testCreateVMSnapshot() throws AgentUnavailableException, OperationTimedoutException, ResourceAllocationException, NoTransitionException{ + when(vmMock.getState()).thenReturn(State.Running); + _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); + + when(_vmSnapshotDao.findCurrentSnapshotByVmId(anyLong())).thenReturn(null); + doReturn(new ArrayList()).when(_vmSnapshotMgr).getVolumeTOList(anyLong()); + doReturn(new CreateVMSnapshotAnswer(null,true,"")).when(_vmSnapshotMgr).sendToPool(anyLong(), any(CreateVMSnapshotCommand.class)); + doNothing().when(_vmSnapshotMgr).processAnswer(any(VMSnapshotVO.class), + any(UserVmVO.class), any(Answer.class), anyLong()); + doReturn(true).when(_vmSnapshotMgr).vmSnapshotStateTransitTo(any(VMSnapshotVO.class),any(VMSnapshot.Event.class)); + _vmSnapshotMgr.createVmSnapshotInternal(vmMock, mock(VMSnapshotVO.class), 5L); + } + +} diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index f89c8851e8e..11ae26745e8 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -2625,6 +2625,36 @@ INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (1, INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (2, UUID(), 'snmp','Linux System CPU - percentage', '1.3.6.1.4.1.2021.11.10.0', now()); INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (3, UUID(), 'snmp','Linux CPU Idle - percentage', '1.3.6.1.4.1.2021.11.11.0', now()); INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (100, UUID(), 'netscaler','Response Time - microseconds', 'RESPTIME', now()); +CREATE TABLE `cloud`.`vm_snapshots` ( + `id` bigint(20) unsigned NOT NULL auto_increment COMMENT 'Primary Key', + `uuid` varchar(40) NOT NULL, + `name` varchar(255) NOT NULL, + `display_name` varchar(255) default NULL, + `description` varchar(255) default NULL, + `vm_id` bigint(20) unsigned NOT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `domain_id` bigint(20) unsigned NOT NULL, + `vm_snapshot_type` varchar(32) default NULL, + `state` varchar(32) NOT NULL, + `parent` bigint unsigned default NULL, + `current` int(1) unsigned default NULL, + `update_count` bigint unsigned NOT NULL DEFAULT 0, + `updated` datetime default NULL, + `created` datetime default NULL, + `removed` datetime default NULL, + PRIMARY KEY (`id`), + CONSTRAINT UNIQUE KEY `uc_vm_snapshots_uuid` (`uuid`), + INDEX `vm_snapshots_name` (`name`), + INDEX `vm_snapshots_vm_id` (`vm_id`), + INDEX `vm_snapshots_account_id` (`account_id`), + INDEX `vm_snapshots_display_name` (`display_name`), + INDEX `vm_snapshots_removed` (`removed`), + INDEX `vm_snapshots_parent` (`parent`), + CONSTRAINT `fk_vm_snapshots_vm_id__vm_instance_id` FOREIGN KEY `fk_vm_snapshots_vm_id__vm_instance_id` (`vm_id`) REFERENCES `vm_instance` (`id`), + CONSTRAINT `fk_vm_snapshots_account_id__account_id` FOREIGN KEY `fk_vm_snapshots_account_id__account_id` (`account_id`) REFERENCES `account` (`id`), + CONSTRAINT `fk_vm_snapshots_domain_id__domain_id` FOREIGN KEY `fk_vm_snapshots_domain_id__domain_id` (`domain_id`) REFERENCES `domain` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE `cloud`.`user_ipv6_address` ( `id` bigint unsigned NOT NULL UNIQUE auto_increment, diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 6cb82f2ea25..42cd7836587 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -1931,7 +1931,7 @@ div.detail-group.actions td { } .detail-group table td.detail-actions { - width: 55%; + width: 59%; height: 26px; } diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index 508135611e3..2abafaab3ce 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -1437,6 +1437,16 @@ dictionary = { 'label.resize.new.size': '', 'label.action.resize.volume': '', 'label.resize.new.offering.id': '', -'label.resize.shrink.ok': '' +'label.resize.shrink.ok': '', +'label.vmsnapshot.current': '', +'label.vmsnapshot.parentname': '', +'label.vmsnapshot.type': '', +'label.vmsnapshot.memory': '', +'label.vmsnapshot': '', +'label.action.vmsnapshot.create': '', +'label.action.vmsnapshot.delete': '', +'label.action.vmsnapshot.revert': '', +'message.action.vmsnapshot.delete': '', +'message.action.vmsnapshot.revert': '' }; diff --git a/ui/index.jsp b/ui/index.jsp index fdacd9e99d4..8af147816a0 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -1674,6 +1674,8 @@ under the License. + + diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 5b5ed1853e7..264b5a1cbbe 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -48,7 +48,7 @@ displayname: { label: 'label.display.name' }, zonename: { label: 'label.zone.name' }, state: { - label: 'label.state', + label: 'label.state', indicator: { 'Running': 'on', 'Stopped': 'off', @@ -153,6 +153,194 @@ notification: { poll: pollAsyncJobResult } + }, + start: { + label: 'label.action.start.instance' , + action: function(args) { + $.ajax({ + url: createURL("startVirtualMachine&id=" + args.context.instances[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.startvirtualmachineresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + } + ); + } + }); + }, + messages: { + confirm: function(args) { + return 'message.action.start.instance'; + }, + notification: function(args) { + return 'label.action.start.instance'; + }, + complete: function(args) { + if(args.password != null) { + alert('Password of the VM is ' + args.password); + } + return 'label.action.start.instance'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + stop: { + label: 'label.action.stop.instance', + addRow: 'false', + createForm: { + title: 'label.action.stop.instance', + desc: 'message.action.stop.instance', + fields: { + forced: { + label: 'force.stop', + isBoolean: true, + isChecked: false + } + } + }, + action: function(args) { + var array1 = []; + array1.push("&forced=" + (args.data.forced == "on")); + $.ajax({ + url: createURL("stopVirtualMachine&id=" + args.context.instances[0].id + array1.join("")), + dataType: "json", + async: true, + success: function(json) { + var jid = json.stopvirtualmachineresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + } + ); + } + }); + }, + messages: { + confirm: function(args) { + return 'message.action.stop.instance'; + }, + + notification: function(args) { + return 'label.action.stop.instance'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + restart: { + label: 'instances.actions.reboot.label', + action: function(args) { + $.ajax({ + url: createURL("rebootVirtualMachine&id=" + args.context.instances[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.rebootvirtualmachineresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + } + ); + } + }); + }, + messages: { + confirm: function(args) { + return 'message.action.reboot.instance'; + }, + notification: function(args) { + return 'instances.actions.reboot.label'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + + destroy: { + label: 'label.action.destroy.instance', + messages: { + confirm: function(args) { + return 'message.action.destroy.instance'; + }, + notification: function(args) { + return 'label.action.destroy.instance'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("destroyVirtualMachine&id=" + args.context.instances[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.destroyvirtualmachineresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + } + ); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + }, + restore: { + label: 'label.action.restore.instance', + messages: { + confirm: function(args) { + return 'message.action.restore.instance'; + }, + notification: function(args) { + return 'label.action.restore.instance'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("recoverVirtualMachine&id=" + args.context.instances[0].id), + dataType: "json", + async: true, + success: function(json) { + var item = json.recovervirtualmachineresponse.virtualmachine; + args.response.success({data:item}); + } + }); + } } }, @@ -231,7 +419,7 @@ detailView: { name: 'Instance details', - viewAll: { path: 'storage.volumes', label: 'label.volumes' }, + viewAll: [{ path: 'storage.volumes', label: 'label.volumes' }, { path: 'vmsnapshots', label: 'Snapshots' } ], tabFilter: function(args) { var hiddenTabs = []; @@ -409,6 +597,70 @@ poll: pollAsyncJobResult } }, + + snapshot: { + messages: { + notification: function(args) { + return 'label.action.vmsnapshot.create'; + } + }, + label: 'label.action.vmsnapshot.create', + addRow: 'false', + createForm: { + title: 'label.action.vmsnapshot.create', + fields: { + name: { + label: 'label.name', + isInput: true + }, + description: { + label: 'label.description', + isTextarea: true + }, + snapshotMemory: { + label: 'label.vmsnapshot.memory', + isBoolean: true, + isChecked: false + } + } + }, + action: function(args) { + var array1 = []; + array1.push("&snapshotmemory=" + (args.data.snapshotMemory == "on")); + var displayname = args.data.name; + if (displayname != null && displayname.length > 0) { + array1.push("&name=" + todb(displayname)); + } + var description = args.data.description; + if (description != null && description.length > 0) { + array1.push("&description=" + todb(description)); + } + $.ajax({ + url: createURL("createVMSnapshot&virtualmachineid=" + args.context.instances[0].id + array1.join("")), + dataType: "json", + async: true, + success: function(json) { + var jid = json.createvmsnapshotresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + }); + } + }); + + }, + notification: { + pool: pollAsyncJobResult + } + }, + destroy: { label: 'label.action.destroy.instance', compactLabel: 'label.destroy', @@ -1232,6 +1484,7 @@ else if (jsonObj.state == 'Running') { allowedActions.push("stop"); allowedActions.push("restart"); + allowedActions.push("snapshot"); allowedActions.push("destroy"); allowedActions.push("changeService"); allowedActions.push("reset"); @@ -1257,7 +1510,7 @@ allowedActions.push("start"); allowedActions.push("destroy"); allowedActions.push("reset"); - + allowedActions.push("snapshot"); if(isAdmin()) allowedActions.push("migrateToAnotherStorage"); diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index 4b4fbd5a477..5f0c0f0ee4b 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -892,14 +892,20 @@ $actions.prependTo($firstRow.closest('div.detail-group').closest('.details')); } if (detailViewArgs.viewAll && showViewAll) { + + if( !(detailViewArgs.viewAll instanceof Array)){ + detailViewArgs.viewAll = [detailViewArgs.viewAll]; + } + $.each(detailViewArgs.viewAll, function(n, view){ $('
') .addClass('view-all') .append( $('') .attr({ href: '#' }) - .data('detail-view-link-view-all', detailViewArgs.viewAll) + .css('padding','0 1px') + .data('detail-view-link-view-all', view) .append( - $('').html(_l('label.view') + ' ' + _l(detailViewArgs.viewAll.label)) + $('').html(_l('label.view') + ' ' + _l(view.label)) ) ) .append( @@ -908,9 +914,10 @@ .appendTo( $('') .addClass('view-all') + .css('padding','9px 3px 8px 0') .appendTo($actions.find('tr')) ); - + }); } } diff --git a/ui/scripts/vm_snapshots.js b/ui/scripts/vm_snapshots.js new file mode 100644 index 00000000000..0d6305bfbe4 --- /dev/null +++ b/ui/scripts/vm_snapshots.js @@ -0,0 +1,196 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +(function($, cloudStack) { + cloudStack.sections.vmsnapshots = { + title: 'label.vmsnapshot', + id: 'vmsnapshots', + listView: { + id: 'vmsnapshots', + isMaximized: true, + fields: { + displayname: { + label: 'label.name' + }, + state: { + label: 'label.state', + indicator: { + 'Ready': 'on', + 'Error': 'off' + } + }, + type:{ + label: 'label.vmsnapshot.type' + }, + current:{ + label: 'label.vmsnapshot.current', + converter: cloudStack.converters.toBooleanText + }, + parentName:{ + label: 'label.vmsnapshot.parentname' + }, + created: { + label: 'label.date', + converter: cloudStack.converters.toLocalDate + } + }, + + dataProvider: function(args) { + var apiCmd = "listVMSnapshot&listAll=true"; + if (args.context != null) { + if ("instances" in args.context) { + apiCmd += "&virtualmachineid=" + args.context.instances[0].id; + } + } + $.ajax({ + url: createURL(apiCmd), + dataType: "json", + async: true, + success: function(json) { + var jsonObj; + jsonObj = json.listvmsnapshotresponse.vmSnapshot; + args.response.success({ + data: jsonObj + }); + } + }); + }, + //dataProvider end + detailView: { + tabs: { + details: { + title: 'label.details', + fields: { + id: { + label: 'label.id' + }, + name: { + label: 'label.name' + }, + displayname: { + label: 'label.display.name', + }, + type: { + label: 'label.vmsnapshot.type', + }, + description: { + label: 'label.description', + }, + state: { + label: 'label.state', + indicator: { + 'Ready': 'on', + 'Error': 'off' + } + }, + current: { + label: 'label.vmsnapshot.current', + converter: cloudStack.converters.toBooleanText + }, + parentName: { + label: 'label.vmsnapshot.parentname' + }, + created: { + label: 'label.date', + converter: cloudStack.converters.toLocalDate + }, + + }, + dataProvider: function(args) { + $.ajax({ + url: createURL("listVMSnapshot&listAll=true&id=" + args.context.vmsnapshots[0].id), + dataType: "json", + async: true, + success: function(json) { + var jsonObj; + jsonObj = json.listvmsnapshotresponse.vmSnapshot[0]; + args.response.success({ + //actionFilter: vmActionfilter, + data: jsonObj + }); + } + }); + }, + tags: cloudStack.api.tags({ resourceType: 'VMSnapshot', contextId: 'vmsnapshots' }) + } + }, + actions: { + //delete a snapshot + remove: { + label: 'label.action.vmsnapshot.delete', + messages: { + confirm: function(args) { + return 'message.action.vmsnapshot.delete'; + }, + notification: function(args) { + return 'label.action.vmsnapshot.delete'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("deleteVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.deletevmsnapshotresponse.jobid; + args.response.success( + {_custom: + {jobId: jid} + } + ); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + }, + restart: { + label: 'label.action.vmsnapshot.revert', + messages: { + confirm: function(args) { + return 'label.action.vmsnapshot.revert'; + }, + notification: function(args) { + return 'message.action.vmsnapshot.revert'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("revertToSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.reverttosnapshotresponse.jobid; + args.response.success({ + _custom: { + jobId: jid + } + }); + } + }); + + }, + notification: { + poll: pollAsyncJobResult + } + } + } + } + //detailview end + } + } +})(jQuery, cloudStack); \ No newline at end of file diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index a765b42fd78..40343b692c3 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -58,6 +58,8 @@ import com.vmware.vim25.PropertySpec; import com.vmware.vim25.SelectionSpec; import com.vmware.vim25.TraversalSpec; import com.vmware.vim25.VirtualMachineConfigSpec; +import com.vmware.vim25.VirtualMachineSnapshotInfo; +import com.vmware.vim25.VirtualMachineSnapshotTree; import com.vmware.vim25.VirtualNicManagerNetConfig; import com.vmware.vim25.NasDatastoreInfo; @@ -949,4 +951,20 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { HostRuntimeInfo runtimeInfo = (HostRuntimeInfo)_context.getServiceUtil().getDynamicProperty(_mor, "runtime"); return runtimeInfo.getConnectionState() == HostSystemConnectionState.connected; } + + public boolean revertToSnapshot(ManagedObjectReference morSnapshot) + throws Exception { + ManagedObjectReference morTask = _context.getService() + .revertToSnapshot_Task(morSnapshot, _mor, false); + String result = _context.getServiceUtil().waitForTask(morTask); + if (result.equals("sucess")) { + _context.waitForTaskProgressDone(morTask); + return true; + } else { + s_logger.error("VMware revert to snapshot failed due to " + + TaskMO.getTaskFailureInfo(_context, morTask)); + } + + return false; + } } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index cd54127fcc2..d36814d9148 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -405,6 +405,26 @@ public class VirtualMachineMO extends BaseMO { return false; } + public boolean revertToSnapshot(String snapshotName) throws Exception { + ManagedObjectReference morSnapshot = getSnapshotMor(snapshotName); + if (morSnapshot == null) { + s_logger.warn("Unable to find snapshot: " + snapshotName); + return false; + } + ManagedObjectReference morTask = _context.getService() + .revertToSnapshot_Task(morSnapshot, _mor, null); + String result = _context.getServiceUtil().waitForTask(morTask); + if (result.equals("sucess")) { + _context.waitForTaskProgressDone(morTask); + return true; + } else { + s_logger.error("VMware revert to snapshot failed due to " + + TaskMO.getTaskFailureInfo(_context, morTask)); + } + + return false; + } + public boolean removeAllSnapshots() throws Exception { VirtualMachineSnapshotInfo snapshotInfo = getSnapshotInfo(); From 2be44e916dd08c25f24e8619f048b5f3e875d791 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Wed, 13 Feb 2013 10:43:42 -0700 Subject: [PATCH 38/42] Summary: Change vhd-util check in cloud.spec to point to /usr/share/cloudstack Signed-off-by: Marcus Sorensen 1360777422 -0700 --- packaging/centos63/cloud.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index a90fde95871..82de954f27a 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -300,9 +300,9 @@ if [ "$1" == "1" ] ; then /sbin/chkconfig --level 345 cloud-management on > /dev/null 2>&1 || true fi -if [ ! -f %{_datadir}/cloud/management/webapps/client/WEB-INF/classes/scripts/scripts/vm/hypervisor/xenserver/vhd-util ] ; then +if [ ! -f %{_datadir}/cloudstack/management/webapps/client/WEB-INF/classes/scripts/scripts/vm/hypervisor/xenserver/vhd-util ] ; then echo Please download vhd-util from http://download.cloud.com.s3.amazonaws.com/tools/vhd-util and put it in - echo %{_datadir}/cloud/management/webapps/client/WEB-INF/classes/scripts/vm/hypervisor/xenserver/ + echo %{_datadir}/cloudstack/management/webapps/client/WEB-INF/classes/scripts/vm/hypervisor/xenserver/ fi #No default permission as the permission setup is complex From 4b7dbbe5cd3d934f06d7b4bd6d47c6a937aba1b6 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 13 Feb 2013 11:53:33 -0800 Subject: [PATCH 39/42] CLOUDSTACK-618: cloudstack UI - API request throttling - for async job actions that are not using notification widget, make frequency of calling queryAsyncJobResult API based on listCapabilities response. --- ui/scripts/autoscaler.js | 22 ++++++++++----------- ui/scripts/system.js | 24 +++++++++++------------ ui/scripts/zoneWizard.js | 42 ++++++++++++++++++++-------------------- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/ui/scripts/autoscaler.js b/ui/scripts/autoscaler.js index a56fd94e441..05011a3c4ff 100644 --- a/ui/scripts/autoscaler.js +++ b/ui/scripts/autoscaler.js @@ -847,7 +847,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -900,7 +900,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -915,7 +915,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -986,7 +986,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -1040,7 +1040,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -1058,7 +1058,7 @@ args.response.error(parseXMLHttpResponse(XMLHttpResponse)); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); }); @@ -1144,7 +1144,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -1214,7 +1214,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -1283,7 +1283,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -1328,7 +1328,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); @@ -1370,7 +1370,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { args.response.error(parseXMLHttpResponse(XMLHttpResponse)); diff --git a/ui/scripts/system.js b/ui/scripts/system.js index cb09a851742..e51bf90ba81 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -3165,7 +3165,7 @@ alert("addNetworkServiceProvider&name=Netscaler failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -3406,7 +3406,7 @@ alert("addNetworkServiceProvider&name=F5BigIpfailed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -3669,7 +3669,7 @@ alert("addNetworkServiceProvider&name=JuniperSRX failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -3988,7 +3988,7 @@ alert("addNetworkServiceProvider&name=NiciraNvp failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -6240,7 +6240,7 @@ alert("addNetworkServiceProvider&name=Netscaler failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -6435,7 +6435,7 @@ alert("addNetworkServiceProvider&name=F5BigIpfailed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -6646,7 +6646,7 @@ alert("addNetworkServiceProvider&name=JuniperSRX failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -6812,7 +6812,7 @@ alert("addNetworkServiceProvider&name=NiciraNvp failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -10222,7 +10222,7 @@ alert("updateNetworkServiceProvider failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -10303,7 +10303,7 @@ alert("updateNetworkServiceProvider failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -10317,7 +10317,7 @@ alert("configureVirtualRouterElement failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -10331,7 +10331,7 @@ alert("updatePhysicalNetwork failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); }; diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 26838a173c3..7677c15740d 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -1433,7 +1433,7 @@ alert("Failed to add Guest traffic type to basic zone. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); @@ -1479,7 +1479,7 @@ alert("Failed to add Management traffic type to basic zone. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); @@ -1527,7 +1527,7 @@ alert("Failed to add Management traffic type to basic zone. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -1574,7 +1574,7 @@ alert("Failed to add Public traffic type to basic zone. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -1589,7 +1589,7 @@ alert("createPhysicalNetwork failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); @@ -1687,7 +1687,7 @@ alert(apiCmd + " failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); }); @@ -1702,7 +1702,7 @@ alert("createPhysicalNetwork failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); }); @@ -1864,7 +1864,7 @@ alert("failed to enable security group provider. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -1884,7 +1884,7 @@ alert("failed to enable Virtual Router Provider. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -1898,7 +1898,7 @@ alert("configureVirtualRouterElement failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -1912,7 +1912,7 @@ alert("updatePhysicalNetwork failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -2037,7 +2037,7 @@ alert("updateNetworkServiceProvider failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -2051,7 +2051,7 @@ alert("configureVirtualRouterElement failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); // ***** Virtual Router ***** (end) ***** @@ -2142,7 +2142,7 @@ alert("failed to enable VPC Virtual Router Provider. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -2156,7 +2156,7 @@ alert("configureVirtualRouterElement failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); // ***** VPC Virtual Router ***** (end) ***** @@ -2212,7 +2212,7 @@ alert("failed to enable security group provider. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); } @@ -2226,7 +2226,7 @@ alert("failed to enable physical network. Error: " + parseXMLHttpResponse(XMLHttpResponse)); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); }); @@ -2270,7 +2270,7 @@ alert("addNetworkServiceProvider&name=Netscaler failed. Error: " + errorMsg); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); //add netscaler provider (end) @@ -2421,7 +2421,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); @@ -2435,7 +2435,7 @@ } } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); @@ -2811,7 +2811,7 @@ error('configureGuestTraffic', errorMsg, { fn: 'configureGuestTraffic', args: args }); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); } }); }); From ceb44aca30ff4fb944e48ea08dc455d2bd0fb7b8 Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 13 Feb 2013 11:55:05 -0800 Subject: [PATCH 40/42] CLOUDSTACK-1251: cloudstack UI - zBaremetal - zone wizard - when hypervisor is BareMetal, click Next button in Guest Traffic step will finish the whole process. --- ui/scripts/ui-custom/zoneWizard.js | 13 ++++++++++++- ui/scripts/zoneWizard.js | 14 +++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index 47fa3857f9a..718b45482a3 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -1210,7 +1210,18 @@ if (($form && $form.find('.error:visible').size()) || !isCustomValidated) return false; } - + + //when hypervisor is BareMetal (begin) + var data = getData($wizard); + if(('zone' in data) && (data.zone.hypervisor == 'BareMetal')) { + if($('.zone-wizard:visible').find('#add_zone_guest_traffic_desc:visible').size() > 0) { //$steps.filter(':visible').index() == 6 + showStep('launch'); + completeAction(); + return false; + } + } + //when hypervisor is BareMetal (end) + if (!$target.closest('.button.next.final').size()) showStep($steps.filter(':visible').index() + 2); else { diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 7677c15740d..60656e05850 100755 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -2735,9 +2735,17 @@ dataType: "json", success: function(json) { args.data.returnedGuestNetwork.returnedVlanIpRange = json.createvlaniprangeresponse.vlan; - stepFns.addCluster({ - data: args.data - }); + + //when hypervisor is BareMetal (begin) + if(args.data.cluster.hypervisor == "BareMetal") { + alert('Zone creation is completed. Please refresh this page.'); + } + else { + stepFns.addCluster({ + data: args.data + }); + } + //when hypervisor is BareMetal (end) }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); From 4e7da0001d983aca351fc97b2385f6a462aa09be Mon Sep 17 00:00:00 2001 From: Jessica Wang Date: Wed, 13 Feb 2013 11:55:59 -0800 Subject: [PATCH 41/42] CLOUDSTACK-618: cloudstack UI - API request throttling - for async job action uploadCustomCertificate, make frequency of calling queryAsyncJobResult API based on listCapabilities response. --- ui/scripts/sharedFunctions.js | 2 +- ui/scripts/ui-custom/physicalResources.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 026089f6ea4..ad26b34196e 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -26,7 +26,7 @@ var g_timezone = null; var g_supportELB = null; var g_userPublicTemplateEnabled = "true"; var g_cloudstackversion = null; -var g_queryAsyncJobResultInterval = 6000; +var g_queryAsyncJobResultInterval = 3000; //keyboard keycode var keycode_Enter = 13; diff --git a/ui/scripts/ui-custom/physicalResources.js b/ui/scripts/ui-custom/physicalResources.js index a247dcfe2c2..69c0295c08d 100644 --- a/ui/scripts/ui-custom/physicalResources.js +++ b/ui/scripts/ui-custom/physicalResources.js @@ -111,7 +111,7 @@ $loading.remove(); } }); - }, 3000); + }, g_queryAsyncJobResultInterval); }, error: function(XMLHttpResponse) { cloudStack.dialog.notice({ message: 'Failed to update SSL Certificate. ' + parseXMLHttpResponse(XMLHttpResponse) }); From 8652e5f87e49e44eeddb14454984569f4f3439e1 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Wed, 13 Feb 2013 13:19:09 -0700 Subject: [PATCH 42/42] Summary: Fix hanging references to /var/lib/cloud, changed to /var/cloudstack BUG-ID: CLOUDSTACK-1201 BUG-ID: CLOUDSTACK-1196 Signed-off-by: Marcus Sorensen 1360786749 -0700 --- packaging/centos63/cloud.spec | 2 +- packaging/centos63/package.sh | 0 python/lib/cloudutils/serviceConfigServer.py | 2 +- server/src/com/cloud/configuration/Config.java | 2 +- setup/db/server-setup.xml | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 packaging/centos63/package.sh diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 82de954f27a..5a15ce40929 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -284,7 +284,7 @@ fi %pre management id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -c "CloudStack unprivileged user" \ - -r -s /bin/sh -d %{_localstatedir}/cloud/management cloud|| true + -r -s /bin/sh -d %{_localstatedir}/cloudstack/management cloud|| true # set max file descriptors for cloud user to 4096 sed -i /"cloud hard nofile"/d /etc/security/limits.conf diff --git a/packaging/centos63/package.sh b/packaging/centos63/package.sh old mode 100644 new mode 100755 diff --git a/python/lib/cloudutils/serviceConfigServer.py b/python/lib/cloudutils/serviceConfigServer.py index 66ec2d00a92..a08ce02d766 100644 --- a/python/lib/cloudutils/serviceConfigServer.py +++ b/python/lib/cloudutils/serviceConfigServer.py @@ -73,7 +73,7 @@ class cloudManagementConfig(serviceCfgBase): bash("iptables -A PREROUTING -t nat -p tcp --dport 443 -j REDIRECT --to-port 8250 ") #generate keystore - keyPath = "/var/lib/cloud/management/web.keystore" + keyPath = "/var/cloudstack/management/web.keystore" if not os.path.exists(keyPath): cmd = bash("keytool -genkey -keystore %s -storepass \"cloud.com\" -keypass \"cloud.com\" -validity 3650 -dname cn=\"Cloudstack User\",ou=\"mycloud.cloud.com\",o=\"mycloud.cloud.com\",c=\"Unknown\""%keyPath) diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 26499bcd4f2..b22bf4b76f0 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -180,7 +180,7 @@ public enum Config { MigrateWait("Advanced", AgentManager.class, Integer.class, "migratewait", "3600", "Time (in seconds) to wait for VM migrate finish", null), Workers("Advanced", AgentManager.class, Integer.class, "workers", "5", "Number of worker threads.", null), HAWorkers("Advanced", AgentManager.class, Integer.class, "ha.workers", "5", "Number of ha worker threads.", null), - MountParent("Advanced", ManagementServer.class, String.class, "mount.parent", "/var/lib/cloud/management/mnt", "The mount point on the Management Server for Secondary Storage.", null), + MountParent("Advanced", ManagementServer.class, String.class, "mount.parent", "/var/cloudstack/mnt", "The mount point on the Management Server for Secondary Storage.", null), // UpgradeURL("Advanced", ManagementServer.class, String.class, "upgrade.url", "http://example.com:8080/client/agent/update.zip", "The upgrade URL is the URL of the management server that agents will connect to in order to automatically upgrade.", null), SystemVMUseLocalStorage("Advanced", ManagementServer.class, Boolean.class, "system.vm.use.local.storage", "false", "Indicates whether to use local storage pools or shared storage pools for system VMs.", null), SystemVMAutoReserveCapacity("Advanced", ManagementServer.class, Boolean.class, "system.vm.auto.reserve.capacity", "true", "Indicates whether or not to automatically reserver system VM standby capacity.", null), diff --git a/setup/db/server-setup.xml b/setup/db/server-setup.xml index 912dd0b85d8..bb8878f8d37 100755 --- a/setup/db/server-setup.xml +++ b/setup/db/server-setup.xml @@ -446,7 +446,7 @@ under the License.