diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..bf9e0bc97 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.vagrant +bin/** diff --git a/.drone.local.plugin.yml b/.drone.local.plugin.yml new file mode 100644 index 000000000..fbf7c4f30 --- /dev/null +++ b/.drone.local.plugin.yml @@ -0,0 +1,32 @@ +workspace: + base: /go + path: src/github.com/vmware/vic + +pipeline: + clone: + image: plugins/git + tags: true + recursive: false + vic-uia: + image: ${TEST_BUILD_IMAGE=gcr.io/eminent-nation-87317/vic-integration-test:1.33} + pull: true + environment: + BIN: bin + GOPATH: /go + SHELL: /bin/bash + TEST_DATASTORE: ${TEST_DATASTORE} + TEST_RESOURCE: ${TEST_RESOURCE} + GOVC_INSECURE: true + BUILD_NUMBER: ${buildNumber} + TEST_VSPHERE_VER: #TEST_VSPHERE_VER + TEST_VCSA_BUILD: #TEST_VCSA_BUILD + TEST_OS: #TEST_OS + SELENIUM_BROWSER: #SELENIUM_BROWSER + BROWSER_NORMALIZED_NAME: #BROWSER_NORMALIZED_NAME + UI_TEST_CASES_FOLDER: tests/test-cases/Group18-VIC-UI + TEST_RESULTS_FOLDER: #TEST_RESULTS_FOLDER + commands: + - apt-get update && apt-get install -yq maven netcat + - pip install pexpect robotframework-selenium2library + - cd $UI_TEST_CASES_FOLDER + - robot -C ansi -d ../../../$TEST_RESULTS_FOLDER 18-4-VIC-UI-Plugin-tests.robot diff --git a/.drone.local.script.yml b/.drone.local.script.yml new file mode 100644 index 000000000..05e6d53af --- /dev/null +++ b/.drone.local.script.yml @@ -0,0 +1,33 @@ +workspace: + base: /go + path: src/github.com/vmware/vic + +pipeline: + clone: + image: plugins/git + tags: true + recursive: false + vic-uia: + image: ${TEST_BUILD_IMAGE=gcr.io/eminent-nation-87317/vic-integration-test:1.33} + pull: true + environment: + BIN: bin + GOPATH: /go + SHELL: /bin/bash + TEST_DATASTORE: ${TEST_DATASTORE} + TEST_RESOURCE: ${TEST_RESOURCE} + GOVC_INSECURE: true + BUILD_NUMBER: #BUILD_NUMBER + TEST_VSPHERE_VER: #TEST_VSPHERE_VER + TEST_VCSA_BUILD: #TEST_VCSA_BUILD + TEST_OS: #TEST_OS + UI_TEST_CASES_FOLDER: tests/test-cases/Group18-VIC-UI + TEST_RESULTS_FOLDER: #TEST_RESULTS_FOLDER + ROBOT_SCRIPT: #ROBOT_SCRIPT + commands: + - set -e + - pip install pexpect + - cd $UI_TEST_CASES_FOLDER + - if [ $TEST_OS = "Mac" ] ; then robot --log container_log.html --report container_report.html --output container_output.xml --test *Mac -C ansi -d ../../../$TEST_RESULTS_FOLDER $ROBOT_SCRIPT ; fi + - if [ $TEST_OS = "Ubuntu" ] ; then robot --include anyos --include unixlike --test TestCase-* -C ansi -d ../../../$TEST_RESULTS_FOLDER $ROBOT_SCRIPT ; fi + - if [ $TEST_OS = "Windows" ] ; then robot --log container_log.html --report container_report.html --output container_output.xml --test *Windows -C ansi -d ../../../$TEST_RESULTS_FOLDER $ROBOT_SCRIPT ; fi diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 000000000..bdbe9ae1e --- /dev/null +++ b/.drone.yml @@ -0,0 +1,162 @@ + +workspace: + base: /go + path: src/github.com/vmware/vic-ui + +clone: + git: + image: plugins/git + tags: true + recursive: false + +pipeline: + check-org-membership: + image: 'gcr.io/eminent-nation-87317/vic-integration-test:1.50' + pull: true + environment: + BIN: bin + GOPATH: /go + SHELL: /bin/bash + secrets: + - github_automation_api_key + commands: + - tests/check-org-membership.sh + when: + status: success + + vic-ui: + image: 'gcr.io/eminent-nation-87317/vic-integration-test:1.50' + pull: true + environment: + BIN: bin + secrets: + - gs_client_email + - gs_private_key + - gs_project_id + commands: + - 'export BUILD_NUMBER=${DRONE_BUILD_NUMBER}' + - 'npm config set registry https://build-artifactory.eng.vmware.com/artifactory/api/npm/npm && make vic-ui-plugins' + when: + status: success + + bundle: + image: 'gcr.io/eminent-nation-87317/vic-integration-test:1.50' + pull: true + environment: + BIN: bin + GOPATH: /go + SHELL: /bin/bash + commands: + - 'mkdir -p $BIN/ui' + - 'tar -czvf $BIN/vic_ui_${DRONE_BUILD_NUMBER}.tar.gz $BIN/ui' + - 'mkdir bundle' + - 'mkdir bundle-release' + - 'cp $BIN/vic_ui_${DRONE_BUILD_NUMBER}.tar.gz bundle' + - 'cp $BIN/vic_ui_${DRONE_BUILD_NUMBER}.tar.gz bundle-release/vic_ui_`git describe --tags --abbrev=0`.tar.gz' + - 'rm -rf $BIN' + - 'ls -la bundle' + when: + repo: vmware/vic-ui + status: success + + publish-gcs-builds-on-pass: + image: 'victest/drone-gcs:1' + pull: true + secrets: + - google_key + source: bundle + target: vic-ui-builds/ + acl: + - 'allUsers:READER' + cache_control: 'public,max-age=3600' + when: + repo: vmware/vic-ui + event: [push] + branch: [master] + status: success + + publish-gcs-develop-builds-on-pass: + image: 'victest/drone-gcs:1' + pull: true + secrets: + - google_key + source: bundle + target: vic-ui-builds/develop/ + acl: + - 'allUsers:READER' + cache_control: 'public,max-age=3600' + when: + repo: vmware/vic-ui + event: [push] + branch: [develop] + status: success + + publish-gcs-release-builds-on-pass: + image: 'victest/drone-gcs:1' + pull: true + secrets: + - google_key + source: bundle + target: vic-ui-builds/${DRONE_BRANCH}/ + acl: + - 'allUsers:READER' + cache_control: 'public,max-age=3600' + when: + repo: vmware/vic-ui + event: [push] + branch: ['releases/*'] + status: success + + publish-gcs-releases: + image: 'victest/drone-gcs:1' + pull: true + secrets: + - google_key + source: bundle-release + target: vic-ui-releases + acl: + - 'allUsers:READER' + cache_control: 'public,max-age=3600' + when: + repo: vmware/vic-ui + branch: ['releases/*', 'refs/tags/*'] + event: tag + status: success + + report-coverage: + image: 'robertstettner/drone-codecov' + secrets: + - codecov_token + files: + - 'h5c/vic/src/vic-webapp/coverage/lcov.info' + + trigger-downstream: + image: 'gcr.io/eminent-nation-87317/vic-downstream-trigger:1.3' + environment: + SHELL: /bin/bash + DOWNSTREAM_REPO: vmware/vic-product + DOWNSTREAM_BRANCH: ${DRONE_BRANCH} + secrets: + - drone_server + - drone_token + when: + repo: vmware/vic-ui + event: [push, tag] + branch: [master, 'releases/*', 'refs/tags/*'] + status: success + + pass-rate: + image: 'gcr.io/eminent-nation-87317/vic-integration-test:1.42' + pull: true + environment: + BIN: bin + SHELL: /bin/bash + secrets: + - github_automation_api_key + - slack_url + commands: + - tests/pass-rate.sh + +services: + selenium: + image: selenium/standalone-firefox diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..f06df60fa --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,73 @@ + + +#For stories, please include the information below: + +**User Statement:** + +A brief statement describe who, what and why of the story for example - As a job seeker, I want to search for a job, so I can advance my career. + +**Details:** +Any details of what you want that might clarify for the developer how to approach the implementation. + +**Acceptance Criteria:** +Specific deliverable and actionable items that the story needs to deliver in order to be considered complete, the more detail here the more accurate the implementation will be. + + +#For bug reports, please include the information below: + + +**VIC-UI version:** + +If applicable: + +[In the VIC plugin] > Summary tab > Version + +**Platform details:** + +Which platform are you using? (Windows, Mac OS, Linux, etc) + +**Browser details:** + +Which browser are you using? (IE, Chrome, Firefox, etc) + +**Additional details:** + +If applicable please include the following: + +Error messages. (In the UI or from the browser's developer tools) + +Request/response, including headers (from the browser's developer tools). + +**Steps to reproduce:** + +**Actual behavior:** + +**Expected behavior:** + +**Logs:** + +If applicable attach vsphere_client_virgo.log + +See: + +https://docs.vmware.com/en/VMware-vSphere/6.5/com.vmware.vsphere.monitoring.doc/GUID-7E10C58F-16EA-44AB-8AA0-8D4A66399879.html + + +**Additional details as necessary:** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..5faf9e419 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ + + +Fixes # + +PR acceptance checklist: + +[ ] All unit tests pass +[ ] All e2e tests pass +[ ] Unit test(s) included* +[ ] e2e test(s) included* +[ ] Screenshot attached and UX approved* + + *if applicable, add n/a if not diff --git a/.gitignore b/.gitignore index e69de29bb..eff8adcb7 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,62 @@ +.vagrant/ +.vscode/ +.cover/ +bin/ +binary/ +vic_binaries*.tar.gz +secrets.yml +test_secrets.yml +nightly_ui_tests_secrets.yml +secrets-e2e.yml +vic-ci-logs.key +install-*.tar.gz +*.log +log.html +report.html +*.gas +*.pyc +package.list +*container-logs.zip +*certs.zip +integration_logs* +vmware-ovftool +.DS_Store + +# generally a bad idea to check certificates into repos - whitelist if needed +**/*.pem + +lib/apiservers/portlayer/client/ +lib/apiservers/portlayer/cmd +lib/apiservers/portlayer/models +lib/apiservers/portlayer/restapi/*.go +lib/apiservers/portlayer/restapi/operations +!lib/apiservers/portlayer/restapi/configure_port_layer.go +lib/config/dynamic/admiral/client +lib/config/dynamic/admiral/models +lib/config/dynamic/admiral/operations + +tests/.project +# go test binaries +*.test + +# Intellij files +*.iml + +*.zip +\$* + +# vic plugin jar / war files +scripts/plugin-manifest +scripts/vsphere-client-serenity/ +scripts/plugin-packages/ +scripts/upload-to-staging-fileserver.sh +test.secrets +**/*.swf +**/*.jar +**/*.war +**/bin +**/target/* +**/unittest-results/* + +# IDE configs files +.idea/ diff --git a/.pullapprove.yml b/.pullapprove.yml new file mode 100644 index 000000000..b5f644084 --- /dev/null +++ b/.pullapprove.yml @@ -0,0 +1,243 @@ +version: 2 +group_defaults: + required: 1 + author_approval: + auto: true + approve_by_comment: + enabled: true + approve_regex: 'Approved|:shipit:|:sheep::it:|:\+1:|LGTM|lgtm|yolo|YOLO' + reset_on_push: + enabled: false + +groups: + contributors: + required: 3 + teams: + - vic-maintainers + + tether: + users: + - caglar10ur + - hickeng + conditions: + files: + include: + - "cmd/tether/*" + - "lib/tether/*" + + build-ci-infra: + users: + - mhagen-vmware + - rajanashok + - rogeliosanchez + - mdharamadas1 + conditions: + files: + include: + - "demos/*" + - "infra/*" + - "tests/*" + + design-docs: + users: + - hickeng + conditions: + files: + include: + - "doc/design/*" + + docs: + users: + - stuclem + - mdubya66 + - alextopuzov + conditions: + files: + include: + - "doc/*" + exclude: + - "doc/design/*" + - "doc/bundle/*" + + nightlies: + users: + - rajanashok + - mhagen-vmware + - rogeliosanchez + conditions: + files: + include: + - "tests/nightly/*" + + log: + users: + - hmahmood + conditions: + files: + include: + - "pkg/log/*" + + docker-persona: + users: + - sflxn + - cgtexmex + - jzt + conditions: + files: + include: + - "lib/apiservers/engine/*" + - "cmd/docker/*" + exclude: + - "lib/apiservers/portlayer/*" + + install: + users: + - emlin + - andrewtchin + conditions: + files: + include: + - "cmd/vic-machine/*" + - "lib/install/*" + upgrade: + users: + - emlin + conditions: + files: + include: + - "lib/migration/*" + + config: + users: + - emlin + - hickeng + conditions: + files: + include: + - "lib/config/*" + - "lib/metadata/*" + + vicadmin: + users: + - jzt + - gigawhitlocks + - sgairo + conditions: + files: + include: + - "cmd/vicadmin/*" + - "lib/vicadmin/*" + - "isos/vicadmin/*" + + port-layer-exec-interaction: + users: + - caglar10ur + - hickeng + conditions: + files: + include: + - "lib/portlayer/attach/*" + - "lib/portlayer/exec/*" + - "lib/portlayer/exec2/*" + + port-layer-storage: + users: + - jzt + - matthewavery + conditions: + files: + include: + - "lib/portlayer/storage/*" + + port-layer-store: + users: + - cgtexmex + conditions: + files: + include: + - "lib/portlayer/store/*" + + + package-kv-store: + users: + - jzt + - matthewavery + conditions: + files: + include: + - "pkg/kvstore/*" + + port-layer-network: + users: + - hmahmood + - andrewtchin + - caglar10ur + conditions: + files: + include: + - "cmd/vic-dns/*" + - "lib/dns/*" + - "lib/dhcp/*" + - "lib/portlayer/network/*" + + events: + users: + - cgtexmex + conditions: + files: + include: + - "lib/portlayer/event/*" + + imagec: + users: + - jzt + conditions: + files: + include: + - "lib/imagec/*" + + vendor: + users: + - mdubya66 + - dougm + conditions: + files: + include: + - "vendor/*" + ui: + users: + - jooskim + - cristianfalcone + - jak-atx + conditions: + files: + include: + - "ui/*" + + ui-cmd: + users: + - andrewtchin + conditions: + files: + include: + - "cmd/vic-ui/*" + + pullapprove: + users: + - gigawhitlocks + - anchal-agrawal + conditions: + files: + include: + - ".pullapprove.yml" + + drone: + users: + - jakedsouza + - rajanashok + - mhagen-vmware + conditions: + files: + include: + - ".drone.yml" + - ".drone.sec" diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..a41e8a528 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,46 @@ +sudo: required +dist: trusty +language: node_js +node_js: + - "6" +cache: + apt: true + yarn: true +addons: + apt: + packages: + - wget +env: + global: + - BIN="bin" + - VICUI_H5_UI_PATH="h5c/vic" + - VICUI_H5_SERVICE_PATH="h5c/vic-service" + - GCP_DOWNLOAD_PATH="https://storage.googleapis.com/vic-engine-builds/" + - SDK_PACKAGE_ARCHIVE="vic-ui-sdk.tar.gz" + - ENV_VSPHERE_SDK_HOME="/tmp/sdk/vc_sdk_min" + - ENV_HTML_SDK_HOME="/tmp/sdk/html-client-sdk" + - BUILD_NUMBER="$TRAVIS_BUILD_NUMBER" +before_install: + - sudo apt-key adv --fetch-keys http://dl.yarnpkg.com/debian/pubkey.gpg + - echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - + - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' + - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - + - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + - sudo apt-get update -qq + - sudo apt-get install -y openjdk-7-jdk maven ant ant-optional google-chrome-stable yarn=0.24.6-1 + - export PATH=/usr/share/yarn/bin:$PATH + - export TAG=$(git describe --tags --abbrev=0) + - export TAG_NUM=$(git describe --tags --abbrev=0 | cut -c 2-) +branches: + only: + - new-master +script: + - sed -e "s/0.0.1/$(printf %s ${TAG_NUM}.${BUILD_NUMBER})/" -e "s/\-rc[[:digit:]]//g" ./$VICUI_H5_UI_PATH/plugin-package.xml > ./$VICUI_H5_UI_PATH/new_plugin-package.xml + - sed "s/UI_VERSION_PLACEHOLDER/$(printf %s ${TAG}.${BUILD_NUMBER})/" ./$VICUI_H5_SERVICE_PATH/src/main/resources/configs.properties > ./$VICUI_H5_SERVICE_PATH/src/main/resources/new_configs.properties + - mv ./$VICUI_H5_UI_PATH/new_plugin-package.xml ./$VICUI_H5_UI_PATH/plugin-package.xml + - mv ./$VICUI_H5_SERVICE_PATH/src/main/resources/new_configs.properties ./$VICUI_H5_SERVICE_PATH/src/main/resources/configs.properties + - wget -nv $GCP_DOWNLOAD_PATH$SDK_PACKAGE_ARCHIVE -O /tmp/$SDK_PACKAGE_ARCHIVE + - tar --warning=no-unknown-keyword -xzf /tmp/$SDK_PACKAGE_ARCHIVE -C /tmp/ + - mkdir -p $BIN/ui + - cp -rf scripts/* $BIN/ui diff --git a/LICENSE.txt b/LICENSE.txt index ab955af3b..d9b0cdf49 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,9 +1,673 @@ -Integrated containers UI for vSphere +LICENSE -Copyright 2017 VMware, Inc. All rights reserved. +Integrated containers UI for vSphere 1.5.2 GA -The Apache 2.0 license (the “License”) set forth below applies to all parts of the VMware Integrated containers UI for vSphere -project. You may not use this file except in compliance with the License. +====================================================================== + + +Copyright 2017-2019 VMware, Inc. All rights reserved. + +This product is licensed to you under the Apache License version 2.0 (the License). You may not use this product except in compliance with the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed 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. + +================================================================= + +Integrated containers UI for vSphere 1.5.2 includes a number of components with separate copyright notices and license terms. This product does not necessarily use all the open source components referred to below. +Your use of the source code for these components is subject to the terms and conditions of the following licenses. + +=============== TABLE OF CONTENTS ============================= + +The following is a listing of the open source components detailed in +this document. This list is provided for your convenience; please read +further if you wish to review the copyright notice(s) and the full text +of the license associated with each component. + + +SECTION 1: Apache License, V2.0 + >>> rxjs-6.3.1 + >>> web-animations-js-2.3.1 + + +SECTION 2: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES + + >>> @angular/animations-6.1.10 + >>> @angular/common-6.1.10 + >>> @angular/compiler-6.1.10 + >>> @angular/compiler-cli-6.1.10 + >>> @angular/core-6.1.10 + >>> @angular/forms-6.1.10 + >>> @angular/http-6.1.10 + >>> @angular/platform-browser-6.1.10 + >>> @angular/platform-browser-dynamic-6.1.10 + >>> @angular/router-6.1.10 + >>> @clr/angular-0.12.10 + >>> @clr/icons-0.12.16 + >>> @clr/ui-0.12.16 + >>> @webcomponents/custom-elements-1.2.1 + >>> classlist.js-1.1.20150312 + >>> core-js-2.5.7 + >>> framebus-2.0.8 + >>> mutationobserver-shim-0.3.2 + >>> pkijs-2.1.71 + >>> zone.js-0.8.26 + + +APPENDIX. Standard License Files + + >>> Apache License, V2.0 + + + +--------------- SECTION 1: Apache License, V2.0 ---------- + +Apache License, V2.0 is applicable to the following component(s). + +>>> rxjs-6.3.1 + +License: Apache 2.0 + +>>> web-animations-js-2.3.1 + +Copyright 2014 Google Inc. All rights reserved. + + Licensed 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. + + + +--------------- SECTION 2: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES ---------- + + +BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES are applicable to the following component(s). + +>>> @angular/common-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. + +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +common-6.1.10.tgz\common-6.1.10.tar\package\bundles\common-http.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/compiler-6.1.10 + +@license Angular v6.1.10 +(c) 2010-2018 Google, Inc. https://angular.io/ +License: MIT + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +compiler-6.1.10.tgz\compiler-6.1.10.tar\package\bundles\ compiler.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/compiler-cli-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +>>> @angular/core-6.1.10 + +@license Angular v6.1.3 +(c) 2010-2018 Google, Inc. https://angular.io/ +License: MIT + +ADDITIONAL LICENSE INFORMATION: + +> Apache2.0 + +core-6.1.10.tgz\core-6.1.10.tar\package\bundles\core.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/forms-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. + +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +forms-6.1.10.tgz\forms-6.1.10.tar\package\bundles\forms.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/http-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. + +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +http-6.1.10.tgz\http-6.1.10.tar\package\bundles\http.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/platform-browser-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. + +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +platform-browser-6.1.10.tgz\platform-browser-6.1.10.tar\package\bundles\platform-browser.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/platform-browser-dynamic-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. + +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +platform-browser-dynamic-6.1.10.tgz\platform-browser-dynamic-6.1.10.tar\package\bundles\platform-browser-dynamic.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @angular/router-6.1.10 + +@license +Copyright Google Inc. All Rights Reserved. + +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE file at https://angular.io/license + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +router-6.1.10.tgz\router-6.1.10.tar\package\bundles\router.umd.js + +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed 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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. + +>>> @clr/angular-0.12.10 + +Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. +This software is released under MIT license. + The full license information can be found in LICENSE in the root directory of this project. + +>>> @clr/icons-0.12.16 + +Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. +This software is released under MIT license. +The full license information can be found in LICENSE in the root directory of this project. + +>>> @clr/ui-0.12.16 + +Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. +This software is released under MIT license. +The full license information can be found in LICENSE in the root directory of this project. + + +>>> @webcomponents/custom-elements-1.2.1 + +# License + +Everything in this repo is BSD style license unless otherwise specified. + +Copyright (c) 2015 The Polymer Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +* Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 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. + +>>> classlist.js-1.1.20150312 + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to + +>>> core-js-2.5.7 + +Copyright (c) 2014-2018 Denis Pushkarev + +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. + +>>> framebus-2.0.8 + +Copyright (c) 2009-2017 Braintree, a division of PayPal, Inc. + +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. + +>>> mutationobserver-shim-0.3.2 + +Copyright © 2014 Graeme Yeates + +This work is free. You can redistribute it and/or modify it under the +terms of the Do What The Fuck You Want To Public License, Version 2, +as published by Sam Hocevar. See http:www.wtfpl.net/ for more details. + +This program is free software. It comes without any warranty, to +the extent permitted by applicable law. You can redistribute it +and/or modify it under the terms of the Do What The Fuck You Want +To Public License, Version 2, as published by Sam Hocevar. See +http:www.wtfpl.net/ for more details. + +ADDITIONAL LICENSE INFORMATION: + +> MIT + +MutationObserver.js-0.3.2.tar.gz\MutationObserver.js-0.3.2.tar\MutationObserver.js-0.3.2\test\vendor\jslitmus.js + +Copyright (c) 2008-2009, Robert Kieffer +All Rights Reserved + +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 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. + +> MIT + +MutationObserver.js-0.3.2.tar.gz\MutationObserver.js-0.3.2.tar\MutationObserver.js-0.3.2\test\vendor\jquery.js + +[PLEASE NOTE: VMWARE, INC. ELECTS TO USE AND DISTRIBUTE THIS COMPONENT UNDER THE TERMS OF THE MIT LICENSE. THE ORIGINAL LICENSE TERMS ARE REPRODUCED BELOW ONLY AS A REFERENCE.] + +jQuery JavaScript Library v1.7.2 +http://jquery.com/ + +Copyright 2011, John Resig +Dual licensed under the MIT or GPL Version 2 licenses. +http://jquery.org/license + +Includes Sizzle.js +http://sizzlejs.com/ +Copyright 2011, The Dojo Foundation +Released under the MIT, BSD, and GPL Licenses. + +Date: Wed Mar 21 12:46:34 2012 -0700 + +>>> pkijs-2.1.24 + +Copyright (c) 2014, GlobalSign +Copyright (c) 2015-2017, Peculiar Ventures +All rights reserved. + +Author 2014-2017, Yury Strozhevsky + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of the {organization} nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +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. + +ADDITIONAL LICENSE INFORMATION: + +> MIT + +PKI.js-master.zip\PKI.js-master\examples\HowToUseES6DirectlyInBrowser\package.json + +License: MIT + +>>> pkijs-2.1.71 + +Copyright (c) 2014, GlobalSign +Copyright (c) 2015-2018, Peculiar Ventures +All rights reserved. + +Author 2014-2018, Yury Strozhevsky + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +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. + +ADDITIONAL LICENSE INFORMATION + +> MIT + +PKI.js-master.zip\PKI.js-master\examples\HowToUseES6DirectlyInBrowser\package.json + +license": "MIT" + +>>> zone.js-0.8.26 + +Copyright (c) 2016-2018 Google, Inc. + +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. + + + +=============== APPENDIX. Standard License Files ============== + + +--------------- SECTION 1: Apache License, V2.0 ----------- Apache License @@ -179,3 +843,23 @@ asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + + +====================================================================== + +To the extent any open source components are licensed under the GPL +and/or LGPL, or other similar licenses that require the source code +and/or modifications to source code to be made available (as would be +noted above), you may obtain a copy of the source code corresponding +to the binaries for such open source components and modifications +thereto,if any, (the "Source Files"), by downloading the Source +Files from VMware's github site, or by sending a request, with +your name and address to: VMware, Inc., 3401 Hillview Avenue, +Palo Alto, CA 94304, United States of America. All such requests +should clearly specify: OPEN SOURCE FILES REQUEST, Attention +General Counsel. VMware shall mail a copy of the Source Files +to you ona CD or equivalent physical medium.This offer to obtain +a copy of the Source Filesis valid for three years from the date +you acquired this Software product. + +[VICUI152GAAD030419] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..94e979ccf --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +# Copyright 2016-2017 VMware, Inc. All Rights Reserved. +# +# Licensed 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. + +REV :=$(shell git rev-parse --short=8 HEAD) +TAG :=$(shell git describe --tags --abbrev=0) # e.g. `v0.9.0` +TAG_NUM :=$(shell git describe --tags --abbrev=0 | cut -c 2-) # e.g. `0.9.0` + +BIN ?= bin + +VICUI_H5_UI_PATH = "h5c/vic" +VICUI_H5_SERVICE_PATH = "h5c/vic-service" +GCP_DOWNLOAD_PATH = "https://storage.googleapis.com/vic-engine-builds/" +SDK_PACKAGE_ARCHIVE = "vic-ui-sdk.tar.gz" +UI_INSTALLER_WIN_PATH = "scripts/vCenterForWindows" +ENV_VSPHERE_SDK_HOME = "/tmp/sdk/vc_sdk_min" +ENV_HTML_SDK_HOME = "/tmp/sdk/html-client-sdk" +vic-ui-plugins: + @npm install -g yarn@0.24.6 > /dev/null + sed -e "s/0.0.1/$(shell printf %s ${TAG_NUM}.${BUILD_NUMBER})/" -e "s/\-rc[[:digit:]]\|\-dev//g" ./$(VICUI_H5_UI_PATH)/plugin-package.xml > ./$(VICUI_H5_UI_PATH)/new_plugin-package.xml + sed "s/UI_VERSION_PLACEHOLDER/$(shell printf %s ${TAG}.${BUILD_NUMBER})/" ./$(VICUI_H5_SERVICE_PATH)/src/main/resources/configs.properties > ./$(VICUI_H5_SERVICE_PATH)/src/main/resources/new_configs.properties + mv ./$(VICUI_H5_UI_PATH)/new_plugin-package.xml ./$(VICUI_H5_UI_PATH)/plugin-package.xml + mv ./$(VICUI_H5_SERVICE_PATH)/src/main/resources/new_configs.properties ./$(VICUI_H5_SERVICE_PATH)/src/main/resources/configs.properties + wget -nv $(GCP_DOWNLOAD_PATH)$(SDK_PACKAGE_ARCHIVE) -O /tmp/$(SDK_PACKAGE_ARCHIVE) + tar -xzf /tmp/$(SDK_PACKAGE_ARCHIVE) -C /tmp/ + ant -f h5c/build-deployable.xml -Denv.VSPHERE_SDK_HOME=$(ENV_VSPHERE_SDK_HOME) -Denv.VSPHERE_H5C_SDK_HOME=$(ENV_HTML_SDK_HOME) -Denv.BUILD_MODE=prod + ls -la scripts/ + mkdir -p $(BIN)/ui + cp -rf scripts/* $(BIN)/ui + # cleanup + rm -rf $(VICUI_H5_UI_PATH)/src/vic-app/aot + rm -f $(VICUI_H5_UI_PATH)/src/vic-app/yarn.lock + rm -rf $(VICUI_H5_UI_PATH)/src/vic-app/node_modules + +clean: + @rm -rf $(VICUI_H5_UI_PATH)/src/vic-app/node_modules + @rm -f $(VICUI_H5_UI_PATH)/src/vic-app/yarn.lock diff --git a/NOTICE.txt b/NOTICE.txt index 253c853f0..ca58dc8a8 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,7 +1,9 @@ +NOTICE + Integrated containers UI for vSphere -Copyright 2017 VMware, Inc. All Rights Reserved. +Copyright (c) 2017-2019 VMware, Inc. All Rights Reserved. -This product is licensed to you under the Apache 2.0 license (the "License"). You may not use this product except in compliance with the Apache 2.0 License. +This product is licensed to you under the Apache License, Version 2.0 (the "License"). You may not use this product except in compliance with the License. -This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. +This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. diff --git a/OWNERS.md b/OWNERS.md new file mode 100644 index 000000000..31aa23264 --- /dev/null +++ b/OWNERS.md @@ -0,0 +1,19 @@ +This file documents the current people responsible for maintaining this +repository, and their roles. This provides context to help the broader community +better understand who they are interacting with (e.g., when reading, creating, +or commenting on issues and pull requests). + +This document is not intended to list all contributors, and many important and +valued members of the extended team are not listed below. + +Please see [CONTRIBUTING.md](CONTRIBUTING.md) for general contribution +guidelines and [Contributors](https://github.com/vmware/vic/graphs/contributors) +for a more complete list of contributors. + +Core Maintainers +================ + +* Steven Ren ([@renmaosheng](https://github.com/renmaosheng)), Engineering Manager +* James Zabala ([@clouderati](https://github.com/clouderati)), Product Manager +* Mia Zhou ([zhoumeina](https://github.com/zhoumeina)) + diff --git a/README.md b/README.md index d40c0e3f9..054c81c90 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,18 @@ ## Overview -## Try it out +vSphere UI plugin for Integrated Containers on vSphere -### Prerequisites +## Related Documentation -* Prereq 1 -* Prereq 2 -* Prereq 3 +http://vmware.github.io/vic/ -### Build & Run - -1. Step 1 -2. Step 2 -3. Step 3 - -## Documentation +https://code.vmware.com/web/sdk/65/web-client ## Releases & Major Branches +https://github.com/vmware/vic-ui/releases + ## Contributing The vic-ui project team welcomes contributions from the community. If you wish to contribute code and you have not diff --git a/h5c/.gitignore b/h5c/.gitignore new file mode 100644 index 000000000..16cd68854 --- /dev/null +++ b/h5c/.gitignore @@ -0,0 +1,9 @@ +# build artifacts +vic/src/main/webapp/locales/ +vic/src/main/webapp/resources/dist/ +vic/src/main/webapp/resources/build-dev/ +vic/src/vic-app/coverage/ + +# build shortcuts +dev-build.sh +prod-build.sh diff --git a/h5c/build-deployable.xml b/h5c/build-deployable.xml new file mode 100644 index 000000000..eef7cb440 --- /dev/null +++ b/h5c/build-deployable.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Building HTML5 plugin... + + + + + + + + + + + + + key_h5c="${pluginPackage(id)}" + + + ${pluginPackage(id)} v${pluginPackage(version)} + ${PACKAGE_NAME} was created + + + diff --git a/h5c/vic-service/.classpath b/h5c/vic-service/.classpath new file mode 100644 index 000000000..c722a3774 --- /dev/null +++ b/h5c/vic-service/.classpath @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic-service/.project b/h5c/vic-service/.project new file mode 100644 index 000000000..e890d6986 --- /dev/null +++ b/h5c/vic-service/.project @@ -0,0 +1,30 @@ + + + vic-service + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + + org.eclipse.virgo.ide.facet.core.bundlenature + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/h5c/vic-service/.settings/org.eclipse.jdt.core.prefs b/h5c/vic-service/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..62492222a --- /dev/null +++ b/h5c/vic-service/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/h5c/vic-service/.settings/org.eclipse.m2e.core.prefs b/h5c/vic-service/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/h5c/vic-service/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/h5c/vic-service/.settings/org.eclipse.wst.common.project.facet.core.xml b/h5c/vic-service/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 000000000..3a7a03cd9 --- /dev/null +++ b/h5c/vic-service/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/h5c/vic-service/build-java.bat b/h5c/vic-service/build-java.bat new file mode 100644 index 000000000..98af0f20d --- /dev/null +++ b/h5c/vic-service/build-java.bat @@ -0,0 +1,35 @@ +@echo off +REM Copyright 2017 VMware, Inc. All Rights Reserved. +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM 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, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. +REM +REM --- Windows script +REM --- (if Ant runs out of memory try defining ANT_OPTS=-Xmx512M) + +@IF not defined ANT_HOME ( + @echo BUILD FAILED: You must set the env variable ANT_HOME to your Apache Ant folder + goto end +) +@IF not defined VSPHERE_SDK_HOME ( + @echo BUILD FAILED: You must set the env variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + goto end +) +@IF not exist "%VSPHERE_SDK_HOME%\libs\vsphere-client-lib.jar" ( + @echo BUILD FAILED: VSPHERE_SDK_HOME is not set to a valid vSphere Client SDK folder + @echo %VSPHERE_SDK_HOME%\libs\vsphere-client-lib.jar is missing + goto end +) + +@call "%ANT_HOME%\bin\ant" -f build-java.xml + +:end diff --git a/h5c/vic-service/build-java.sh b/h5c/vic-service/build-java.sh new file mode 100644 index 000000000..2d9b13cc1 --- /dev/null +++ b/h5c/vic-service/build-java.sh @@ -0,0 +1,33 @@ +#!/bin/bash -e +# Copyright 2017 VMware, Inc. All Rights Reserved. +# +# Licensed 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. +# +# Mac OS script +# Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +if [ -z "$ANT_HOME" ] || [ ! -f "${ANT_HOME}"/bin/ant ] +then + echo BUILD FAILED: You must set the environment variable ANT_HOME to your Apache Ant folder + exit 1 +fi + +if [ -z "$VSPHERE_SDK_HOME" ] || [ ! -f "${VSPHERE_SDK_HOME}"/libs/vsphere-client-lib.jar ] +then + echo BUILD FAILED: You must set the environment variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + exit 1 +fi + +"${ANT_HOME}"/bin/ant -f build-java.xml + +exit 0 diff --git a/h5c/vic-service/build-java.xml b/h5c/vic-service/build-java.xml new file mode 100644 index 000000000..6ddac959c --- /dev/null +++ b/h5c/vic-service/build-java.xml @@ -0,0 +1,115 @@ + + + Ant script to build vic-service. + The output is a bundle in target/vic-service.jar that can be + copied in the "plugins" folder of the plugin-package. + ----------------------------------------------------------------------- + NOTE: in Eclipse/STS you can use the project builder directly. + ----------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic-service/src/main/java/com/vmware/utils/ssl/ThumbprintHostNameVerifier.java b/h5c/vic-service/src/main/java/com/vmware/utils/ssl/ThumbprintHostNameVerifier.java new file mode 100644 index 000000000..2bafc441b --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/utils/ssl/ThumbprintHostNameVerifier.java @@ -0,0 +1,35 @@ +/* Copyright 2016 VMware, Inc. All rights reserved. -- VMware Confidential */ +package com.vmware.utils.ssl; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +public class ThumbprintHostNameVerifier implements HostnameVerifier { + + @Override + public boolean verify(String host, SSLSession session) { + try { + Certificate[] certificates = session.getPeerCertificates(); + verify(host, (X509Certificate) certificates[0]); + return true; + } catch (SSLException e) { + return false; + } + } + + private void verify(String host, X509Certificate cert) throws SSLException { + try { + String thumbprint = ThumbprintTrustManager.getThumbprint(cert); + boolean hasThumbprint = ThumbprintTrustManager.checkThumbprint(thumbprint); + if (!hasThumbprint) { + throw new SSLException("Server certificate chain is not trusted and thumbprint doesn't match"); + } + } catch (CertificateException e) { + throw new SSLException(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/h5c/vic-service/src/main/java/com/vmware/utils/ssl/ThumbprintTrustManager.java b/h5c/vic-service/src/main/java/com/vmware/utils/ssl/ThumbprintTrustManager.java new file mode 100644 index 000000000..d6bd98310 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/utils/ssl/ThumbprintTrustManager.java @@ -0,0 +1,91 @@ +/* Copyright 2016 VMware, Inc. All rights reserved. -- VMware Confidential */ +package com.vmware.utils.ssl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.SSLException; + +public class ThumbprintTrustManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager { + + private static final Log _logger = LogFactory.getLog(ThumbprintTrustManager.class); + private static Set _thumbprints = new HashSet(); + + public static void setThumbprints(Set thumbprints) { + synchronized (ThumbprintTrustManager.class) { + _thumbprints = thumbprints; + } + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + boolean isContainCert = false; + for (java.security.cert.X509Certificate cert : certs) { + String thumbprint = getThumbprint(cert); + if (checkThumbprint(thumbprint)) { + isContainCert = true; + break; + } + } + + if (!isContainCert) { + throw new CertificateException("Server certificate chain is not trusted and thumbprint doesn't match"); + } + + } + + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) + throws CertificateException { + return; + } + + public static String getThumbprint(java.security.cert.X509Certificate cert) + throws java.security.cert.CertificateException { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] certBytes = cert.getEncoded(); + byte[] bytes = md.digest(certBytes); + + StringBuilder builder = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xFF & b); + if (hex.length() == 1) { + builder.append("0"); + } + builder.append(hex); + } + return builder.toString().toLowerCase(); + } catch (NoSuchAlgorithmException ex) { + return null; + } + } + + public static boolean checkThumbprint(String thumbprint) { + synchronized (ThumbprintTrustManager.class) { + if (_thumbprints.contains(thumbprint)) { + _logger.info("expected one of this thumbprints: " + _thumbprints + "\n" + "actual thumbprint: " + "[" + + thumbprint + "]" + "...thumbprints matching ok!"); + return true; + } + + _logger.error("Server certificate chain is not trusted " + "and thumbprint doesn't match\n" + + "expected one " + "of this " + _thumbprints + "\n" + "actual: " + "[" + thumbprint + "]" + + "...matching failed!!"); + return false; + + } + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/ModelObjectUriResolver.java b/h5c/vic-service/src/main/java/com/vmware/vic/ModelObjectUriResolver.java new file mode 100644 index 000000000..04956f76b --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/ModelObjectUriResolver.java @@ -0,0 +1,181 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic; + +import java.net.URI; +import java.net.URISyntaxException; + +import com.vmware.vise.data.uri.ResourceTypeResolver; + +public class ModelObjectUriResolver implements ResourceTypeResolver { + private static final String SCHEME = "urn"; + private static final String NAMESPACE = "vic"; + private static final String UID_PREFIX = SCHEME + ":" + NAMESPACE; + + private static final String TYPE_DELIMITER = ":"; + private static final String FRAGMENT_SEPARATOR = "/"; + + /* + * (non-Javadoc) + * @see com.vmware.vise.data.uri.ResourceTypeResolver#getResourceType(java.net.URI) + */ + @Override + public String getResourceType(URI uri) { + if (!isValid(uri)) { + throwIllegalURIException(uri); + } + + return parseUri(uri, true); + } + + /* + * (non-Javadoc) + * @see com.vmware.vise.data.uri.ResourceTypeResolver#getServerGuid(java.net.URI) + */ + @Override + public String getServerGuid(URI uri) { + String id = parseUri(uri, false); + int fragmentSeperatorIndex = id.indexOf(FRAGMENT_SEPARATOR); + if (fragmentSeperatorIndex <= 0) { + throwIllegalURIException(uri); + } + return id.substring(0, fragmentSeperatorIndex); + } + + /** + * Parse URI object according to parseType given + * @param uri + * @param parseType + * @return resourceType if parseType is true + resourceId if parseType is false + */ + private String parseUri(URI uri, boolean parseType) { + // get substring after urn: + String ssPart = uri.getSchemeSpecificPart(); + int typeIndex = ssPart.indexOf(TYPE_DELIMITER); + ssPart = ssPart.substring(typeIndex + 1); + int resourceIndex = ssPart.lastIndexOf(TYPE_DELIMITER); + + if (resourceIndex == -1) { + throw new IllegalArgumentException( + "Invalid URI. Missing type delimiter " + toString(uri)); + } + + String result; + if (parseType) { + result = ssPart.substring(0, resourceIndex); + } else { + result = ssPart.substring(resourceIndex + 1); + } + + return result; + } + + /** + * Generate a URI instance with resource type and id + * @param type + * @param id + * @return URI + */ + public URI createUri(String type, String id) { + if (type == null || type.length() < 1) { + throw new IllegalArgumentException("type must be non-null"); + } + + if (id == null || id.length() < 1) { + throw new IllegalArgumentException("id must be non-null"); + } + + URI uri = null; + try { + String schemeSpecificPart = + NAMESPACE + TYPE_DELIMITER + type + TYPE_DELIMITER + id; + uri = new URI(SCHEME, schemeSpecificPart, null); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + return uri; + } + + /** + * Return resourceId + * @param uri + * @return resource id of URI (e.g. server1/vic-root + from URI urn:vic:vic:root:server1/vic-root) + */ + public String getId(URI uri) { + if (!isValid(uri)) { + throwIllegalURIException(uri); + } + return parseUri(uri, false); + } + + /** + * Return objectId of resourceId + * @param uri + * @return object id of resourceId (e.g. vic-root from + * server1/vic-root) + */ + public String getObjectId(URI uri) { + String id = parseUri(uri, false); + int fragmentSeperatorIndex = id.indexOf(FRAGMENT_SEPARATOR); + if (fragmentSeperatorIndex <= 0) { + throwIllegalURIException(uri); + } + return id.substring(fragmentSeperatorIndex + 1); + } + + /** + * Return the string representation of the URI + * @param uri + * @return uri.toString() + */ + public String getUid(URI uri) { + if (!isValid(uri)) { + throwIllegalURIException(uri); + } + return uri.toString(); + } + + /** + * Create and return the resourceId based on serverGuid and objectId + * @param serverGuid + * @param objectId + * @return serverGuid appended by FRAGMENT_SEPARATOR and objectId such that + * the object can be used uniquely identified within any server + */ + public String createResourceId(String serverGuid, String objectId) { + return serverGuid + FRAGMENT_SEPARATOR + objectId; + } + + private boolean isValid(URI uri) { + return (uri != null) && uri.toString().startsWith(UID_PREFIX); + } + + private void throwIllegalURIException(URI uri) { + throw new IllegalArgumentException("URI " + + toString(uri) + " is invalid for this resolver"); + } + + private String toString(URI uri) { + if (uri == null) { + return null; + } + return uri.toString(); + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/ObjectStore.java b/h5c/vic-service/src/main/java/com/vmware/vic/ObjectStore.java new file mode 100644 index 000000000..dd031534d --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/ObjectStore.java @@ -0,0 +1,176 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.Root; +import com.vmware.vic.model.RootInfo; +import com.vmware.vic.model.VmQueryResult; +import com.vmware.vic.utils.ConfigLoader; +import com.vmware.vim25.InvalidPropertyFaultMsg; +import com.vmware.vim25.RuntimeFaultFaultMsg; +import com.vmware.vise.data.query.DataService; +import com.vmware.vise.data.query.PropertyValue; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.vim.data.VimObjectReferenceService; + +public class ObjectStore { + private Root _currentRootObject; + private final DataService _dataService; + private final ModelObjectUriResolver _modelObjectUriResolver; + private final ConfigLoader _configLoader; + private final PropFetcher _propFetcher; + private final VimObjectReferenceService _objectRefService; + private final static String CONFIG_PROP_FILES = "configs.properties"; + private final static String UI_VERSION_CONFIG_KEY = "uiVersion"; + private final static String ROOT_TYPE = VicUIDataAdapter.ROOT_TYPE; + private final static String VCH_TYPE = VicUIDataAdapter.VCH_TYPE; + private final static String CONTAINER_TYPE = VicUIDataAdapter.CONTAINER_TYPE; + private final static Log _logger = LogFactory.getLog(ObjectStore.class); + + public ObjectStore( + DataService dataService, + ModelObjectUriResolver modelObjectUriResolver, + PropFetcher propFetcher, + VimObjectReferenceService objectRefService) throws IOException { + if (dataService == null || + modelObjectUriResolver == null || + propFetcher == null || + objectRefService == null) { + throw new IllegalArgumentException("constructor arg cannot be null"); + } + _dataService = dataService; + _modelObjectUriResolver = modelObjectUriResolver; + _currentRootObject = null; + _configLoader = new ConfigLoader(CONFIG_PROP_FILES); + _propFetcher = propFetcher; + _objectRefService = objectRefService; + } + + /** + * Initialize the ObjectStore + * @throws IOException + */ + public void init() { + _currentRootObject = new Root( + new RootInfo(new String[]{ + _configLoader.getProp(UI_VERSION_CONFIG_KEY)}), 0, 0); + } + + public void destroy() { + // nothing to clean up yet + } + + /** + * @return the Root object that contains the # of VirtualContainerHostVms + * and # of ContainerVms + * @throws RuntimeFaultFaultMsg + * @throws InvalidPropertyFaultMsg + */ + private Root getRootObject() { + synchronized(_currentRootObject) { + ResultItem vchsRi = _propFetcher.getVicVms(true); + ResultItem containersRi = _propFetcher.getVicVms(false); + + int numberOfVchs = vchsRi.properties.length; + int numberOfContainers = containersRi.properties.length; + + Root rootObj = new Root( + new RootInfo(new String[]{ + _configLoader.getProp(UI_VERSION_CONFIG_KEY)}), + numberOfVchs, + numberOfContainers); + _currentRootObject = rootObj; + + return _currentRootObject; + } + } + + /** + * Get Root, VirtualContainerHostVm or ContainerVm based on URI + * @param uri + * @return All VCH VMs if uri relates to vic:VirtualContainerHostVm:vic/ALL. + Otherwise returns the specified VCH VM. + If the resourceType is vic:ContainerVm, returns all Container VMs. + Also returns parent vApp's information. + */ + public ModelObject getObj(URI uri) { + String resourceType = _modelObjectUriResolver.getResourceType(uri); + + if (ROOT_TYPE.equals(resourceType)) { + return getRootObject(); + } else if (VCH_TYPE.equals(resourceType)) { + return getVms(uri, true); + } else if (CONTAINER_TYPE.equals(resourceType)) { + return getVms(uri, false); + } + return null; + } + + /** + * Get Root model's URI. This is to be used with Simple Constraint + * @return URI for Root model + */ + public URI getRootUri() { + try { + URI uri = new URI("urn", String.format("%s:%s:%s/%s", + "vic", ROOT_TYPE, "vic", "vic-root"), null); + return uri; + } catch (URISyntaxException e) { + _logger.error(e.getMessage()); + return null; + } + } + + /** + * Get vApp(s) and return VMs + * @param uri + * @param isVch + * @return VmQueryResult containing results for VCH VMs or Container VMs + */ + private VmQueryResult getVms(URI uri, boolean isVch) { + ResultItem vmsRi = _propFetcher.getVicVms(isVch); + Map resultsMap = new HashMap(); + for (PropertyValue pv : vmsRi.properties) { + if (pv.propertyName == "vm") { + ModelObject mo = (ModelObject)pv.value; + resultsMap.put(mo.getId(), mo); + } + } + + VmQueryResult vmQueryResult = new VmQueryResult( + resultsMap, + _objectRefService); + + return vmQueryResult; + } + + public URI createUri(String type, String id) { + return _modelObjectUriResolver.createUri(type, id); + } + +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/PropFetcher.java b/h5c/vic-service/src/main/java/com/vmware/vic/PropFetcher.java new file mode 100644 index 000000000..266c62316 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/PropFetcher.java @@ -0,0 +1,767 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.TrustManager; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.handler.MessageContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.vmware.utils.ssl.ThumbprintHostNameVerifier; +import com.vmware.utils.ssl.ThumbprintTrustManager; +import com.vmware.vic.model.ContainerVm; +import com.vmware.vic.model.VicApplianceVm; +import com.vmware.vic.model.VirtualContainerHostVm; +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vic.model.constants.Container; +import com.vmware.vic.model.constants.Vch; +import com.vmware.vic.model.constants.VsphereObjects; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.InvalidPropertyFaultMsg; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.NotFoundFaultMsg; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.ObjectSpec; +import com.vmware.vim25.OptionValue; +import com.vmware.vim25.PropertyFilterSpec; +import com.vmware.vim25.PropertySpec; +import com.vmware.vim25.RetrieveOptions; +import com.vmware.vim25.RetrieveResult; +import com.vmware.vim25.RuntimeFaultFaultMsg; +import com.vmware.vim25.ServiceContent; +import com.vmware.vim25.TraversalSpec; +import com.vmware.vim25.UserSearchResult; +import com.vmware.vim25.VimPortType; +import com.vmware.vim25.VimService; +import com.vmware.vim25.VirtualMachineConfigInfo; +import com.vmware.vise.data.query.PropertyValue; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.security.ClientSessionEndListener; +import com.vmware.vise.usersession.ServerInfo; +import com.vmware.vise.usersession.UserSession; +import com.vmware.vise.usersession.UserSessionService; +import com.vmware.vise.vim.data.VimObjectReferenceService; +import com.vmware.vic.cache.LocalCache; + +public class PropFetcher implements ClientSessionEndListener { + private static final Log _logger = LogFactory.getLog(PropFetcher.class); + private static VimPortType _vimPort = initializeVimPort(); + private static final String[] VIC_VM_TYPES = { "isVCH", "isContainer" }; + private static final String SERVICE_INSTANCE = "ServiceInstance"; + private static final Set _thumbprints = new HashSet(); + private static final String[] VM_PROPERTIES_TO_FETCH = new String[] { BaseVm.VM_NAME, + BaseVm.Config.VM_GUESTFULLNAME, BaseVm.Config.VM_EXTRACONFIG, BaseVm.VM_OVERALL_STATUS, BaseVm.VM_SUMMARY, + BaseVm.VM_RESOURCECONFIG, BaseVm.VM_RESOURCEPOOL, BaseVm.Runtime.VM_POWERSTATE_FULLPATH }; + private static final String VM_GUESTNAME_VCH_IDENTIFIER = "Photon - VCH"; + private static final String[] VM_GUESTNAME_CONTAINER_IDENTIFIER = new String[] { "Photon - Container", + "Redhat - Container", "Windows - Container" }; + private static final String GROUP_ADMINISTRATORS = "Administrators"; + private static final String VICUI_H5C_EXTENSION_KEY = "com.vmware.vic"; + private final UserSessionService _userSessionService; + private final VimObjectReferenceService _vimObjectReferenceService; + private Object _rpMorValueToVchLock = new Object(); + private Map _rpMorValueVchEndpointNameMap = new HashMap(); + private Map _rpMorValueVchMorValueMap = new HashMap(); + + private static VimPortType initializeVimPort() { + VimService vimService = new VimService(); + return vimService.getVimPort(); + } + + static { + HostnameVerifier hostNameVerifier = new ThumbprintHostNameVerifier(); + HttpsURLConnection.setDefaultHostnameVerifier(hostNameVerifier); + + TrustManager[] trustManagers = new TrustManager[1]; + TrustManager trustManager = new ThumbprintTrustManager(); + trustManagers[0] = trustManager; + SSLContext sslContext = null; + + try { + sslContext = SSLContext.getInstance("TLS"); + } catch (NoSuchAlgorithmException e) { + _logger.error(e); + } + + if (null != sslContext) { + try { + sslContext.init(null, trustManagers, null); + } catch (KeyManagementException e) { + _logger.error(e); + } + + SSLSessionContext sslSessionContext = sslContext.getServerSessionContext(); + sslSessionContext.setSessionTimeout(0); + + HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); + } + } + + public PropFetcher(UserSessionService userSessionService, VimObjectReferenceService vimObjectReferenceService) { + if (userSessionService == null || vimObjectReferenceService == null) { + throw new IllegalArgumentException("constructor argument cannot be null"); + } + _userSessionService = userSessionService; + _vimObjectReferenceService = vimObjectReferenceService; + } + + /** + * Look up the current session user in UserDirectory and check if the user + * belongs to the vSphere administrators group + * + * @return true if admin + */ + public boolean isSessionUserVsphereAdmin() { + ServerInfo[] sInfos = _userSessionService.getUserSession().serversInfo; + String login = _userSessionService.getUserSession().userName; + String[] loginSplit = login.split("@"); + String userName = loginSplit[0]; + String domain = ""; + if (loginSplit.length > 1) { + domain = loginSplit[1]; + } + + for (ServerInfo sInfo : sInfos) { + if (sInfo.serviceGuid != null) { + String serverGuid = sInfo.serviceGuid; + ServiceContent service = getServiceContent(serverGuid); + if (service == null) { + _logger.error("Failed to retrieve ServiceContent!"); + return false; + } + + try { + List userSrchResults = _vimPort.retrieveUserGroups(service.getUserDirectory(), + domain, userName, GROUP_ADMINISTRATORS, null, true, true, false); + + return userSrchResults.size() == 1; + } catch (NotFoundFaultMsg e) { + _logger.warn(e.getMessage()); + } catch (RuntimeFaultFaultMsg e) { + _logger.error(e.getMessage()); + } + } + } + return false; + } + + /** + * Get VIC VMs + * + * @param isVch : true for VCHs and false for Container VMs + * @return ResultItem object containing either VCH VM(s) or Container VM(s) + * based on the isVch boolean value + */ + synchronized public ResultItem getVicVms(boolean isVch) { + LocalCache instance = LocalCache.getInstance(); + if (isVch && instance.get("vch") != null) { + return (ResultItem) instance.get("vch"); + } else if (!isVch && instance.get("containerVm") != null) { + return (ResultItem) instance.get("containerVm"); + } + + List pvList = new ArrayList(); + ResultItem resultItem = new ResultItem(); + + ServerInfo[] sInfos = _userSessionService.getUserSession().serversInfo; + // get VMs for every linked VC + for (ServerInfo sInfo : sInfos) { + if (sInfo.serviceGuid != null) { + String serviceGuid = sInfo.serviceGuid; + ServiceContent service = getServiceContent(serviceGuid); + if (service == null) { + _logger.error("Failed to retrieve ServiceContent!"); + return null; + } + + ManagedObjectReference viewMgrRef = service.getViewManager(); + List vmList = new ArrayList(); + vmList.add(VsphereObjects.VirtualMachine); + try { + ManagedObjectReference cViewRef = _vimPort.createContainerView(viewMgrRef, service.getRootFolder(), + vmList, true); + + PropertySpec propertySpec = new PropertySpec(); + propertySpec.setType(VsphereObjects.VirtualMachine); + List pSpecPathSet = propertySpec.getPathSet(); + for (String vmProp : VM_PROPERTIES_TO_FETCH) { + pSpecPathSet.add(vmProp); + } + + PropertySpec propertySpecRp = new PropertySpec(); + propertySpecRp.setType(VsphereObjects.ResourcePool); + List pSpecPathSetRp = propertySpecRp.getPathSet(); + pSpecPathSetRp.add(VsphereObjects.NamePropertyKey); + pSpecPathSetRp.add(VsphereObjects.VmPropertyValueKey); + + // set the root traversal spec + TraversalSpec tSpec = new TraversalSpec(); + tSpec.setName("traverseEntities"); + tSpec.setPath("view"); + tSpec.setSkip(false); + tSpec.setType("ContainerView"); + + // add traversal spec for VirtualMachine->ResourcePool + TraversalSpec vmRpTraversalSpec = new TraversalSpec(); + vmRpTraversalSpec.setName("traverseResourcePool"); + vmRpTraversalSpec.setPath("resourcePool"); + vmRpTraversalSpec.setSkip(false); + vmRpTraversalSpec.setType(VsphereObjects.VirtualMachine); + tSpec.getSelectSet().add(vmRpTraversalSpec); + + TraversalSpec rpVmTraversalSpec = new TraversalSpec(); + rpVmTraversalSpec.setName("traversalRpVm"); + rpVmTraversalSpec.setPath(VsphereObjects.VmPropertyValueKey); + rpVmTraversalSpec.setSkip(false); + rpVmTraversalSpec.setType(VsphereObjects.ResourcePool); + vmRpTraversalSpec.getSelectSet().add(rpVmTraversalSpec); + + // set objectspec and attach the root traversal spec + ObjectSpec objectSpec = new ObjectSpec(); + objectSpec.setObj(cViewRef); + objectSpec.setSkip(Boolean.TRUE); + objectSpec.getSelectSet().add(tSpec); + + PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec(); + propertyFilterSpec.getPropSet().add(propertySpec); + propertyFilterSpec.getPropSet().add(propertySpecRp); + propertyFilterSpec.getObjectSet().add(objectSpec); + + List propertyFilterSpecs = new ArrayList(); + propertyFilterSpecs.add(propertyFilterSpec); + RetrieveOptions ro = new RetrieveOptions(); + + RetrieveResult props = _vimPort.retrievePropertiesEx(service.getPropertyCollector(), + propertyFilterSpecs, ro); + if (props != null) { + // continue fetching all results + while (true) { + for (ObjectContent objC : props.getObjects()) { + List dpList = objC.getPropSet(); + String objType = objC.getObj().getType(); + String objMorValue = objC.getObj().getValue(); + boolean isVicVm = false; + + // if it's a VCH VM, store its resourcePool MOR value and the VM's MOR value in + // rpMorValueTovchMorValue map + + if (objType.equals(VsphereObjects.VirtualMachine)) { + // process VirtualMachine by looking for + // config.guestFullName to determine if the VM is + // the desired VIC VM. if so, then set flag isVicVm to + // true such that this VirtualMachine object will be + // processed to be returned in the ResultItem object + boolean isVchEndpoint = false; + String resourcePoolMorValue = null; + String vmName = null; + String vmMorValue = objMorValue; + + for (DynamicProperty dp : dpList) { + if (dp.getName().equals(BaseVm.Config.VM_GUESTFULLNAME)) { + String guestName = ((String) dp.getVal()); + if (isVch) { + isVicVm = guestName.contains(VM_GUESTNAME_VCH_IDENTIFIER); + isVchEndpoint = isVicVm; + } else { + for (String contId : VM_GUESTNAME_CONTAINER_IDENTIFIER) { + if (guestName.contains(contId)) { + isVicVm = true; + break; + } + } + } + } else if (dp.getName().equals(BaseVm.VM_NAME)) { + vmName = (String) dp.getVal(); + } else if (dp.getName().equals(BaseVm.VM_RESOURCEPOOL)) { + ManagedObjectReference mor = (ManagedObjectReference) dp.getVal(); + resourcePoolMorValue = mor.getValue(); + } + } + + if (isVchEndpoint) { + synchronized (_rpMorValueToVchLock) { + // if this is a VCH VM, put the the following information into maps: + // 1. parent resourcePool's MOR value - endpoint VM's name + // 2. parent resourcePool's MOR value - endpoint VM's MOR value + _rpMorValueVchEndpointNameMap.put(resourcePoolMorValue, vmName); + _rpMorValueVchMorValueMap.put(resourcePoolMorValue, vmMorValue); + } + } + } + + // if this ObjectContent is indeed either a VCH VM or a + // Container VM then create an instance of its class and + // add it to the array list + if (isVicVm) { + PropertyValue pv = new PropertyValue(); + pv.propertyName = VsphereObjects.VmPropertyValueKey; + if (isVch) { + pv.value = new VirtualContainerHostVm(objC, serviceGuid); + } else { + pv.value = new ContainerVm(objC, serviceGuid); + } + pvList.add(pv); + continue; + } + } + + // if requesting Container VMs, get the name of its + // parent ResourcePool or VirtualApp for each + if (!isVch) { + for (PropertyValue pv : pvList) { + // for each container vm, get the parent object's + // name from vcValueRpNameMap + ContainerVm cvm = (ContainerVm) pv.value; + ManagedObjectReference parentRpMor = (ManagedObjectReference) cvm.getResourcePool(); + String nameOfParent = _rpMorValueVchEndpointNameMap.get(parentRpMor.getValue()); + String morValueOfVchEndpointVm = _rpMorValueVchMorValueMap + .get(parentRpMor.getValue()); + cvm.setVchEndpointVmMorValue(morValueOfVchEndpointVm); + cvm.setParentName(nameOfParent); + } + } + + // if there is no more data to fetch, break out of the loop + if (props.getToken() == null || props.getToken().isEmpty()) { + break; + } + + // otherwise continue loading data + RetrieveResult propsContinued = _vimPort + .continueRetrievePropertiesEx(service.getPropertyCollector(), props.getToken()); + props = propsContinued; + } + } + } catch (InvalidPropertyFaultMsg e) { + _logger.error(e); + } catch (RuntimeFaultFaultMsg e) { + _logger.error(e); + } + } + } + + resultItem.properties = pvList.toArray(new PropertyValue[] {}); + if (isVch) { + instance.set("vch", (Object) resultItem); + } else { + instance.set("containerVm", (Object) resultItem); + } + return resultItem; + } + + /** + * Return an array of VIC appliances information + * + * @return List containing all VIC appliance VMs + * @throws RuntimeFaultFaultMsg + * @throws InvalidPropertyFaultMsg + */ + synchronized public List getVicApplianceVms() throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg { + ArrayList vicAppliancesList = new ArrayList(); + UserSession userSession = _userSessionService.getUserSession(); + + for (ServerInfo sinfo : userSession.serversInfo) { + try { + ServiceContent service = getServiceContent(sinfo.serviceGuid); + if (service == null) { + _logger.error("Failed to retrieve ServiceContent!"); + return null; + } + + List vmList = new ArrayList(); + vmList.add(VsphereObjects.VirtualMachine); + + PropertySpec propertySpec = new PropertySpec(); + propertySpec.setType(VsphereObjects.VirtualMachine); + List pSpecPathSet = propertySpec.getPathSet(); + pSpecPathSet.add(BaseVm.VM_NAME); + pSpecPathSet.add(BaseVm.VM_SUMMARY); + + List applianceVms = _vimPort.queryManagedBy(service.getExtensionManager(), + VICUI_H5C_EXTENSION_KEY); + + if (applianceVms.size() == 0) { + continue; + } + + List propertyFilterSpecs = new ArrayList(); + + for (ManagedObjectReference mor : applianceVms) { + ObjectSpec objectSpec = new ObjectSpec(); + objectSpec.setObj(mor); + objectSpec.setSkip(Boolean.FALSE); + + PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec(); + propertyFilterSpec.getPropSet().add(propertySpec); + propertyFilterSpec.getObjectSet().add(objectSpec); + propertyFilterSpecs.add(propertyFilterSpec); + } + + RetrieveOptions ro = new RetrieveOptions(); + RetrieveResult props = _vimPort.retrievePropertiesEx(service.getPropertyCollector(), + propertyFilterSpecs, ro); + + if (props != null) { + for (ObjectContent objC : props.getObjects()) { + VicApplianceVm applianceVm = new VicApplianceVm(objC); + String ip = applianceVm.getIpAddress(); + String hostnameCanonical = ip; + if (ip != null) { + try { + InetAddress address = InetAddress.getByName(ip); + hostnameCanonical = address.getCanonicalHostName(); + + } catch (UnknownHostException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + vicAppliancesList.add(applianceVm.getName() + ": " + applianceVm.getVersionString() + "," + + hostnameCanonical); + + } + } + } catch (RuntimeFaultFaultMsg | InvalidPropertyFaultMsg e) { + e.printStackTrace(); + } + } + + Collections.sort(vicAppliancesList, new Comparator() { + @Override + public int compare(String a, String b) { + String[] aSplit = a.split(","); + String[] bSplit = b.split(","); + if (aSplit != null && aSplit.length > 1 && bSplit != null && bSplit.length > 1) { + String versionA = aSplit[0]; + String versionB = bSplit[0]; + String[] aVersionArr = versionA.split("-"); + String[] bVersionArr = versionB.split("-"); + int aBuildNumber = Integer.parseInt(aVersionArr[aVersionArr.length - 2]); + int bBuildNumber = Integer.parseInt(bVersionArr[bVersionArr.length - 2]); + return aBuildNumber > bBuildNumber ? -1 : (aBuildNumber < bBuildNumber ? 1 : 0); + } else { + return 0; + } + + } + }); + + return vicAppliancesList; + } + + /** + * Get VMs belonging to a given vApp object reference. + * + * @param objRef + * @param isVch + * @return ResultItem object containing either VCH VM(s) or Container VM(s) + * based on the isVch boolean value + * @throws InvalidPropertyFaultMsg + * @throws RuntimeFaultFaultMsg + */ + public ResultItem getVmsBelongingToMor(Object objRef, boolean isVch) + throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg { + List pvList = new ArrayList(); + ResultItem resultItem = new ResultItem(); + resultItem.resourceObject = objRef; + + String entityType = _vimObjectReferenceService.getResourceObjectType(objRef); + String entityName = _vimObjectReferenceService.getValue(objRef); + String serverGuid = _vimObjectReferenceService.getServerGuid(objRef); + + ManagedObjectReference mor = new ManagedObjectReference(); + mor.setType(entityType); + mor.setValue(entityName); + + ServiceContent service = getServiceContent(serverGuid); + if (service == null) { + _logger.error("Failed to retrieve ServiceContent!"); + return null; + } + + ManagedObjectReference viewMgrRef = service.getViewManager(); + List vmList = new ArrayList(); + vmList.add(VsphereObjects.VirtualMachine); + ManagedObjectReference cViewRef = _vimPort.createContainerView(viewMgrRef, mor, vmList, true); + + PropertySpec propertySpec = new PropertySpec(); + propertySpec.setType(VsphereObjects.VirtualMachine); + List pSpecPathSet = propertySpec.getPathSet(); + pSpecPathSet.add(BaseVm.VM_NAME); + pSpecPathSet.add(BaseVm.VM_SUMMARY); + pSpecPathSet.add(BaseVm.VM_OVERALL_STATUS); + pSpecPathSet.add(BaseVm.Runtime.VM_POWERSTATE_FULLPATH); + pSpecPathSet.add(BaseVm.Config.VM_EXTRACONFIG); + + // set the root traversal spec + TraversalSpec tSpec = new TraversalSpec(); + tSpec.setName("traverseEntities"); + tSpec.setPath("view"); + tSpec.setSkip(false); + tSpec.setType("ContainerView"); + + // set objectspec and attach the root traversal spec + ObjectSpec objectSpec = new ObjectSpec(); + objectSpec.setObj(cViewRef); + objectSpec.setSkip(Boolean.TRUE); + objectSpec.getSelectSet().add(tSpec); + + // set traversal node for VirtualApp->VirtualMachine + TraversalSpec tSpecVappVm = new TraversalSpec(); + tSpecVappVm.setType(VsphereObjects.VirtualApp); + tSpecVappVm.setPath(VsphereObjects.VmPropertyValueKey); + tSpecVappVm.setSkip(false); + tSpec.getSelectSet().add(tSpecVappVm); + + PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec(); + propertyFilterSpec.getPropSet().add(propertySpec); + propertyFilterSpec.getObjectSet().add(objectSpec); + + List propertyFilterSpecs = new ArrayList(); + propertyFilterSpecs.add(propertyFilterSpec); + RetrieveOptions ro = new RetrieveOptions(); + + RetrieveResult props = _vimPort.retrievePropertiesEx(service.getPropertyCollector(), propertyFilterSpecs, ro); + if (props != null) { + for (ObjectContent objC : props.getObjects()) { + // each managed object reference found will be added to resultItem.properties + PropertyValue pv = new PropertyValue(); + pv.propertyName = VsphereObjects.VmPropertyValueKey; + if (isVch) { + pv.value = new VirtualContainerHostVm(objC, serverGuid); + } else { + pv.value = new ContainerVm(objC, serverGuid); + } + + pvList.add(pv); + } + } + resultItem.properties = pvList.toArray(new PropertyValue[] {}); + + return resultItem; + } + + /** + * Compute custom VM properties isContainer and isVCH + * + * @param objRef + * @return ResultItem object containing PropertyValue[] for the the custom VM + * properties + * @throws InvalidPropertyFaultMsg + * @throws RuntimeFaultFaultMsg + */ + public ResultItem getVmProperties(Object objRef) throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg { + ResultItem resultItem = new ResultItem(); + resultItem.resourceObject = objRef; + String entityType = _vimObjectReferenceService.getResourceObjectType(objRef); + String entityName = _vimObjectReferenceService.getValue(objRef); + String serverGuid = _vimObjectReferenceService.getServerGuid(objRef); + + ManagedObjectReference vmMor = new ManagedObjectReference(); + vmMor.setType(entityType); + vmMor.setValue(entityName); + + VirtualMachineConfigInfo config = null; + + // initialize properties isVCH and isContainer + PropertyValue pv_is_vch = new PropertyValue(); + pv_is_vch.resourceObject = objRef; + pv_is_vch.propertyName = VIC_VM_TYPES[0]; + pv_is_vch.value = false; + + PropertyValue pv_is_container = new PropertyValue(); + pv_is_container.resourceObject = objRef; + pv_is_container.propertyName = VIC_VM_TYPES[1]; + pv_is_container.value = false; + + ServiceContent service = getServiceContent(serverGuid); + if (service == null) { + _logger.error("Failed to retrieve ServiceContent!"); + return null; + } + + PropertySpec propertySpec = new PropertySpec(); + propertySpec.setAll(Boolean.FALSE); + propertySpec.setType(VsphereObjects.VirtualMachine); + propertySpec.getPathSet().add("config"); + + ObjectSpec objectSpec = new ObjectSpec(); + objectSpec.setObj(vmMor); + objectSpec.setSkip(Boolean.FALSE); + + PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec(); + propertyFilterSpec.getPropSet().add(propertySpec); + propertyFilterSpec.getObjectSet().add(objectSpec); + + List propertyFilterSpecs = new ArrayList(); + propertyFilterSpecs.add(propertyFilterSpec); + + List objectContents = _vimPort.retrieveProperties(service.getPropertyCollector(), + propertyFilterSpecs); + if (objectContents != null) { + for (ObjectContent content : objectContents) { + List dps = content.getPropSet(); + if (dps != null) { + for (DynamicProperty dp : dps) { + config = (VirtualMachineConfigInfo) dp.getVal(); + + List extraConfigs = config.getExtraConfig(); + for (OptionValue option : extraConfigs) { + + if (option.getKey().equals(Container.VM_EXTRACONFIG_CONTAINER_KEY)) { + pv_is_container.value = true; + break; + } + + if (option.getKey().equals(Vch.VM_EXTRACONFIG_VCH_KEY)) { + pv_is_vch.value = true; + break; + } + } + } + } + } + } + + resultItem.properties = new PropertyValue[] { pv_is_vch, pv_is_container }; + + return resultItem; + } + + /** + * Get ServerInfo with the given serverGuid + * + * @param serverGuid + * @return ServerInfo object corresponding to the specified serverGuid + */ + private ServerInfo getServerInfoObject(String serverGuid) { + UserSession userSession = _userSessionService.getUserSession(); + + for (ServerInfo sinfo : userSession.serversInfo) { + if (sinfo.serviceGuid.equalsIgnoreCase(serverGuid)) { + return sinfo; + } + } + return null; + } + + /** + * Set thumbprint from the ServerInfo object + * + * @param sinfo + */ + private void setThumbprint(ServerInfo sinfo) { + String thumbprint = sinfo.thumbprint; + if (thumbprint != null) { + _thumbprints.add(thumbprint.replaceAll(":", "").toLowerCase()); + } + ThumbprintTrustManager.setThumbprints(_thumbprints); + } + + /** + * Get ServerContent object with the given serverGuid + * + * @param serverGuid + * @return ServiceContent object corresponding to the specified serverGuid + */ + synchronized private ServiceContent getServiceContent(String serverGuid) { + ServerInfo serverInfoObject = getServerInfoObject(serverGuid); + setThumbprint(serverInfoObject); + String sessionCookie = serverInfoObject.sessionCookie; + String serviceUrl = serverInfoObject.serviceUrl; + + List values = new ArrayList(); + values.add("vmware_soap_session=" + sessionCookie); + Map> reqHeadrs = new HashMap>(); + reqHeadrs.put("Cookie", values); + + Map reqContext = ((BindingProvider) _vimPort).getRequestContext(); + reqContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serviceUrl); + reqContext.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true); + reqContext.put(MessageContext.HTTP_REQUEST_HEADERS, reqHeadrs); + + final ManagedObjectReference svgInstanceRef = new ManagedObjectReference(); + svgInstanceRef.setType(SERVICE_INSTANCE); + svgInstanceRef.setValue(SERVICE_INSTANCE); + + ServiceContent serviceContent = null; + try { + serviceContent = _vimPort.retrieveServiceContent(svgInstanceRef); + } catch (RuntimeFaultFaultMsg e) { + _logger.error("getServiceContent error: " + e); + } + + return serviceContent; + } + + @Override + public void sessionEnded(String clientId) { + _logger.info("Logging out client session - " + clientId); + } + + /** + * Obtain a clone ticket from vSphere + * + * @throws Exception + */ + synchronized public String acquireCloneTicket(String serviceGuid) throws Exception { + + ServerInfo[] sInfos = _userSessionService.getUserSession().serversInfo; + + try { + for (ServerInfo sInfo : sInfos) { + if (serviceGuid.equals(sInfo.serviceGuid)) { + ServiceContent service = getServiceContent(serviceGuid); + + if (service == null) { + _logger.error("Failed to retrieve ServiceContent!"); + return null; + } + + ManagedObjectReference sessionMgrRef = service.getSessionManager(); + return _vimPort.acquireCloneTicket(sessionMgrRef); + } + } + } catch (SecurityException e) { + e.printStackTrace(); + } catch (RuntimeFaultFaultMsg e) { + e.printStackTrace(); + } + + throw new RuntimeException("Could not acquire clone ticket for serviceGuid: " + serviceGuid); + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/VicUIDataAdapter.java b/h5c/vic-service/src/main/java/com/vmware/vic/VicUIDataAdapter.java new file mode 100644 index 000000000..97cea431f --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/VicUIDataAdapter.java @@ -0,0 +1,336 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic; + +import java.util.List; +import java.util.Set; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.vmware.vic.model.ModelObject; +import com.vmware.vim25.InvalidPropertyFaultMsg; +import com.vmware.vim25.RuntimeFaultFaultMsg; +import com.vmware.vise.data.Constraint; +import com.vmware.vise.data.PropertySpec; +import com.vmware.vise.data.ResourceSpec; +import com.vmware.vise.data.query.Comparator; +import com.vmware.vise.data.query.CompositeConstraint; +import com.vmware.vise.data.query.DataProviderAdapter; +import com.vmware.vise.data.query.ObjectIdentityConstraint; +import com.vmware.vise.data.query.PropertyConstraint; +import com.vmware.vise.data.query.PropertyValue; +import com.vmware.vise.data.query.QuerySpec; +import com.vmware.vise.data.query.RelationalConstraint; +import com.vmware.vise.data.query.RequestSpec; +import com.vmware.vise.data.query.Response; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.data.query.type; +import com.vmware.vise.vim.data.VimObjectReferenceService; +import com.vmware.vise.data.query.ResultSet; + +@type("vic:Root,vic:VirtualContainerHostVm,vic:ContainerVm,vic:VmQueryResult") +public class VicUIDataAdapter implements DataProviderAdapter { + private static final Log _logger = LogFactory.getLog(VicUIDataAdapter.class); + public static final String ROOT_TYPE = "vic:Root"; + public static final String VCH_TYPE = "vic:VirtualContainerHostVm"; + public static final String CONTAINER_TYPE = "vic:ContainerVm"; + public static final String VMQUERY_RESULT_TYPE = "vic:VmQueryResult"; + + private final VimObjectReferenceService _objRefService; + private final ObjectStore _objectStore; + + public VicUIDataAdapter( + VimObjectReferenceService objRefService, + ObjectStore objectStore + ) { + if (objRefService == null || + objectStore == null) { + throw new IllegalArgumentException("Constructor arguments cannot be null"); + } + _objRefService = objRefService; + _objectStore = objectStore; + } + + /** + * Extends vSphere Client's DataService + */ + @Override + public Response getData(RequestSpec request) throws Exception { + if (request == null) { + throw new IllegalArgumentException("request should be non-null"); + } + + QuerySpec[] querySpecs = request.querySpec; + List results = new ArrayList(querySpecs.length); + + for (QuerySpec querySpec : querySpecs) { + ResultSet resultset = processQuerySpec(querySpec); + results.add(resultset); + } + + Response response = new Response(); + response.resultSet = results.toArray(new ResultSet[] {}); + return response; + } + + /** + * Process QuerySpec to get requested data + * @param querySpec + * @return ResultSet containing requested object(s) + */ + private ResultSet processQuerySpec(QuerySpec querySpec) { + ResultSet rs = new ResultSet(); + if (!validateQuery(querySpec)) { + return rs; + } + + List items = processConstraint( + querySpec.resourceSpec.constraint, + querySpec.resourceSpec.propertySpecs); + rs.queryName = querySpec.name; + rs.totalMatchedObjectCount = (items != null) ? items.size() : 0; + rs.items = items.toArray(new ResultItem[]{}); + + return rs; + } + + /** + * Processes various types of Constraint for consumption with ResultSet + * @param constraint + * @return ResultItem list containing query results + */ + private List processConstraint( + Constraint constraint, + PropertySpec[] propertySpecs) { + List results = null; + if (constraint instanceof ObjectIdentityConstraint) { + ObjectIdentityConstraint oic = (ObjectIdentityConstraint)constraint; + results = processObjectIdentityConstraint(oic, propertySpecs); + } else if (constraint instanceof CompositeConstraint) { + _logger.warn("CompositeConstraint is unsupported"); + } else if (constraint instanceof PropertyConstraint) { + _logger.warn("PropertyConstraint is unsupported"); + } else if (constraint instanceof RelationalConstraint) { + _logger.warn("RelationalConstraint is unsupported"); + } else if (isSimpleConstraint(constraint)) { + results = processSimpleConstraint(constraint, propertySpecs); + } + + if (results == null) { + results = new ArrayList(); + } + return results; + } + + private boolean isSimpleConstraint(Constraint constraint) { + return constraint.getClass().getSimpleName().equals( + Constraint.class.getSimpleName()); + } + + /** + * Process an ObjectIdentityConstraint where constraint.target is a + * specific object for which we need to return requested properties + * @throws RuntimeFaultFaultMsg + * @throws InvalidPropertyFaultMsg + */ + private List processObjectIdentityConstraint( + ObjectIdentityConstraint oic, + PropertySpec[] propertySpecs) { + List items = new ArrayList(); + URI objectUri = toURI(oic.target); + if (objectUri != null) { + ModelObject mo = _objectStore.getObj(objectUri); + if (mo == null) { + _logger.error("ModelObject for " + objectUri + " does not exist!"); + return items; + } + ResultItem ri = createResultItem(mo, objectUri, propertySpecs); + if (ri != null) { + items.add(ri); + } + } + return items; + } + + private List processSimpleConstraint( + Constraint constraint, + PropertySpec[] propertySpecs) { + List items = new ArrayList(); + URI objectUri = null; + + if (ROOT_TYPE.equals(constraint.targetType)) { + objectUri = _objectStore.getRootUri(); + } else if (VCH_TYPE.equals(constraint.targetType)) { + objectUri = _objectStore.createUri(VCH_TYPE, "vic/ALL"); + } else if (CONTAINER_TYPE.equals(constraint.targetType)) { + objectUri = _objectStore.createUri(CONTAINER_TYPE, "vic/ALL"); + } + + if (objectUri != null) { + ModelObject mo = _objectStore.getObj(objectUri); + if (mo == null) { + _logger.error("ModelObject for " + objectUri + " does not exist!"); + return items; + } + ResultItem ri = createResultItem(mo, objectUri, propertySpecs); + if (ri != null) { + items.add(ri); + } + } + + return items; + } + + /** + * Extract requested properties from the given ModelObject + * @param mo + * @param uri + * @param propertySpecs + * @return ResultItem object containing PropertyValues for the given + * ModelObject and propertySpecs + */ + private ResultItem createResultItem( + ModelObject mo, + URI uri, + PropertySpec[] propertySpecs) { + String[] requestedPropertyNames = getPropertyNames(propertySpecs); + try { + if (mo == null) { + throw new IllegalArgumentException( + "ModelObject not found for " + uri.toString()); + } + ResultItem ri = new ResultItem(); + ri.resourceObject = uri; + List pvs = new ArrayList( + propertySpecs.length); + for (String reqPropertyName : requestedPropertyNames) { + Object value = mo.getProperty(reqPropertyName); + if (value != ModelObject.UNSUPPORTED_PROPERTY) { + PropertyValue pv = new PropertyValue(); + pv.propertyName = reqPropertyName; + pv.resourceObject = uri; + pv.value = value; + pvs.add(pv); + } + } + ri.properties = pvs.toArray(new PropertyValue[]{}); + return ri; + } catch (Exception ex) { + _logger.error("Error getting the ResultItem for " + uri, ex); + return null; + } + } + + /** + * Extract property names from PropertySpec[] + * @param propertySpecs + * @return String[] containing property names + */ + private String[] getPropertyNames(PropertySpec[] propertySpecs) { + Set properties = new HashSet(); + if (propertySpecs != null) { + for (PropertySpec pSpec : propertySpecs) { + for (String pName : pSpec.propertyNames) { + properties.add(pName); + } + } + } + return properties.toArray(new String[]{}); + } + + /** + * Cast Object of type URI into URI object + * @param object + * @return (URI) object + */ + private URI toURI(Object object) { + if (!(object instanceof URI)) { + return null; + } + return (URI)object; + } + + /** + * Verify QuerySpec + * @param qs + * @return true if valid, otherwise false + */ + private boolean validateQuery(QuerySpec qs) { + if (qs == null) { + return false; + } + + ResourceSpec resourceSpec = qs.resourceSpec; + if (resourceSpec == null) { + return false; + } + + return validateConstraint(resourceSpec.constraint); + } + + /** + * Validate resourceSpec's constraint + * @param constraint + * @return true if valid, otherwise false + */ + private boolean validateConstraint(Constraint constraint) { + if (constraint == null) { + return false; + } + + if (constraint instanceof ObjectIdentityConstraint) { + String sourceType = _objRefService.getResourceObjectType( + ((ObjectIdentityConstraint)constraint).target); + return isSupportedType(sourceType); + } else if (constraint instanceof CompositeConstraint) { + CompositeConstraint cc = (CompositeConstraint)constraint; + for (Constraint c : cc.nestedConstraints) { + if (!validateConstraint(c)) { + return false; + } + } + return true; + } else if (constraint instanceof PropertyConstraint) { + return isSupportedType(constraint.targetType) && + ((PropertyConstraint)constraint).comparator + .equals(Comparator.TEXTUALLY_MATCHES); + } else if (isSimpleConstraint(constraint)) { + return isSupportedType(constraint.targetType); + } + + _logger.error("querySpec constraint is not supported: " + + constraint.getClass().getName()); + return false; + } + + /** + * Validate targetType of Constraint object + * @param type + * @return true if supported, otherwise false + */ + private boolean isSupportedType(String type) { + return VCH_TYPE.equals(type) || + CONTAINER_TYPE.equals(type) || + ROOT_TYPE.equals(type) || + VMQUERY_RESULT_TYPE.equals(type); + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/VicUIPropertyProvider.java b/h5c/vic-service/src/main/java/com/vmware/vic/VicUIPropertyProvider.java new file mode 100644 index 000000000..925a4dd1e --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/VicUIPropertyProvider.java @@ -0,0 +1,72 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.vmware.vise.data.query.DataServiceExtensionRegistry; +import com.vmware.vise.data.query.PropertyProviderAdapter; +import com.vmware.vise.data.query.PropertyRequestSpec; +import com.vmware.vise.data.query.ResultSet; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.data.query.TypeInfo; + +public class VicUIPropertyProvider implements PropertyProviderAdapter { + private static final Log _logger = LogFactory.getLog(VicUIPropertyProvider.class); + private static final String[] VIC_VM_TYPES = {"isVCH", "isContainer"}; + private final PropFetcher _propFetcher; + + public VicUIPropertyProvider( + DataServiceExtensionRegistry extensionRegistry, + PropFetcher propFetcher) { + TypeInfo vmTypeInfo = new TypeInfo(); + vmTypeInfo.type = "VirtualMachine"; + vmTypeInfo.properties = VIC_VM_TYPES; + TypeInfo[] providerTypes = new TypeInfo[] { vmTypeInfo }; + + _propFetcher = propFetcher; + extensionRegistry.registerDataAdapter(this, providerTypes); + } + + @Override + public ResultSet getProperties(PropertyRequestSpec propertyRequest) { + ResultSet resultSet = new ResultSet(); + + try { + List resultItems = new ArrayList(); + + for (Object objRef : propertyRequest.objects) { + ResultItem resultItem = _propFetcher.getVmProperties(objRef); + if (resultItem != null) { + resultItems.add(resultItem); + } + } + + resultSet.items = resultItems.toArray(new ResultItem[] {}); + + } catch (Exception e) { + _logger.error("VicUIServiceImpl.getProperties error: " + e); + } + + return resultSet; + } +} \ No newline at end of file diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/cache/LocalCache.java b/h5c/vic-service/src/main/java/com/vmware/vic/cache/LocalCache.java new file mode 100644 index 000000000..9ce1e5d12 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/cache/LocalCache.java @@ -0,0 +1,82 @@ +/* + +Copyright 2019 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.cache; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class LocalCache { + + private LocalCache() { + } + + private static LocalCache instance; + + public static LocalCache getInstance() { + if (instance == null) { + synchronized (LocalCache.class) { + if (instance == null) { + instance = new LocalCache(); + } + } + } + return instance; + } + + protected static final Map localCache = new ConcurrentHashMap(); + + private static class ValueObject { + + private Object value; + private long timeout; + + private ValueObject(Object value, long timeout) { + this.value = value; + this.timeout = timeout; + } + + private Object getValue() { + return value; + } + + private long getTimeout() { + return timeout; + } + + } + + public static void set(String key, Object value) { + long currentTime = System.currentTimeMillis(); + localCache.put(key, new ValueObject(value, currentTime + 10000)); + } + + public static Object get(String key) { + long currentTime = System.currentTimeMillis(); + ValueObject valueObject = localCache.get(key); + if (valueObject == null) { + return null; + } + if (currentTime <= valueObject.getTimeout()) { + return valueObject.getValue(); + } else { + localCache.remove(key); + return null; + } + } + +} \ No newline at end of file diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/filters/VicSessionFilter.java b/h5c/vic-service/src/main/java/com/vmware/vic/filters/VicSessionFilter.java new file mode 100644 index 000000000..be5a11c16 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/filters/VicSessionFilter.java @@ -0,0 +1,69 @@ +/* Copyright (c) 2021 VMware, Inc. All Rights Reserved. */ + +package com.vmware.vic.filters; +import static org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.web.context.WebApplicationContext; + +import com.vmware.vise.usersession.UserSessionService; + +/** + * Filters requests that have no user session associated with. + */ +@SuppressWarnings("checkstyle:autowiredFilter") +public class VicSessionFilter implements Filter { + + private static final Log LOG = LogFactory.getLog(VicSessionFilter.class); + + /** + * The session service bean. + */ + @Autowired + public UserSessionService sessionService; + + @Override + public void init(final FilterConfig filterConfig) { + // Enable auto-wiring of beans + WebApplicationContext context = getWebApplicationContext(filterConfig.getServletContext()); + if (context == null) { + System.out.println("org.springframework.web.context. is null"); + } + AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory(); + factory.autowireBean(this); + } + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, + final FilterChain filterChain) + throws IOException, ServletException { + if (sessionService != null && sessionService.getUserSession() == null) { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + final HttpServletResponse httpResponse = (HttpServletResponse) response; + LOG.warn(String.format("Null session detected for a %s request to %s", + httpRequest.getMethod(), httpRequest.getRequestURL())); + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + filterChain.doFilter(request, response); + } + + @Override + public void destroy() { + } + +} \ No newline at end of file diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/ContainerVm.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/ContainerVm.java new file mode 100644 index 000000000..05068988a --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/ContainerVm.java @@ -0,0 +1,201 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +import java.util.List; + +import com.vmware.vic.model.constants.Container; +import com.vmware.vim25.ArrayOfOptionValue; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.OptionValue; +import com.vmware.vim25.ResourceConfigSpec; +import com.vmware.vim25.VirtualMachinePowerState; +import com.vmware.vim25.VirtualMachineSummary; + +public class ContainerVm extends VicBaseVm { + private static final String EXTRACONFIG_CONTAINER_NAME_KEY = + Container.VM_EXTRACONFIG_CONTAINER_KEY; + private static final String EXTRACONFIG_IMAGE_NAME_KEY = + Container.VM_EXTRACONFIG_IMAGE_NAME_KEY; + private static final String EXTRACONFIG_PORTMAPPING_KEY = + Container.VM_EXTRACONFIG_PORTMAPPING_KEY; + private static final String VM_CONTAINERNAME_KEY = + Container.VM_CONTAINERNAME_KEY; + private static final String VM_IMAGENAME_KEY = + Container.VM_IMAGENAME_KEY; + private static final String VM_PORTMAPPING_KEY = + Container.VM_PORTMAPPING_KEY; + private static final String PARENT_OBJ_NAME_KEY = + Container.PARENT_NAME_KEY; + private String _containerName = null; + private String _parentObjectName = null; + private String _vchEndpointVmMorValue = null; + private String _imageName = null; + private String _portMapping = null; + + public ContainerVm(ObjectContent objContent, String serverGuid) { + super(objContent, serverGuid); + processDynamicProperties(objContent.getPropSet()); + } + + /** + * Getter for Docker Container's name + */ + public String getContainerName() { + return _containerName; + } + + /** + * Getter for Parent Object's name + */ + public String getParentObjectName() { + return _parentObjectName; + } + + /** + * Getter for VCH endpoint's ManagedObjectReference value + */ + public String getVchEndpointVmMorValue() { + return _vchEndpointVmMorValue; + } + + /** + * Getter for Docker Container's imageName + */ + public String getImageName() { + return _imageName; + } + + /** + * Getter for Docker Container's portMapping + */ + public String getPortMapping() { + return _portMapping; + } + + /** + * Property getter + * @param property : property to retrieve + */ + @Override + public Object getProperty(String property) { + if ("objectRef".equals(property)) { + return _objectRef; + } else if (VM_KEY_NAME.equals(property)) { + return _vmName; + } else if (VM_KEY_OVERALL_STATUS.equals(property)) { + return _overallStatus; + } else if (VM_KEY_POWERSTATE.equals(property)) { + return _powerState; + } else if (VM_KEY_GUESTFULLNAME.equals(property)) { + return _guestFullName; + } else if (VM_KEY_OVERALLCPUUSAGE.equals(property)) { + return _overallCpuUsage; + } else if (VM_KEY_GUESTMEMORYUSAGE.equals(property)) { + return _guestMemoryUsage; + } else if (VM_KEY_COMMITTEDSTORAGE.equals(property)) { + return _committedStorage; + } else if (VM_CONTAINERNAME_KEY.equals(property)) { + return _containerName; + } else if (PARENT_OBJ_NAME_KEY.equals(property)) { + return _parentObjectName; + } else if (VM_IMAGENAME_KEY.equals(property)) { + return _imageName; + } else if (VM_PORTMAPPING_KEY.equals(property)) { + return _portMapping; + } else if (VM_KEY_RESOURCECONFIG.equals(property)) { + return _resourceConfig; + } else if (VM_KEY_RESOURCEPOOL.equals(property)) { + return _resourcePool; + } + return UNSUPPORTED_PROPERTY; + } + + /** + * Process DynamicProperty[] and extract information + * needed for the ContainerVm model + * @param dpsList : DynamicProperty list from ObjectContent.getPropSet() + */ + @Override + protected void processDynamicProperties(List dpsList) { + for (DynamicProperty dp : dpsList) { + if (dp.getName().equals(VM_KEY_NAME)) { + _vmName = (String)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_OVERALL_STATUS)) { + _overallStatus = (ManagedEntityStatus)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_POWERSTATE)) { + _powerState = (VirtualMachinePowerState)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_SUMMARY)) { + processVmSummary((VirtualMachineSummary)dp.getVal()); + } else if (dp.getName().equals(VM_KEY_CONFIG_EXTRACONFIG)) { + processExtraConfig((ArrayOfOptionValue)dp.getVal()); + } else if (dp.getName().equals(VM_KEY_RESOURCECONFIG)) { + _resourceConfig = (ResourceConfigSpec)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_RESOURCEPOOL)) { + _resourcePool = dp.getVal(); + } + } + } + + /** + * Extract Container information from config.extraConfig + * @param ovs + */ + private void processExtraConfig(ArrayOfOptionValue ovs) { + if (ovs != null) { + for (OptionValue ov : ovs.getOptionValue()) { + String key = ov.getKey(); + if (EXTRACONFIG_CONTAINER_NAME_KEY.equals(key)) { + _containerName = (String)ov.getValue(); + } else if (EXTRACONFIG_IMAGE_NAME_KEY.equals(key)) { + _imageName = (String)ov.getValue(); + } else if (EXTRACONFIG_PORTMAPPING_KEY.equals(key)) { + _portMapping = (String)ov.getValue(); + } + } + } + } + + /** + * Return ManagedObjectReference's value portion + * @return the 'value' of the ManagedObjectReference object + */ + public String getMorValue() { + String[] splitIdString = this.getId().split("/"); + return splitIdString[1]; + } + + /** + * Set _parentObjectName which is the name of this VM's + * parent object (VirtualApp or ResourcePool) + * @param name + */ + public void setParentName(String name) { + _parentObjectName = name; + } + + /** + * Set _parentObjectMorId which is the MOR value for the VCH endpoint VM + * @param name + */ + public void setVchEndpointVmMorValue(String id) { + _vchEndpointVmMorValue = id; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/ModelObject.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/ModelObject.java new file mode 100644 index 000000000..e7f36a12d --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/ModelObject.java @@ -0,0 +1,68 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +import java.net.URI; + +import com.vmware.vic.ModelObjectUriResolver; + +public abstract class ModelObject { + public static final String NAMESPACE = "vic:"; + public static final Object UNSUPPORTED_PROPERTY = new Object(); + + private String _id; + private URI _uri; + private String _type; + + /** + * @return object id + */ + public String getId() { + return _id; + } + + /** + * @param value + */ + protected void setId(String value) { + _id = value; + } + + /** + * @return object type + */ + public String getType() { + if (_type == null) { + _type = NAMESPACE + this.getClass().getSimpleName(); + } + return _type; + } + + /** + * @param resolver + * @return URI for this object + */ + public URI getUri(ModelObjectUriResolver resolver) { + if (_uri == null) { + _uri = resolver.createUri(getType(), _id); + } + return _uri; + } + + public abstract Object getProperty(String property); +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/Root.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/Root.java new file mode 100644 index 000000000..8c2511bf2 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/Root.java @@ -0,0 +1,69 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +public class Root extends ModelObject { + private final String _uiVersion; + private static final String VIC_ROOT = "vSphere Integrated Containers"; + private int _vchVmsLen; + private int _containerVmsLen; + + public Root(RootInfo rootInfo, int vchVmsLen, int containerVmsLen) { + this.setId("vic/vic-root"); + _uiVersion = rootInfo.uiVersion; + _vchVmsLen = vchVmsLen; + _containerVmsLen = containerVmsLen; + } + + public String getName() { + return VIC_ROOT; + } + + public String getUiVersion() { + return _uiVersion; + } + + public int getVchVmsLen() { + return _vchVmsLen; + } + + public int getContainerVmsLen() { + return _containerVmsLen; + } + + @Override + public Object getProperty(String property) { + if ("uiVersion".equals(property)) { + return _uiVersion; + } else if ("vchVmsLen".equals(property)) { + return _vchVmsLen; + } else if ("containerVmsLen".equals(property)) { + return _containerVmsLen; + } else if ("id".equals(property)) { + return this.getId(); + } else if ("name".equals(property)) { + return VIC_ROOT; + } + return UNSUPPORTED_PROPERTY; + } + + @Override + public String toString() { + return "uiVersion: " + _uiVersion + ", vchVms.length: " + _vchVmsLen + ", containerVms.length: " + _containerVmsLen; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/RootInfo.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/RootInfo.java new file mode 100644 index 000000000..7e772598a --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/RootInfo.java @@ -0,0 +1,30 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +public class RootInfo { + public String uiVersion; + + public RootInfo() { + // an empty constructor is required for the AMF serialization to work + } + + public RootInfo(String[] data) { + this.uiVersion = data[0]; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/VicApplianceVm.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/VicApplianceVm.java new file mode 100644 index 000000000..b5518b9d2 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/VicApplianceVm.java @@ -0,0 +1,79 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.VAppProductInfo; +import com.vmware.vim25.VirtualMachineConfigSummary; +import com.vmware.vim25.VirtualMachineGuestSummary; +import com.vmware.vim25.VirtualMachineSummary; + +public class VicApplianceVm { + private String name; + private String moId; + private String ipAddress; + private String versionString; + + public VicApplianceVm(ObjectContent objContent) { + if (objContent == null) { + throw new IllegalArgumentException("constructor argument cannot be null"); + } + + VirtualMachineSummary vmSummary = null; + for (DynamicProperty dp : objContent.getPropSet()) { + String key = dp.getName(); + if (key.equals(BaseVm.VM_NAME)) { + name = (String) dp.getVal(); + continue; + } + + if (key.equals(BaseVm.VM_SUMMARY)) { + vmSummary = (VirtualMachineSummary) dp.getVal(); + } + } + + if (vmSummary != null) { + ManagedObjectReference vmMor = vmSummary.getVm(); + moId = vmMor.getValue(); + VirtualMachineGuestSummary guestSummary = vmSummary.getGuest(); + ipAddress = guestSummary.getIpAddress(); + VirtualMachineConfigSummary summaryConfig = vmSummary.getConfig(); + VAppProductInfo vAppProductInfo = summaryConfig.getProduct(); + versionString = vAppProductInfo.getVersion(); + } + } + + public String getName() { + return name; + } + + public String getMoId() { + return moId; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getVersionString() { + return versionString; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/VicBaseVm.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/VicBaseVm.java new file mode 100644 index 000000000..a7fb39703 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/VicBaseVm.java @@ -0,0 +1,132 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +import java.util.List; + +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vic.model.constants.Vch; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.ResourceConfigSpec; +import com.vmware.vim25.VirtualMachinePowerState; +import com.vmware.vim25.VirtualMachineQuickStats; +import com.vmware.vim25.VirtualMachineStorageSummary; +import com.vmware.vim25.VirtualMachineSummary; + +public abstract class VicBaseVm extends ModelObject { + protected static final String VM_KEY_NAME = BaseVm.VM_NAME; + protected static final String VM_KEY_OVERALL_STATUS = + BaseVm.VM_OVERALL_STATUS; + protected static final String VM_KEY_POWERSTATE = + BaseVm.Runtime.VM_POWERSTATE_FULLPATH; + protected static final String VM_KEY_SUMMARY = BaseVm.VM_SUMMARY; + protected static final String VM_KEY_GUESTFULLNAME = + BaseVm.Config.VM_GUESTFULLNAME; + protected static final String VM_KEY_CONFIG_EXTRACONFIG = + BaseVm.Config.VM_EXTRACONFIG; + protected static final String VM_KEY_RESOURCECONFIG = + BaseVm.VM_RESOURCECONFIG; + protected static final String VM_KEY_RESOURCEPOOL = + BaseVm.VM_RESOURCEPOOL; + protected static final String VM_KEY_CLIENT_IP = Vch.VM_CLIENT_IP; + protected static final String VM_KEY_OVERALLCPUUSAGE = + BaseVm.VM_OVERALLCPUUSAGE; + protected static final String VM_KEY_GUESTMEMORYUSAGE = + BaseVm.VM_GUESTMEMORYUSAGE; + protected static final String VM_KEY_COMMITTEDSTORAGE = + BaseVm.VM_COMMITTEDSTORAGE; + protected final ManagedObjectReference _objectRef; + protected String _vmName = null; + protected String _guestFullName = null; + protected ResourceConfigSpec _resourceConfig = null; + protected Object _resourcePool = null; + protected int _overallCpuUsage; + protected int _guestMemoryUsage; + protected long _committedStorage; + protected VirtualMachinePowerState _powerState = null; + protected ManagedEntityStatus _overallStatus = null; + + public VicBaseVm( + ObjectContent objContent, + String serverGuid) { + if (objContent == null) { + throw new IllegalArgumentException("constructor argument cannot be null"); + } + _objectRef = objContent.getObj(); + this.setId(serverGuid + "/" + _objectRef.getValue()); + } + + abstract protected void processDynamicProperties(List dpsList); + + /** + * Process VirtualMachineSummary to extract quickStats and storage info + * @param summary + */ + protected void processVmSummary(VirtualMachineSummary summary) { + VirtualMachineQuickStats quickStats = summary.getQuickStats(); + if (quickStats != null) { + _overallCpuUsage = quickStats.getOverallCpuUsage(); + _guestMemoryUsage = quickStats.getGuestMemoryUsage(); + } + + VirtualMachineStorageSummary vmStorageSummary = summary.getStorage(); + if (vmStorageSummary != null) { + _committedStorage = vmStorageSummary.getCommitted(); + } + } + + public String getName() { + return _vmName; + } + + public String getOverallStatus() { + return _overallStatus.toString(); + } + + public String getPowerState() { + return _powerState.toString(); + } + + public String getGuestFullName() { + return _guestFullName; + } + + public int getOverallCpuUsage() { + return _overallCpuUsage; + } + + public int getGuestMemoryUsage() { + return _guestMemoryUsage; + } + + public long getCommittedStorage() { + return _committedStorage; + } + + public ResourceConfigSpec getResourceConfig() { + return _resourceConfig; + } + + public Object getResourcePool() { + return _resourcePool; + } + +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/VirtualContainerHostVm.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/VirtualContainerHostVm.java new file mode 100644 index 000000000..cdcce7c0f --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/VirtualContainerHostVm.java @@ -0,0 +1,157 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +import java.util.List; + +import javax.xml.bind.DatatypeConverter; + +import com.vmware.vic.model.constants.Vch; +import com.vmware.vim25.ArrayOfOptionValue; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.OptionValue; +import com.vmware.vim25.ResourceConfigSpec; +import com.vmware.vim25.VirtualMachinePowerState; +import com.vmware.vim25.VirtualMachineSummary; + +public class VirtualContainerHostVm extends VicBaseVm { + private static final String EXTRACONFIG_CLIENT_IP_KEY = + Vch.VM_EXTRACONFIG_CLIENT_IP_KEY; + private static final String EXTRACONFIG_DOCKER_PERSONALITY_ARGS_KEY = + Vch.VM_EXTRACONFIG_DOCKER_PERSONALITY_ARGS_KEY; + private static final String DOCKER_ENGINE_SERVER_TLS_PORT = "2376"; + private static final String VM_KEY_IS_USING_TLS = Vch.VM_IS_USING_TLS; + private String _clientIp = null; + private boolean _isUsingTls = true; + + public VirtualContainerHostVm(ObjectContent objContent, String serverGuid) { + super(objContent, serverGuid); + processDynamicProperties(objContent.getPropSet()); + } + + /** + * Getter for VCH VM's IP + */ + public String getClientIp() { + return _clientIp; + } + + /** + * Getter for isUsingTls + */ + public boolean getIsUsingTls() { + return _isUsingTls; + } + + /** + * Process DynamicProperty[] and extract information + * needed for the VirtualContainerHostVm model + * @param dpsList : DynamicProperty list from ObjectContent.getPropSet() + */ + @Override + protected void processDynamicProperties(List dpsList) { + for (DynamicProperty dp : dpsList) { + if (dp.getName().equals(VM_KEY_NAME)) { + _vmName = (String)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_GUESTFULLNAME)) { + _guestFullName = (String)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_OVERALL_STATUS)) { + _overallStatus = (ManagedEntityStatus)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_POWERSTATE)) { + _powerState = (VirtualMachinePowerState)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_SUMMARY)) { + processVmSummary((VirtualMachineSummary)dp.getVal()); + } else if (dp.getName().equals(VM_KEY_RESOURCECONFIG)) { + _resourceConfig = (ResourceConfigSpec)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_RESOURCEPOOL)) { + _resourcePool = (Object)dp.getVal(); + } else if (dp.getName().equals(VM_KEY_CONFIG_EXTRACONFIG)) { + processExtraConfig((ArrayOfOptionValue)dp.getVal()); + } + } + } + + /** + * Extract VCH IP from config.extraConfig. Also determine + * if VCH appliance uses TLS by looking for a string pattern + * "2376" in docker engine server arguments + * @param ovs + */ + private void processExtraConfig(ArrayOfOptionValue ovs) { + if (ovs != null) { + for (OptionValue ov : ovs.getOptionValue()) { + String key = ov.getKey(); + if (EXTRACONFIG_CLIENT_IP_KEY.equals(key)) { + byte[] decoded = DatatypeConverter.parseBase64Binary((String)ov.getValue()); + int idx = decoded.length == 16 ? 12 : 0; + StringBuilder sb = new StringBuilder(); + for (int i = idx; i < decoded.length; i++) { + sb.append((decoded[i] << 24) >>> 24); + if (i < decoded.length - 1) { + sb.append("."); + } + } + _clientIp = sb.toString(); + continue; + } + + if (EXTRACONFIG_DOCKER_PERSONALITY_ARGS_KEY.equals(key)) { + _isUsingTls = ((String)ov.getValue()).indexOf( + DOCKER_ENGINE_SERVER_TLS_PORT) > -1; + continue; + } + } + } + } + + /** + * Property getter + * @param property : property to retrieve + */ + @Override + public Object getProperty(String property) { + if ("objectRef".equals(property)) { + return _objectRef; + } else if (VM_KEY_NAME.equals(property)) { + return _vmName; + } else if (VM_KEY_OVERALL_STATUS.equals(property)) { + return _overallStatus; + } else if (VM_KEY_POWERSTATE.equals(property)) { + return _powerState; + } else if (VM_KEY_GUESTFULLNAME.equals(property)) { + return _guestFullName; + } else if (VM_KEY_CLIENT_IP.equals(property)) { + return _clientIp; + } else if (VM_KEY_OVERALLCPUUSAGE.equals(property)) { + return _overallCpuUsage; + } else if (VM_KEY_GUESTMEMORYUSAGE.equals(property)) { + return _guestMemoryUsage; + } else if (VM_KEY_COMMITTEDSTORAGE.equals(property)) { + return _committedStorage; + } else if (VM_KEY_IS_USING_TLS.equals(property)) { + return _isUsingTls; + } else if (VM_KEY_RESOURCECONFIG.equals(property)) { + return _resourceConfig; + } else if (VM_KEY_RESOURCEPOOL.equals(property)) { + return _resourcePool; + } + return UNSUPPORTED_PROPERTY; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/VmQueryResult.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/VmQueryResult.java new file mode 100644 index 000000000..f987a2f6a --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/VmQueryResult.java @@ -0,0 +1,48 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model; + +import java.util.Map; +import com.vmware.vise.vim.data.VimObjectReferenceService; + +public class VmQueryResult extends ModelObject { + private final Map _vmsMap; + private int _match = 0; + + public VmQueryResult( + Map vmsMap, + VimObjectReferenceService vimObjectReferenceService) { + _vmsMap = vmsMap; + _match = vmsMap.size(); + if (_vmsMap == null || + vimObjectReferenceService == null) { + throw new IllegalArgumentException("constructor argument cannot be empty!"); + } + } + + @Override + public Object getProperty(String property) { + if ("match".equals(property)) { + return _match; + } else if ("results".equals(property)) { + return _vmsMap; + } + return null; + } + +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/BaseVm.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/BaseVm.java new file mode 100644 index 000000000..33a0a24ab --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/BaseVm.java @@ -0,0 +1,38 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model.constants; + +public class BaseVm { + public static final String ID = "id"; + public static final String VM_NAME = "name"; + public static final String VM_OVERALL_STATUS = "overallStatus"; + public static class Runtime { + public static final String VM_POWERSTATE_FULLPATH = "runtime.powerState"; + public static final String VM_POWERSTATE_BASENAME = "powerState"; + } + public static final String VM_SUMMARY = "summary"; + public static class Config { + public static final String VM_GUESTFULLNAME = "config.guestFullName"; + public static final String VM_EXTRACONFIG = "config.extraConfig"; + } + public static final String VM_RESOURCECONFIG = "resourceConfig"; + public static final String VM_RESOURCEPOOL = "resourcePool"; + public static final String VM_OVERALLCPUUSAGE = "overallCpuUsage"; + public static final String VM_GUESTMEMORYUSAGE = "guestMemoryUsage"; + public static final String VM_COMMITTEDSTORAGE = "committedStorage"; +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/Container.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/Container.java new file mode 100644 index 000000000..8ca8e539f --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/Container.java @@ -0,0 +1,30 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model.constants; + +public class Container { + public static final String VM_EXTRACONFIG_CONTAINER_KEY = "common/name"; + public static final String VM_EXTRACONFIG_IMAGE_NAME_KEY = + "guestinfo.vice./repo"; + public static final String VM_EXTRACONFIG_PORTMAPPING_KEY = + "guestinfo.vice./networks|bridge/ports~"; + public static final String VM_CONTAINERNAME_KEY = "containerName"; + public static final String VM_IMAGENAME_KEY = "imageName"; + public static final String VM_PORTMAPPING_KEY = "portMapping"; + public static final String PARENT_NAME_KEY = "parentObjectName"; +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/Vch.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/Vch.java new file mode 100644 index 000000000..d71b8ac07 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/Vch.java @@ -0,0 +1,29 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model.constants; + +public class Vch { + public static final String VM_IS_USING_TLS = "isUsingTls"; + public static final String VM_CLIENT_IP = "clientIp"; + public static final String VM_VCH_IP = "vchIp"; + public static final String VM_EXTRACONFIG_VCH_KEY = "init/common/name"; + public static final String VM_EXTRACONFIG_CLIENT_IP_KEY = + "guestinfo.vice..init.networks|client.assigned.IP"; + public static final String VM_EXTRACONFIG_DOCKER_PERSONALITY_ARGS_KEY = + "guestinfo.vice./init/sessions|docker-personality/cmd/Args~"; +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/VsphereObjects.java b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/VsphereObjects.java new file mode 100644 index 000000000..21943ebfb --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/model/constants/VsphereObjects.java @@ -0,0 +1,26 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.model.constants; + +public class VsphereObjects { + public static final String VirtualMachine = "VirtualMachine"; + public static final String VmPropertyValueKey = "vm"; + public static final String NamePropertyKey = "name"; + public static final String VirtualApp = "VirtualApp"; + public static final String ResourcePool = "ResourcePool"; +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ActionResult.java b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ActionResult.java new file mode 100644 index 000000000..83779868b --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ActionResult.java @@ -0,0 +1,187 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +*/ + +package com.vmware.vic.mvc; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Utility defining the result of an action called in an ActionsController. + * This generic class can be re-used in any Java plugin. + * + * @see getJsonMap() to get the actual value to be returned in the invoke method. + */ +public class ActionResult { + final static String ACTIONUID = "actionUid"; + final static String ERROR_MESSAGE = "errorMessage"; + final static String RESULT = "result"; + final static String OPERATION_TYPE = "operationType"; + final static String OP_ADD = "add"; + final static String OP_CHANGE = "change"; + final static String OP_DELETE = "delete"; + final static String OP_RELATIONSHIP_CHANGE = "relationship_change"; + + private final static Log _logger = LogFactory.getLog(ActionResult.class); + private Map _resultMap = new HashMap(); + private String _resourceBundle; + + /** + * Constructor. + * You must use one of the set___Result() methods later or setErrorMessage + * before using this ActionResult. + * + * @param actionUid The action id. + * @param resourceBundle Name of the resource bundle to use for error messages. + */ + public ActionResult(String actionUid, String resourceBundle) { + _resultMap.put(ACTIONUID, actionUid); + _resourceBundle = resourceBundle; + } + + /** + * @return the map to be used as the return value of the action's invoke method. + */ + public Map getJsonMap() { + if ((_resultMap.get(RESULT) == null) && (_resultMap.get(ERROR_MESSAGE) == null)) { + _logger.error("Missing result or error message in ActionResult for " + + _resultMap.get(ACTIONUID)); + } + return _resultMap; + } + + /** + * Assign a result value to this ActionResult. + * + * @param result + * @param errMsgKey Message key of the error message to display when result is null + * or false, or leave null when no message is needed. + */ + public void setResult(Object result, String errMsgKey) { + _resultMap.put(RESULT, result); + if ((errMsgKey != null) && ((result == null) || + (result instanceof Boolean && (Boolean)result == false))) { + setErrorMessage(errMsgKey); + } + } + + /** + * Set the result of an action creating a new object. This will update the UI model + * to display the new object if the action was successful. + * + * @param result the URI representing the object, or null if no object was created. + * @param uriType the object type + * @param errMsgKey Message key of the error message to display when result is null, + * or leave null when no message is needed. + */ + public void setObjectAddedResult(URI result, String uriType, String errMsgKey) { + _resultMap.put(RESULT, result); + _resultMap.put(OPERATION_TYPE, OP_ADD); + _resultMap.put("uriType", uriType); + if ((result == null) && (errMsgKey != null)) { + setErrorMessage(errMsgKey); + } + } + + /** + * Set the result of an action deleting an object. This will update the UI model to + * remove the object if the action was successful. + * + * @param result true if the action was successful, false otherwise. + * @param errMsgKey Message key of the error message to display when result is false, + * or leave null when no message is needed. + */ + public void setObjectDeletedResult(boolean result, String errMsgKey) { + _resultMap.put(RESULT, result); + _resultMap.put(OPERATION_TYPE, OP_DELETE); + if (!result && (errMsgKey != null)) { + setErrorMessage(errMsgKey); + } + } + + /** + * Set the result of an action modifying an object. This will update the UI model to + * display the object's changes if the action was successful. + * + * @param result + * @param errMsgKey Message key of the error message to display when result is false, + * or null to avoid displaying any error message. + */ + public void setObjectChangedResult(boolean result, String errMsgKey) { + _resultMap.put(RESULT, result); + _resultMap.put(OPERATION_TYPE, OP_CHANGE); + if (!result && (errMsgKey != null)) { + setErrorMessage(errMsgKey); + } + } + + /** + * Set an error message to be displayed in the UI when the action returns. + * + * @param msg The message to display, already localized on the server side (or else + * you can use setErrorMessage()) + */ + public void setErrorLocalizedMessage(String msg) { + _resultMap.put(ERROR_MESSAGE, msg); + } + + /** + * Set an error message to be displayed in the UI when the action returns. The message will be + * localized on the client side for convenience. + * + * @param key The message key. + */ + public void setErrorMessage(String key) { + setErrorMessage(key, null); + } + + /** + * Set an error message to be displayed in the UI when the action returns. The message will be + * localized on the client side for convenience. + * @param key The message key. + * @param params An optional array of parameters when the string resources contains + * place holders {0}, {1} etc. Use null otherwise. + */ + public void setErrorMessage(String key, String[] params) { + _resultMap.put(ERROR_MESSAGE, new ActionMessage(_resourceBundle, key, params)); + } + + class ActionMessage { + public String bundleName; + public String key; + public String[] params; + + ActionMessage() { + // empty constructor needed for Json serialization + } + + ActionMessage(String bundleName, String key, String[] params) { + this.bundleName = bundleName; + this.key = key; + this.params = params; + } + } +} + diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ActionsController.java b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ActionsController.java new file mode 100644 index 000000000..ad8f91d4b --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ActionsController.java @@ -0,0 +1,163 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +*/ + +package com.vmware.vic.mvc; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.vmware.vic.services.SampleActionService; + +import com.vmware.vise.data.query.ObjectReferenceService; + + +/** + * A controller to serve HTTP JSON GET/POST requests to the endpoint "/actions.html". + */ +@Controller +@RequestMapping(value = "/actions.html") +public class ActionsController { + private final static Log _logger = LogFactory.getLog(ActionsController.class); + + // UI plugin resource bundle for localized messages + private final String RESOURCE_BUNDLE = "__bundleName__"; + + private final SampleActionService _actionService; + private final ObjectReferenceService _objectReferenceService; + + @Autowired + public ActionsController( + SampleActionService actionService, + @Qualifier("objectReferenceService") ObjectReferenceService objectReferenceService) { + _actionService = actionService; + _objectReferenceService = objectReferenceService; + QueryUtil.setObjectReferenceService(objectReferenceService); + } + + // Empty controller to avoid warnings in vic's bundle-context.xml + // where the bean is declared + public ActionsController() { + _actionService = null; + _objectReferenceService = null; + } + + + /** + * Generic method to invoke an action on a given object or a global action. + * + * @param actionUid the action Uid as defined in plugin.xml + * + * @param targets null for a global action, comma-separated list of object ids + * for an action on 1 or more objects + * + * @param json additional data in JSON format, or null for the delete action. + * + * @return + * Returns a map with key values. + */ + @RequestMapping(method = RequestMethod.POST) + @ResponseBody + public Map invoke( + @RequestParam(value = "actionUid", required = true) String actionUid, + @RequestParam(value = "targets", required = false) String targets, + @RequestParam(value = "json", required = false) String json) + throws Exception { + // Parameters validation + Object objectRef = null; + if (targets != null) { + String[] objectIds = targets.split(","); + if (objectIds.length > 1) { + // Our actions only support 1 target object for now + _logger.warn("Action " + actionUid + " called with " + objectIds.length + + " target objects, will use only the first one"); + } + String objectId = ObjectIdUtil.decodeParameter(objectIds[0]); + objectRef = _objectReferenceService.getReference(objectId); + if (objectRef == null) { + String errorMsg = "Error in action " + actionUid + + ", object not found with id: " + objectId; + _logger.error(errorMsg); + throw new Exception(errorMsg); + } + } + + ActionResult actionResult = new ActionResult(actionUid, RESOURCE_BUNDLE); + + if (actionUid.equals("com.vmware.vic.sampleAction1")) { + _actionService.sampleAction1(objectRef); + // Display a test error message + actionResult.setErrorLocalizedMessage("Testing error message for action1"); + + } else if (actionUid.equals("com.vmware.vic.sampleAction2")) { + boolean result = _actionService.sampleAction2(objectRef); + actionResult.setResult(result, null); + + } else { + String warning = "Action not implemented yet! "+ actionUid; + _logger.warn(warning); + actionResult.setErrorLocalizedMessage(warning); + } + return actionResult.getJsonMap(); + } + + /** + * Generic handling of internal exceptions. + * Sends a 500 server error response along with a json body with messages + * + * @param ex The exception that was thrown. + * @param response + * @return a map containing the exception message, the cause, and a stackTrace + */ + @ExceptionHandler(Exception.class) + @ResponseBody + public Map handleException(Exception ex, HttpServletResponse response) { + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + + Map errorMap = new HashMap(); + errorMap.put("message", ex.getMessage()); + if(ex.getCause() != null) { + errorMap.put("cause", ex.getCause().getMessage()); + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + ex.printStackTrace(pw); + errorMap.put("stackTrace", sw.toString()); + + return errorMap; + } +} + diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/mvc/DataAccessController.java b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/DataAccessController.java new file mode 100644 index 000000000..0fd082e3a --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/DataAccessController.java @@ -0,0 +1,259 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + + */ + +package com.vmware.vic.mvc; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.vmware.vic.model.ModelObject; +import com.vmware.vise.data.query.DataService; +import com.vmware.vise.data.query.ObjectReferenceService; +import com.vmware.vise.data.query.PropertyValue; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.data.query.ResultSet; + +/** + * A generic controller to serve HTTP JSON GET requests to the endpoint "/data". + * + */ +@Controller +@RequestMapping(value = "/data", method = RequestMethod.GET) +public class DataAccessController { + private final static String OBJECT_ID = "id"; + + private final DataService _dataService; + private final ObjectReferenceService _objectReferenceService; + + @Autowired + public DataAccessController( + DataService dataService, + @Qualifier("objectReferenceService") ObjectReferenceService objectReferenceService) { + _dataService = dataService; + _objectReferenceService = objectReferenceService; + QueryUtil.setObjectReferenceService(objectReferenceService); + } + + // Empty controller to avoid warnings in vic's bundle-context.xml + // where the bean is declared + public DataAccessController() { + _dataService = null; + _objectReferenceService = null; + } + + /** + * Generic method to fetch properties for a given object. + * e.g. /properties/{objectId}?properties=name,config + * + * @param encodedObjectId + * Encoded object id. + * + * @param properties + * Properties passed as a request parameter that needs to be fetched. + * They are comma separated. + * For e.g. name,runtime + * + * @return + * Returns a map with property name as key and property value as the value. + */ + @RequestMapping(value = "/properties/{objectId}") + @ResponseBody + public Map getProperties( + @PathVariable("objectId") String encodedObjectId, + @RequestParam(value = "properties", required = true) String properties, + @RequestParam(value = "page", defaultValue = "1") int page, + @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) + throws Exception { + + Object ref = getDecodedReference(encodedObjectId); + String objectId = _objectReferenceService.getUid(ref); + + String[] props = properties.split(","); + PropertyValue[] pvs = QueryUtil.getProperties(_dataService, ref, props); + Map propsMap = new HashMap(); + propsMap.put(OBJECT_ID, objectId); + for (PropertyValue pv : pvs) { + propsMap.put(pv.propertyName, pv.value); + } + return propsMap; + } + + /** + * Generic method to fetch properties using relation for the given object. + * + * @param encodedObjectId + * Encoded object id. + * + * @param relation + * Relationship, like vm for a host, the relation would be "vm". + * + * @param targetType + * Type of objects targeted by this data request. + * + * @param properties + * Properties passed as a request parameter that needs to be fetched. + * They are comma separated. + * For e.g. name,runtime + * + * @return + * Returns an array of PropertyValue + * @throws Exception + */ + @RequestMapping(value = "/propertiesByRelation/{objectId}") + @ResponseBody + public PropertyValue[] getPropertiesForRelatedObject( + @PathVariable("objectId") String encodedObjectId, + @RequestParam(value = "relation", required = true) String relation, + @RequestParam(value = "targetType", required = true) String targetType, + @RequestParam(value = "properties", required = true) String properties) + throws Exception { + Object ref = getDecodedReference(encodedObjectId); + String[] props = properties.split(","); + PropertyValue []result = QueryUtil.getPropertiesForRelatedObjects( + _dataService, ref, relation, targetType, props); + return result; + } + + /** + * Gets a list items of the given type and extract the given properties + * + * @param targetType object type + * + * @param properties + * List of properties to request for the matched objects, i.e. prop1,prop2,prop3. + * + * @param offset + * The offset into the result of items. + * If the offset is N then items from N to N + maxResultCount - 1 will be returned. + * If empty, it will default to 0. + * + * @param maxResultCount + * The max number of items to retrieve. By default all results are retrieved. + * + * @param sorting + * A pair of strings: the property to sort on and the sorting direction, + * i.e. prop1,asc or prop2,desc. By defaut "name,asc" will be used + * + * @return + * Returns a map where "data" is a list of items and "totalResultCount" is a total + * number of items satisfying the constraint. + * + * @throws Exception + */ + @RequestMapping(value = "/list/") + @ResponseBody + public Map getListDataEx( + @RequestParam(value="targetType") String targetType, + @RequestParam(value="properties") String properties, + @RequestParam(value="offset", defaultValue="0") int offset, + @RequestParam(value="maxResultCount", defaultValue="-1") int maxResultCount, + @RequestParam(value="sorting", required = false) String sorting, + @RequestParam(value="filter", required = false) String filter) throws Exception { + + String[] props = properties.split(","); + String[] sortParams = (sorting != null) ? sorting.split(",") : null; + String[] filterParams = (filter != null && !filter.isEmpty()) ? + filter.split(",") : null; + ResultSet rs = QueryUtil.getListData( + _dataService, targetType, props, + offset, maxResultCount, sortParams, filterParams); + return transformListDataToMap(rs); + } + + /** + * @return a map for the /list API above. + */ + @SuppressWarnings("unchecked") + private Map transformListDataToMap(ResultSet rs) { + Map vmsMap = null; + + for (ResultItem ri : rs.items) { + for (PropertyValue pv : ri.properties) { + if ("results".equals(pv.propertyName)) { + vmsMap = (Map)pv.value; + } + } + } + Map resultObject = new HashMap(); + resultObject.put("data", vmsMap); + resultObject.put("totalResultCount", rs.totalMatchedObjectCount); + return resultObject; + } + + /** + * Generic handling of internal exceptions. + * Sends a 500 server error response along with a json body with messages + * + * @param ex The exception that was thrown. + * @param response + * @return a map containing the exception message, the cause, and a stackTrace + */ + @ExceptionHandler(Exception.class) + @ResponseBody + public Map handleException(Exception ex, HttpServletResponse response) { + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + + Map errorMap = new HashMap(); + errorMap.put("message", ex.getMessage()); + if(ex.getCause() != null) { + errorMap.put("cause", ex.getCause().getMessage()); + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + ex.printStackTrace(pw); + errorMap.put("stackTrace", sw.toString()); + + return errorMap; + } + + /** + * Retrieves the object reference corresponding to the given encoded object id. + * + * Note: objectIds sent to controllers are encoded in case they contain "/". + * + * @param encodedObjectId the encoded id of the desired object reference + * @return an object reference with the given id + * @throws Exception if an object reference with the given id is not found + */ + private Object getDecodedReference(String encodedObjectId) throws Exception { + Object ref = _objectReferenceService.getReference(encodedObjectId, true); + if (ref == null) { + throw new Exception("Object not found with id: " + encodedObjectId); + } + return ref; + } +} + diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ObjectIdUtil.java b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ObjectIdUtil.java new file mode 100644 index 000000000..045bb2773 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ObjectIdUtil.java @@ -0,0 +1,77 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +*/ + +package com.vmware.vic.mvc; + +/** + * ObjectId encoding/decoding + * + */ +public class ObjectIdUtil { + // Forward slash must be encoded in URLs + private static final String FORWARD_SLASH = "/"; + // Single encoding + private static final String FORWARD_SLASH_ENCODED1 = "%2F"; + // Double encoding + private static final String FORWARD_SLASH_ENCODED2 = "%252F"; + + /** + * Encode the given object id. + * + * @param objectId The objectId to be encoded + * @return The encoded objectId + */ + public static String encodeObjectId(String objectId) { + if (objectId != null) { + return objectId.replace(FORWARD_SLASH, FORWARD_SLASH_ENCODED2); + } + return objectId; + } + + /** + * Decode the given objectId when passed as a path variable in a Spring controller + * (Spring already performs 1 level of decoding) + * + * @param objectId Encoded id + * @return The decoded object id + */ + public static String decodePathVariable(String objectId) { + if (objectId != null) { + return objectId.replace(FORWARD_SLASH_ENCODED1, FORWARD_SLASH); + } + return objectId; + } + + /** + * Decode the given objectId when passed as a URL parameter, i.e. reverse the + * double encoding done by encodeObjectId. + * + * @param objectId Encoded id + * @return The decoded object id + */ + public static String decodeParameter(String objectId) { + if (objectId != null) { + return objectId.replace(FORWARD_SLASH_ENCODED2, FORWARD_SLASH); + } + return objectId; + } + +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/mvc/QueryUtil.java b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/QueryUtil.java new file mode 100644 index 000000000..1f1a56130 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/QueryUtil.java @@ -0,0 +1,752 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +*/ + +package com.vmware.vic.mvc; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.vmware.vic.model.ContainerVm; +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.VirtualContainerHostVm; +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vic.model.constants.Container; +import com.vmware.vic.model.constants.Vch; +import com.vmware.vic.utils.VicVmComparator; +import com.vmware.vise.data.Constraint; +import com.vmware.vise.data.PropertySpec; +import com.vmware.vise.data.ResourceSpec; +import com.vmware.vise.data.query.CompositeConstraint; +import com.vmware.vise.data.query.Conjoiner; +import com.vmware.vise.data.query.DataService; +import com.vmware.vise.data.query.ObjectIdentityConstraint; +import com.vmware.vise.data.query.ObjectReferenceService; +import com.vmware.vise.data.query.OrderingCriteria; +import com.vmware.vise.data.query.OrderingPropertySpec; +import com.vmware.vise.data.query.PropertyValue; +import com.vmware.vise.data.query.QuerySpec; +import com.vmware.vise.data.query.RelationalConstraint; +import com.vmware.vise.data.query.RequestSpec; +import com.vmware.vise.data.query.Response; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.data.query.ResultSet; +import com.vmware.vise.data.query.ResultSpec; +import com.vmware.vise.data.query.SortType; + +/** + * General Query utility class for the DataService + * + */ +public class QueryUtil { + + private static ObjectReferenceService _objectReferenceService; + private static final Log _logger = LogFactory.getLog(QueryUtil.class); + + public static void setObjectReferenceService( + ObjectReferenceService objectReferenceService) { + _objectReferenceService = objectReferenceService; + } + + /** + * Helper method to get one or more properties for a given object. + * + * @param dataService + * The data service instance that will be used for retrieving + * the property value. + * + * @param obj + * Object whose properties are required. + * + * @param properties + * The names of the object properties you need to retrieve. + * + * @return An array of PropertyValue instances. Each instance should + * be the value of one of the properties from the properties array. + * The results however can be shuffled: The first + * PropertyValue from the result does not necessarily correspond + * to the first property name in properties array. + * + * @see #getProperties(DataService, Object[], String[]) + */ + public static PropertyValue[] getProperties( + DataService dataService, + Object obj, + String[] properties) throws Exception { + return getProperties(dataService, new Object[] {obj}, properties); + } + + /** + * Helper method to get one or more properties for a set of given objects. + * + * Note: In case if only some of the requested properties are retrieved + * (for some of the objects), instead of throwing error, the partial result + * will be returned. + * + * @param dataService + * DataService instance to use for retrieving the properties of the object. + * + * @param objs + * An array of object instances whose properties are required. + * + * @param properties + * Name of the properties to be requested. + * + * @return + * Values of the requested properties. The array is flat: this means that + * the property values for the different objects will be mixed in it, in no + * particular order. + * + * @throws Exception + * Throws Exception if invalid query parameters are passed. + * For e.g. if objs or properties + * is null or empty. + * + * Throws Exception if an empty result is retrieved + * for the query. + */ + public static PropertyValue[] getProperties( + DataService dataService, + Object[] objs, + String[] properties) throws Exception { + if (objs == null || objs.length == 0 || + properties == null || properties.length == 0) { + throw new Exception("Invalid parameters for getProperties"); + } + + Object obj = objs[0]; + + QuerySpec query = buildQuerySpec(objs, properties); + + query.name = _objectReferenceService.getUid(obj) + ".properties"; + + ResultSet resultSet = getData(dataService, query); + + ArrayList result = new ArrayList(); + if (resultSet != null) { + ResultItem[] items = resultSet.items; + if (items != null && items.length > 0 && items[0] != null) { + for (ResultItem item : items) { + for (PropertyValue v : item.properties) { + v.resourceObject = item.resourceObject; + result.add(v); + } + } + } + } + + // Check if we have at least the partial result, if so + // then we return the result else we throw + if (result.isEmpty() && resultSet.error != null) { + throw resultSet.error; + } + + // Return the result. It will be empty array if that property does not exist. + return toArray(result, PropertyValue.class); + } + + + @SuppressWarnings({ "unchecked" }) + public static T[] toArray(Collection collection, Class elementType) { + T[] result = null; + + if (collection == null) { + result = (T[]) Array.newInstance(elementType, 0); + return result; + } + + T[] copy = (T[]) Array.newInstance(elementType, collection.size()); + result = collection.toArray(copy); + return result; + } + + + /** + * A shortcut method to getPropertiesForRelatedObjects for the case + * where just one property is requested. + * + *

Note that the value returned is still an array of properties, cause the + * relationship may resolve to more than one related object. + * + * @see #getPropertiesForRelatedObjects(DataService, Object, String, String, String[]) + */ + public static PropertyValue[] getPropertyForRelatedObjects( + DataService dataService, + Object object, + String relationship, + String targetType, + String property) throws Exception { + return getPropertiesForRelatedObjects( + dataService, object, relationship, targetType, new String[] {property}); + } + + + /** + * Return the requested properties on the objects which are in + * relationship with the original object. + * + *

The following example will demonstrate how to retrieve all datastore + * names of the datastores where the VM is located: + * {@code + * PropertyValue[] names = QueryUtil.getPropertiesForRelatedObjects( + * dataService, vm, "datastore", "Datastore", new String[] {"name"}); + * if (names != null) { + * for (PropetyValue propValue : propValues) { + * System.out.println(propValue.value); + * } + * } + * } + * + * @param dataService + * The data service to query about the properties. + * + * @param object + * The root object for the relationship. + * + * @param relationship + * The relationship string. A string referencing a property of the + * object parameter. + * + * @param targetType + * The type of the object that the relationship will return. + * In general you can put null here if you are unsure what objects + * are returned, but if you know the type, please do specify it. Passing + * null may not always work. + * + * @param properties + * An array of string referencing properties on the objects returned when the + * relationship is resolved. + * + * @return + * An array of PropetyValue instances or null if the + * relationship cannot be resolved properly or the properties + * requested are missing. + * + * @throws Exception + */ + public static PropertyValue[] getPropertiesForRelatedObjects( + DataService dataService, + Object obj, + String relationship, + String targetType, + String[] properties) throws Exception { + if (obj == null || properties == null || properties.length == 0) { + throw new Exception("invalid parameters in getPropertiesForRelatedObjects"); + } + + // If no relation is given, consider the "properties" as being requested + // directly on "obj". + if (relationship == null || relationship.length() == 0) { + return getProperties(dataService, obj, properties); + } + + // create object identity constraint to match the given server object + ObjectIdentityConstraint objectConstraint = + createObjectIdentityConstraint(obj); + + // create relational constraint for the given relationship to the source object + RelationalConstraint relationalConstraint = + createRelationalConstraint(relationship, + objectConstraint, + true, // true is important here. + targetType); + + QuerySpec query = buildQuerySpec(relationalConstraint, properties); + query.name = _objectReferenceService.getUid(obj) + "." + relationship + ".properties"; + + ResultSet resultSet = getData(dataService, query); + + // Check if we have at least one result. + ArrayList result = new ArrayList(); + if (resultSet != null && resultSet.items != null) { + for (ResultItem item : resultSet.items) { + if (item != null && item.properties != null) { + for (PropertyValue propValue : item.properties) { + if (propValue != null) { + result.add(propValue); + } + } + } + } + } + + if (result.isEmpty() && resultSet.error != null) { + throw resultSet.error; + } + + // There was no error reported, so assume that property does not exist + // or the value is null + return toArray(result, PropertyValue.class); + } + + /** + * Helper method to call DataService with single QuerySpec. Uses injected + * DataService + * + * @param dataService + * The data service instance. + * @param query + * QuerySpec for the data-service + * @return ResultSet from the data-service + * + * @throws Exception + * Throws Exception if an empty result is retrieved + * for the query. + */ + public static ResultSet getData( + DataService dataService, + QuerySpec query) throws Exception { + + RequestSpec requestSpec = new RequestSpec(); + requestSpec.querySpec = new QuerySpec[] { query }; + + Response response = new Response(); + response = dataService.getData(requestSpec); + + ResultSet[] retVal = response.resultSet; + if (retVal == null || retVal.length == 0 || retVal[0] == null) { + throw new Exception("Empty result"); + } + return retVal[0]; + } + + /** + * Helper utility for building simplest data-service QuerySpec: one object + * and several properties of this object. + * + * @param entity + * - managed object of interest + * @param properties + * - names of properties + * @return - QuerySpec to feed into DataService + */ + public static QuerySpec buildQuerySpec(Object entity, + String[] properties) { + ObjectIdentityConstraint oc = new ObjectIdentityConstraint(); + oc.target = entity; + + String targetType = _objectReferenceService.getResourceObjectType(entity); + Set targetTypes = new HashSet(); + targetTypes.add(targetType); + + QuerySpec query = buildQuerySpec(oc, properties, targetTypes); + return query; + } + + public static QuerySpec buildQuerySpec( + Object[] entities, + String[] properties) { + if (entities.length == 1) { + return buildQuerySpec(entities[0], properties); + } + CompositeConstraint cc = new CompositeConstraint(); + cc.conjoiner = Conjoiner.OR; + Constraint[] nestedConstraints = new Constraint[entities.length]; + Set targetTypes = new HashSet(); + String targetType = null; + + for (int index=0; index < entities.length; index++) { + ObjectIdentityConstraint oc = new ObjectIdentityConstraint(); + oc.target = entities[index]; + nestedConstraints[index] = oc; + + targetType = _objectReferenceService.getResourceObjectType(oc.target); + targetTypes.add(targetType); + } + cc.nestedConstraints = nestedConstraints; + + QuerySpec query = buildQuerySpec(cc, properties, targetTypes); + return query; + } + + /** + * Helper utility for building simplest QuerySpec that requests a few properties + * on the objects that are identified with the constraint. + * + * @param constraint + * A constraint which will define the set of object being considered for + * property retrieval. + * + * @param properties + * The properties we want to retrieve. + * + * @return + * A QuerySpec which you can directly feed into a getData() + * method of the DataService. + */ + public static QuerySpec buildQuerySpec(Constraint constraint, + String[] properties) { + QuerySpec query = buildQuerySpec( + constraint, + properties, + null /* target types not known */); + return query; + } + + /** + * Helper utility for building simplest QuerySpec that requests a few properties + * on the objects that are identified with the constraint. + * + * @param constraint + * A constraint which will define the set of object being considered for + * property retrieval. + * + * @param properties + * The properties we want to retrieve. + * + * @param targetTypes + * For each target type in the set, a PropertySpec is created. + * If targetTypes is null, then a single PropertySpec is created + * with type unset. + * + * @return + * A QuerySpec which you can directly feed into a getData() + * method of the DataService. + */ + public static QuerySpec buildQuerySpec( + Constraint constraint, + String[] properties, + Set targetTypes) { + QuerySpec query = new QuerySpec(); + ResourceSpec resourceSpec = new ResourceSpec(); + resourceSpec.constraint = constraint; + + List pSpecs = new ArrayList(); + if (targetTypes != null) { + for (String targetType : targetTypes) { + PropertySpec propSpec = createPropertySpec(properties, targetType); + pSpecs.add(propSpec); + } + } else { + PropertySpec propSpec = createPropertySpec(properties, null); + pSpecs.add(propSpec); + } + + resourceSpec.propertySpecs = pSpecs.toArray(new PropertySpec[]{}); + query.resourceSpec = resourceSpec; + + return query; + } + + + /** + * Creates a RelationalConstraint + * + * @param relationship + * relationship to traverse + * + * @param constraintOnRelatedObject + * constraint on the objects related to the targeted objects by the + * given relationship + * + * @param hasInverseRelation + * indicates whether the constraint given by constraintOnRelatedObject applies to the + * source, as opposed to the target of the relationship given in this instance + * + * @param targetType + * type of the objects to retrieve + * + * @return The created RelationalConstraint. + */ + public static RelationalConstraint createRelationalConstraint( + String relationship, + Constraint constraintOnRelatedObject, + Boolean hasInverseRelation, + String targetType) { + RelationalConstraint rc = new RelationalConstraint(); + rc.relation = relationship; + rc.hasInverseRelation = hasInverseRelation; + rc.constraintOnRelatedObject = constraintOnRelatedObject; + rc.targetType = targetType; + return rc; + } + + + /** + * Creates an ObjectIdentityConstraint + * + * @param entity + * Object to be looked up + * + * @return The created ObjectIdentityConstraint. + */ + public static ObjectIdentityConstraint createObjectIdentityConstraint( + Object entity) { + ObjectIdentityConstraint oc = new ObjectIdentityConstraint(); + oc.target = entity; + oc.targetType = _objectReferenceService.getResourceObjectType(entity); + return oc; + } + + /** + * Utility to create a property spec. + */ + private static PropertySpec createPropertySpec( + String[] properties, String targetType) { + PropertySpec propSpec = new PropertySpec(); + propSpec.type = targetType; + propSpec.propertyNames = properties; + return propSpec; + } + + /** + * Implementation of DataAccessController's /list API + * @throws Exception + */ + @SuppressWarnings("unchecked") +static ResultSet getListData( + DataService dataService, String targetType, String[] requestedProperties, + int offset, int maxResultCount, String[] sortParams, + String[] filterParams) throws Exception { + + Constraint constraint = new Constraint(); + constraint.targetType = targetType; + QuerySpec query = QueryUtil.buildQuerySpec(constraint, requestedProperties); + ResultSpec rs = new ResultSpec(); + rs.offset = offset; + rs.maxResultCount = maxResultCount; + + if (sortParams != null && sortParams.length == 2) { + String sortField = sortParams[0]; + String sortDirection = sortParams[1]; + + OrderingPropertySpec orderPropSpec = new OrderingPropertySpec(); + orderPropSpec.type = constraint.targetType; + orderPropSpec.propertyNames = new String[] {sortField}; + orderPropSpec.orderingType = SortType.ASCENDING; + if ("desc".equalsIgnoreCase(sortDirection)) { + orderPropSpec.orderingType = SortType.DESCENDING; + } + rs.order = new OrderingCriteria(); + rs.order.orderingProperties = new OrderingPropertySpec[] { orderPropSpec }; + } + query.resultSpec = rs; + + RequestSpec requestSpec = new RequestSpec(); + requestSpec.querySpec = new QuerySpec[]{query}; + + // Execute data queries to get counts. + Response response = dataService.getData(requestSpec); + ResultSet[] resultSetArray = response.resultSet; + + // return an empty resultset upon when resultSet array is empty + if (resultSetArray.length == 0) { + ResultSet emptyResultSet = new ResultSet(); + emptyResultSet.totalMatchedObjectCount = 0; + emptyResultSet.items = new ResultItem[] {}; + _logger.warn("No entry for response.resultSet"); + return emptyResultSet; + } + + ResultSet resultSet = resultSetArray[0]; + resultSet.totalMatchedObjectCount = 0; + if (resultSet.error != null) { + throw resultSet.error; + } + + int totalMatchedObjectCount = 0; + if (resultSet.items.length > 0) { + PropertyValue[] pvs = resultSet.items[0].properties; + for (PropertyValue pv : pvs) { + if ("match".equals(pv.propertyName)) { + resultSet.totalMatchedObjectCount += + (int)pv.value; + } else if ("results".equals(pv.propertyName)) { + // returns # of items found + Map adjustedResults = adjustItems( + (HashMap) pv.value, + offset, + maxResultCount, + resultSet.totalMatchedObjectCount, + rs.order, + filterParams + ); + pv.value = adjustedResults.get("results"); + totalMatchedObjectCount = + (int)adjustedResults.get("totalMatchedObjectCount"); + } + } + + if (filterParams != null && filterParams.length > 0) { + resultSet.totalMatchedObjectCount = totalMatchedObjectCount; + } + } + + return resultSet; + } + + /** + * Handles pagination, sort and filter + * + * @param vAppVmsMap: vApp-VMs map + * @param offset: index to include from + * @param maxResultCount: page size + * @param totalMatchedObjectCount: # of all items + * @param order: OrderingCriteria instance + * @param filterParams: filtering criteria + * @return adjusted Map + */ + private static Map adjustItems( + Map vmsMap, + int offset, + int maxResultCount, + int totalMatchedObjectCount, + OrderingCriteria order, + String[] filterParams) { + + // map to return + Map resultMap = new HashMap(); + + // NavigableMap to contain sorted TreeMap + NavigableMap results = new TreeMap(); + + // # of records matching the provided filters + // this would replace totalMatchedObjectCount ONLY if there is at least + // one filter active + int numOfAllRecordsMatchingFilters = 0; + + // return empty results for + // 1. offset is smaller than 0 + // 2. offset is greater than or equal to total # of matched objects + // 3. page size is 0 + if (offset < -1 || + offset >= totalMatchedObjectCount || + maxResultCount == 0) { + resultMap.put("totalMatchedObjectCount", 0); + resultMap.put("results", results); + return resultMap; + } + + // filters results based on the given criteria + if (filterParams != null && filterParams.length > 0) { + Set keys = vmsMap.keySet(); + Map filteredVmsMap = + new HashMap(); + + for (String key : keys) { + if (!vmsMap.containsKey(key)) { + continue; + } + + // filters out VMs not matching the criteria + if (vmMatchesFilterCriteria(vmsMap.get(key), filterParams)) { + filteredVmsMap.put(key, vmsMap.get(key)); + numOfAllRecordsMatchingFilters++; + } + } + + // updates totalmatchedObjectCount and vmsMap + vmsMap = filteredVmsMap; + totalMatchedObjectCount = numOfAllRecordsMatchingFilters; + } + + int endIndex = offset + maxResultCount - 1; + if (totalMatchedObjectCount == 0) { + endIndex = totalMatchedObjectCount; + } else if ((endIndex >= totalMatchedObjectCount) || (maxResultCount < 0)) { + endIndex = totalMatchedObjectCount - 1; + } + + if (order != null && order.orderingProperties.length > 0) { + SortType sortType = order.orderingProperties[0].orderingType; + String orderBy = order.orderingProperties[0].propertyNames[0]; + results = new TreeMap( + new VicVmComparator( + vmsMap, + orderBy, + sortType.equals(SortType.DESCENDING) + ) + ); + results.putAll(vmsMap); + } + + // returns the sorted/filtered/paginated sub map + resultMap.put("totalMatchedObjectCount", totalMatchedObjectCount); + + if (results.size() == 0) { + resultMap.put("results", results); + } else { + String[] vAppKeys = results.keySet().toArray(new String[]{}); + resultMap.put("results", + results.subMap(vAppKeys[offset], true, vAppKeys[endIndex], true)); + } + + return resultMap; + } + + private static boolean vmMatchesFilterCriteria( + ModelObject mo, String[] filterParams) { + for (String filterParam : filterParams) { + String[] params = filterParam.split("="); + if (!getVmPropValue(mo, params[0]) + .contains(params[1].toLowerCase().trim())) { + return false; + } + } + return true; + } + + private static String getVmPropValue(ModelObject mo, String property) { + if (mo instanceof VirtualContainerHostVm) { + if (BaseVm.VM_NAME.equals(property)) { + return ((VirtualContainerHostVm) mo).getName().toLowerCase(); + } else if (Vch.VM_VCH_IP.equals(property)) { + return ((VirtualContainerHostVm) mo).getClientIp().toLowerCase(); + } else if (BaseVm.VM_OVERALL_STATUS.equals(property)) { + return ((VirtualContainerHostVm) mo).getOverallStatus() + .toLowerCase(); + } + return null; + } else if (mo instanceof ContainerVm) { + if (Container.VM_CONTAINERNAME_KEY.equals(property)) { + return ((ContainerVm) mo).getContainerName().toLowerCase(); + } else if (BaseVm.Runtime.VM_POWERSTATE_BASENAME.equals(property)) { + return ((ContainerVm) mo).getPowerState().toLowerCase(); + } else if (BaseVm.VM_GUESTMEMORYUSAGE.equals(property)) { + return Integer.toString(((ContainerVm) mo).getGuestMemoryUsage()); + } else if (BaseVm.VM_OVERALLCPUUSAGE.equals(property)) { + return Integer.toString(((ContainerVm) mo).getOverallCpuUsage()); + } else if (BaseVm.VM_COMMITTEDSTORAGE.equals(property)) { + return Long.toString(((ContainerVm) mo).getCommittedStorage()); + } else if (Container.VM_PORTMAPPING_KEY.equals(property)) { + String pm = ((ContainerVm) mo).getPortMapping(); + return pm != null ? pm.toLowerCase() : ""; + } else if (Container.PARENT_NAME_KEY.equals(property)) { + return ((ContainerVm) mo).getParentObjectName(); + } else if (BaseVm.VM_NAME.equals(property)) { + return ((ContainerVm) mo).getName().toLowerCase(); + } else if (Container.VM_IMAGENAME_KEY.equals(property)) { + return ((ContainerVm) mo).getImageName().toLowerCase(); + } + } + return null; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ServicesController.java b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ServicesController.java new file mode 100644 index 000000000..d2fe1f12f --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/mvc/ServicesController.java @@ -0,0 +1,143 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + + */ + +package com.vmware.vic.mvc; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.vmware.vic.services.CloneTicketService; +import com.vmware.vic.services.ResourcePoolService; +import com.vmware.vic.services.VicApplianceService; +import com.vmware.vic.services.VicUserSessionService; + +/** + * A controller to serve HTTP JSON GET/POST requests to the endpoint "/services". + * Its purpose is simply to redirect HTTP requests to the service APIs implemented in + * separate components. + */ +@Controller +@RequestMapping(value = "/services") +public class ServicesController { + private final ResourcePoolService _resourcePoolService; + private final VicUserSessionService _vicUserSessionService; + private final VicApplianceService _vicApplianceService; + private final CloneTicketService _cloneTicketService; + + @Autowired + public ServicesController( + @Qualifier("rpService") ResourcePoolService resourcePoolService, + @Qualifier("vicUserSessionService") VicUserSessionService vicUserSessionService, + @Qualifier("vaService") VicApplianceService vicApplianceService, + @Qualifier("cloneTicketService") CloneTicketService cloneTicketService) { + _resourcePoolService = resourcePoolService; + _vicApplianceService = vicApplianceService; + _vicUserSessionService = vicUserSessionService; + _cloneTicketService = cloneTicketService; + } + + // Empty controller to avoid compiler warnings in vic's bundle-context.xml + // where the bean is declared + public ServicesController() { + _resourcePoolService = null; + _vicUserSessionService = null; + _vicApplianceService = null; + _cloneTicketService = null; + } + + /** + * Check if the name for a new ResourcePool already exists + * @throws Exception + */ + @RequestMapping(value = "/check-rp-uniqueness", method = RequestMethod.POST) + @ResponseBody + public boolean checkRpUniqueness( + @RequestParam(value = "name", required = true) String name) throws Exception { + return _resourcePoolService.isNameUnique(name); + } + + /** + * Acquire a cloned session ticket from vSphere for auth + * @throws Exception + */ + @RequestMapping(value = "/acquire-clone-ticket", method = RequestMethod.POST) + @ResponseBody + public String acquireCloneTicket(@RequestParam(value = "serviceGuid", required = true) String serviceGuid) throws Exception { + return _cloneTicketService.acquireCloneTicket(serviceGuid); + } + + /** + * Check if the current session user is vSphere admin + * @return true if admin + */ + @RequestMapping(value = "/is-user-vsphere-admin", method = RequestMethod.GET) + @ResponseBody + public boolean isUserVsphereAdmin() { + return _vicUserSessionService.isCurrentUserVsphereAdmin(); + } + + @RequestMapping(value = "/get-vic-appliances", method = RequestMethod.GET) + @ResponseBody + public String[] getVicAppliances() { + return _vicApplianceService.getVicAppliancesList(); + } + + /** + * Generic handling of internal exceptions. + * Sends a 500 server error response along with a json body with messages + * + * @param ex The exception that was thrown. + * @param response + * @return a map containing the exception message, the cause, and a stackTrace + */ + @ExceptionHandler(Exception.class) + @ResponseBody + public Map handleException(Exception ex, HttpServletResponse response) { + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + + Map errorMap = new HashMap(); + errorMap.put("message", ex.getMessage()); + if(ex.getCause() != null) { + errorMap.put("cause", ex.getCause().getMessage()); + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + ex.printStackTrace(pw); + errorMap.put("stackTrace", sw.toString()); + + return errorMap; + } +} + diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/CloneTicketService.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/CloneTicketService.java new file mode 100644 index 000000000..128867843 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/CloneTicketService.java @@ -0,0 +1,17 @@ +/* +Copyright 2017 VMware, Inc. All Rights Reserved. +Licensed 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.vmware.vic.services; + +public interface CloneTicketService { + public String acquireCloneTicket(String serviceGuid) throws Exception; +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/CloneTicketServiceImpl.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/CloneTicketServiceImpl.java new file mode 100644 index 000000000..559356fa3 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/CloneTicketServiceImpl.java @@ -0,0 +1,49 @@ +/* +Copyright 2017 VMware, Inc. All Rights Reserved. +Licensed 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.vmware.vic.services; +import com.vmware.vic.PropFetcher; +import com.vmware.vim25.RuntimeFaultFaultMsg; + +public class CloneTicketServiceImpl implements CloneTicketService { + + private final PropFetcher _propFetcher; + + public CloneTicketServiceImpl (PropFetcher propFetcher) { + + if (propFetcher == null) { + throw new IllegalArgumentException("constructor argument cannot be null"); + } + + _propFetcher = propFetcher; + } + + /** + * Obtain a clone ticket from vSphere + * @throws Exception + */ + @Override + public String acquireCloneTicket(String serviceGuid) throws Exception { + + String acquireCloneTicket = ""; + + try { + acquireCloneTicket = _propFetcher.acquireCloneTicket(serviceGuid); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (RuntimeFaultFaultMsg e) { + e.printStackTrace(); + } + + return acquireCloneTicket; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/ResourcePoolService.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/ResourcePoolService.java new file mode 100644 index 000000000..b25972a22 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/ResourcePoolService.java @@ -0,0 +1,22 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.services; + +public interface ResourcePoolService { + public boolean isNameUnique(String name) throws Exception; +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/ResourcePoolServiceImpl.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/ResourcePoolServiceImpl.java new file mode 100644 index 000000000..0cda19c1e --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/ResourcePoolServiceImpl.java @@ -0,0 +1,169 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.services; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.vmware.vic.model.constants.VsphereObjects; +import com.vmware.vic.mvc.ServicesController; +import com.vmware.vise.data.Constraint; +import com.vmware.vise.data.query.CompositeConstraint; +import com.vmware.vise.data.query.Comparator; +import com.vmware.vise.data.query.Conjoiner; +import com.vmware.vise.data.query.DataService; +import com.vmware.vise.data.query.PropertyConstraint; +import com.vmware.vise.data.query.QuerySpec; +import com.vmware.vise.data.query.RequestSpec; +import com.vmware.vise.data.query.Response; +import com.vmware.vise.data.ResourceSpec; +import com.vmware.vise.data.PropertySpec; + +/** + * Resource Pool Service implementation for checking the uniqueness of + * a new ResourcePool (or VirtualApp) to be created by VCH life cycle + * operations wizard + */ +public class ResourcePoolServiceImpl implements ResourcePoolService { + private final static Log _logger = LogFactory.getLog(ResourcePoolServiceImpl.class); + private DataService _dataService; + + public ResourcePoolServiceImpl(DataService dataService) + throws IllegalArgumentException { + _dataService = dataService; + } + + /** + * Checks if there is any VirtualApp or ResourcePool object with + * the given "name" + * @param name + * @return true if no object with the given name exists, otherwise false + * @throws Exception + */ + public boolean isNameUnique(String name) throws Exception { + PropertyConstraint sameVappNameConstraint = createPropertyConstraint( + VsphereObjects.VirtualApp, + VsphereObjects.NamePropertyKey, + Comparator.EQUALS, + name); + + PropertyConstraint sameRpNameConstraint = createPropertyConstraint( + VsphereObjects.ResourcePool, + VsphereObjects.NamePropertyKey, + Comparator.EQUALS, + name); + + PropertyConstraint[] propConstraints = new PropertyConstraint[]{ + sameVappNameConstraint, + sameRpNameConstraint + }; + + CompositeConstraint compConstraint = + createCompositeConstraint(propConstraints, Conjoiner.OR); + + QuerySpec querySpec = new QuerySpec(); + querySpec.name = "check-name-uniqueness"; + + querySpec.resourceSpec = createEmptyResourceSpec(); + + querySpec.resourceSpec.constraint = compConstraint; + + RequestSpec requestSpec = new RequestSpec(); + requestSpec.querySpec = new QuerySpec[] { querySpec }; + + Response response = _dataService.getData(requestSpec); + if (response.resultSet[0].error != null) { + _logger.error(response.resultSet[0].error); + throw new Exception(response.resultSet[0].error); + } + if (response.resultSet[0].totalMatchedObjectCount > 0) { + return false; + } + return true; + } + + /** + * Creates a PropertyConstraint + * + * @param objectType + * Type of object to retrieve. + * @param propertyName + * Name of the property to be matched. + * @param comparator + * The operator to use for comparison + * ('equals', 'unequals', etc.) + * @param comparableValue + * The value to compare. + * + * @return + * A PropertyConstraint. + */ + private PropertyConstraint createPropertyConstraint( + String objectType, + String propertyName, + Comparator comparator, + Object comparableValue) { + + PropertyConstraint constraint = new PropertyConstraint(); + constraint.targetType = objectType; + constraint.propertyName = propertyName; + constraint.comparator = comparator; + constraint.comparableValue = comparableValue; + + return constraint; + } + + /** + * Creates a CompositeConstraint by given nested Constraints and + * conjoiner between them. + * + * @param constraints + * Array of nested Constraints to add into the composite constraint. + * @param conjoiner + * Conjoiner defining how to combine the nested Constraints. + * Can be "AND" or "OR". + * + * @return + * A CompositeConstraint. + */ + private CompositeConstraint createCompositeConstraint( + Constraint[] nestedConstraints, + Conjoiner conjoiner) { + + // create the result constraint and initialize it + CompositeConstraint compositeConstraint = new CompositeConstraint(); + compositeConstraint.nestedConstraints = nestedConstraints; + compositeConstraint.conjoiner = conjoiner; + + return compositeConstraint; + } + + /** + * Creates an empty ResourceSpec without any additional + * properties of that object. + * + * @return an empty ResourceSpec + */ + private ResourceSpec createEmptyResourceSpec() { + PropertySpec propSpec = new PropertySpec(); + propSpec.propertyNames = new String[] {}; + ResourceSpec resourceSpec = new ResourceSpec(); + resourceSpec.propertySpecs = new PropertySpec[] { propSpec }; + return resourceSpec; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/SampleActionService.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/SampleActionService.java new file mode 100644 index 000000000..a9d5d452f --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/SampleActionService.java @@ -0,0 +1,45 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +*/ + +package com.vmware.vic.services; + +/** + * Service handling some actions invoked from the UI + * + * It must be declared as osgi:service with the same name in + * main/resources/META-INF/spring/bundle-context-osgi.xml + */ +public interface SampleActionService { + /** + * Sample action called on the server. + * + * @param objRef Internal reference to the vCenter object for that action. + */ + public void sampleAction1(Object objRef); + + /** + * Sample action called on the server. + * + * @param objRef Internal reference to the vCenter object for that action. + * @return true is the action is successful, false otherwise. + */ + public boolean sampleAction2(Object objRef); +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/SampleActionServiceImpl.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/SampleActionServiceImpl.java new file mode 100644 index 000000000..b64b37d66 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/SampleActionServiceImpl.java @@ -0,0 +1,76 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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. + +Mac OS script starting an Ant build of the current flex project +Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +*/ + +package com.vmware.vic.services; + +import com.vmware.vise.vim.data.VimObjectReferenceService; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation of the SampleActionService interface + */ +public class SampleActionServiceImpl implements SampleActionService { + + private static final Log _logger = + LogFactory.getLog(SampleActionServiceImpl.class); + + // ObjectReferenceService which provides more info for vSphere objects + private final VimObjectReferenceService _vimObjectReferenceService; + + /** + * Constructor used to inject the utility services (see the declaration + * in main/resources/spring/bundle-context-osgi.xml) + * + * @param vimObjectReferenceService + * Service to access vSphere object references information. + */ + public SampleActionServiceImpl( + VimObjectReferenceService vimObjectReferenceService) { + _vimObjectReferenceService = vimObjectReferenceService; + } + + public void sampleAction1(Object vmReference) { + // All vCenter objects sent from the UI are serialized into an internal type. + // You can use VimObjectReferenceService to get the right information. + // See samples/vsphereviews/vsphere-wssdk-provider for an example using + // the vSphere Web Service SDK to talk to vCenter. + String type = _vimObjectReferenceService.getResourceObjectType(vmReference); + String value = _vimObjectReferenceService.getValue(vmReference); + _logger.info("sampleAction1 called with object type = " + type + + ", value = " + value); + + // Note: the action processing should take place on the back-end Server, + // nothing heavy should run in the vSphere Web Client server JVM! + // If back-end processing takes time it's better to return right away here + // and let the UI deals with updates later. + } + + public boolean sampleAction2(Object vmReference) { + String type = _vimObjectReferenceService.getResourceObjectType(vmReference); + String value = _vimObjectReferenceService.getValue(vmReference); + _logger.info("sampleAction2 called with object type = " + type + + ", value = " + value); + + return true; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/VicApplianceService.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicApplianceService.java new file mode 100644 index 000000000..feb14e72e --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicApplianceService.java @@ -0,0 +1,22 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.services; + +public interface VicApplianceService { + public String[] getVicAppliancesList(); +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/VicApplianceServiceImpl.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicApplianceServiceImpl.java new file mode 100644 index 000000000..06875c03e --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicApplianceServiceImpl.java @@ -0,0 +1,45 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.services; + +import com.vmware.vic.PropFetcher; +import com.vmware.vim25.InvalidPropertyFaultMsg; +import com.vmware.vim25.RuntimeFaultFaultMsg; + +public class VicApplianceServiceImpl implements VicApplianceService { + private final PropFetcher _propFetcher; + + public VicApplianceServiceImpl ( + PropFetcher propFetcher) { + if (propFetcher == null) { + throw new IllegalArgumentException("constructor argument cannot be null"); + } + _propFetcher = propFetcher; + } + + @Override + public String[] getVicAppliancesList() { + String[] results = null; + try { + results = _propFetcher.getVicApplianceVms().toArray(new String[]{}); + } catch (RuntimeFaultFaultMsg | InvalidPropertyFaultMsg e) { + e.printStackTrace(); + } + return results; + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/VicUserSessionService.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicUserSessionService.java new file mode 100644 index 000000000..e4e795063 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicUserSessionService.java @@ -0,0 +1,22 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.services; + +public interface VicUserSessionService { + boolean isCurrentUserVsphereAdmin(); +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/services/VicUserSessionServiceImpl.java b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicUserSessionServiceImpl.java new file mode 100644 index 000000000..3624741c2 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/services/VicUserSessionServiceImpl.java @@ -0,0 +1,35 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.services; + +import com.vmware.vic.PropFetcher; + +public class VicUserSessionServiceImpl implements VicUserSessionService { + private final PropFetcher _propFetcher; + public VicUserSessionServiceImpl(PropFetcher propFetcher) { + if (propFetcher == null) { + throw new IllegalArgumentException("constructor argument cannot be null"); + } + this._propFetcher = propFetcher; + } + + @Override + public boolean isCurrentUserVsphereAdmin() { + return this._propFetcher.isSessionUserVsphereAdmin(); + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/utils/ConfigLoader.java b/h5c/vic-service/src/main/java/com/vmware/vic/utils/ConfigLoader.java new file mode 100644 index 000000000..cea484735 --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/utils/ConfigLoader.java @@ -0,0 +1,48 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.utils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class ConfigLoader { + private final Properties prop; + + public ConfigLoader(String configFile) throws IOException { + InputStream is = null; + try { + prop = new Properties(); + is = getClass().getClassLoader().getResourceAsStream(configFile); + if (is != null) { + prop.load(is); + } else { + throw new FileNotFoundException("'" + configFile + "' was not found"); + } + } finally { + if(is != null) { + is.close(); + } + } + } + + public String getProp(String key) { + return prop.getProperty(key); + } +} diff --git a/h5c/vic-service/src/main/java/com/vmware/vic/utils/VicVmComparator.java b/h5c/vic-service/src/main/java/com/vmware/vic/utils/VicVmComparator.java new file mode 100644 index 000000000..e09c949ab --- /dev/null +++ b/h5c/vic-service/src/main/java/com/vmware/vic/utils/VicVmComparator.java @@ -0,0 +1,165 @@ +/* + Copyright 2017 VMware, Inc. All Rights Reserved. + + Licensed 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.vmware.vic.utils; + +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Comparator; +import java.util.Map; + +import com.vmware.vic.model.ContainerVm; +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.VirtualContainerHostVm; +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vic.model.constants.Container; +import com.vmware.vic.model.constants.Vch; + +/** + * Comparator for VIC VMs + */ +public class VicVmComparator implements Comparator { + private Map base; + private String compareBy; + private boolean reverse; + + public VicVmComparator( + Map base, + String compareBy, + boolean reverse + ) { + this.base = base; + this.compareBy = compareBy; + this.reverse = reverse; + } + + /** + * Comparator.compare() implementation for ModelObject + * @param a : objectId of VM 1 + * @param b : objectId of VM 2 + */ + @Override + public int compare(String a, String b) { + ModelObject modObjA = (ModelObject) base.get(a); + ModelObject modObjB = (ModelObject) base.get(b); + + return compareValues(modObjA, modObjB); + } + + /** + * Compare ModelObjects a and b based on compareBy and reverse. + * This is basically a compareTo() & compare() wrapper for string, + * integer, long, and double types + * @param modObjA : ModelObject a + * @param modObjB : ModelObject b + * @return compare result + */ + private int compareValues(ModelObject modObjA, ModelObject modObjB) { + int result = 0; + String valueA = getStringPropertyFromVm(modObjA); + String valueB = getStringPropertyFromVm(modObjB); + Number numA = tryParseNumericValue(valueA); + Number numB = tryParseNumericValue(valueB); + + // if either numA or numB ends up null then string comparison is used + if (numA == null || numB == null) { + result = getStringPropertyFromVm(modObjA) + .compareTo(getStringPropertyFromVm(modObjB)); + } else { + if (numA instanceof Long && + numB instanceof Long) { + result = Long.compare(numA.longValue(), numB.longValue()); + } else if (numA instanceof Integer && + numB instanceof Integer) { + result = Integer.compare(numA.intValue(), numB.intValue()); + } else if (numA instanceof Double && + numB instanceof Double) { + result = Double.compare(numA.doubleValue(), numB.doubleValue()); + } + } + + // in case the values are the same between modObjA and modObjB, + // compare by hashcode + if (result == 0) { + result = modObjA.hashCode() - modObjB.hashCode(); + } + + return result * (this.reverse ? -1 : 1); + } + + /** + * Check if the passed string is numeric and return the parsed result + * If it's not numeric, return null + * @param value : value of the String type + * @return a Number instance if numeric, or null otherwise + */ + private Number tryParseNumericValue(String value) { + NumberFormat formatter = NumberFormat.getInstance(); + ParsePosition pos = new ParsePosition(0); + Number parsed = formatter.parse(value, pos); + // if the position is not the same as string's length + // then it means parse was not completely successful + if (value.length() != pos.getIndex()) { + return null; + } + return parsed; + } + + /** + * Retrieve string property value from a ModelObject + * @param mo : ModelObject instance + * @return property value for the property name specified by compareBy + */ + private String getStringPropertyFromVm(ModelObject mo) { + if (mo instanceof VirtualContainerHostVm) { + if (BaseVm.ID.equals(compareBy)) { + return ((VirtualContainerHostVm) mo).getId(); + } else if (BaseVm.VM_NAME.equals(compareBy)) { + return ((VirtualContainerHostVm) mo).getName(); + } else if (Vch.VM_VCH_IP.equals(compareBy)) { + return ((VirtualContainerHostVm) mo).getClientIp(); + } else if (BaseVm.VM_OVERALL_STATUS.equals(compareBy)) { + return ((VirtualContainerHostVm) mo).getOverallStatus(); + } + } else if (mo instanceof ContainerVm) { + if (BaseVm.ID.equals(compareBy)) { + return ((ContainerVm) mo).getId(); + } else if (Container.VM_CONTAINERNAME_KEY.equals(compareBy)) { + return ((ContainerVm) mo).getContainerName(); + } else if (BaseVm.Runtime.VM_POWERSTATE_BASENAME.equals(compareBy)) { + return ((ContainerVm) mo).getPowerState(); + } else if (BaseVm.VM_GUESTMEMORYUSAGE.equals(compareBy)) { + return Integer.toString(((ContainerVm) mo).getGuestMemoryUsage()); + } else if (BaseVm.VM_OVERALLCPUUSAGE.equals(compareBy)) { + return Integer.toString(((ContainerVm) mo).getOverallCpuUsage()); + } else if (BaseVm.VM_COMMITTEDSTORAGE.equals(compareBy)) { + return Long.toString(((ContainerVm) mo).getCommittedStorage()); + } else if (Container.VM_PORTMAPPING_KEY.equals(compareBy)) { + String pm = ((ContainerVm) mo).getPortMapping(); + return pm != null ? pm : ""; + } else if (BaseVm.VM_NAME.equals(compareBy)) { + return ((ContainerVm) mo).getName(); + } else if (Container.PARENT_NAME_KEY.equals(compareBy)) { + return ((ContainerVm) mo).getParentObjectName(); + } else if (Container.VM_IMAGENAME_KEY.equals(compareBy)) { + return ((ContainerVm) mo).getImageName(); + } + } + + return null; + } + +} diff --git a/h5c/vic-service/src/main/resources/META-INF/MANIFEST.MF b/h5c/vic-service/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 000000000..f576e0c78 --- /dev/null +++ b/h5c/vic-service/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,31 @@ +Manifest-Version: 1.0 +Bundle-Vendor: VMware +Bundle-Version: 1.0.0 +Tool: Bundlor 1.1.0.RELEASE +Bundle-Name: vic-service +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: com.vmware.vic.service +Export-Package: com.vmware.vic.services;version="1.0.0", + com.vmware.vic.mvc;version="1.0.0", + com.vmware.vic.filters;version="1.0.0", + com.vmware.vic;version="1.0.0" +Import-Package: com.google.gson;version="2.3.1", + com.vmware.vim.binding.vmodl;version=0, + com.vmware.vim25;version="[6.0.0,7)", + com.vmware.vise.core.model;version=0, + com.vmware.vise.data;version=0, + com.vmware.vise.data.query;version=0, + com.vmware.vise.data.uri;version=0, + com.vmware.vise.security;version=0, + com.vmware.vise.usersession;version=0, + com.vmware.vise.vim.data;version=0, + javax.net.ssl;version=0, + javax.servlet.http;version="3.0", + org.apache.commons.logging;version="1.1.1", + org.springframework.beans.factory.annotation, + org.springframework.web.context.support, + org.springframework.beans.factory.config, + org.springframework.web.context, + org.springframework.http, + org.springframework.stereotype, + org.springframework.web.bind.annotation diff --git a/h5c/vic-service/src/main/resources/META-INF/spring/bundle-context-osgi.xml b/h5c/vic-service/src/main/resources/META-INF/spring/bundle-context-osgi.xml new file mode 100644 index 000000000..841ff457d --- /dev/null +++ b/h5c/vic-service/src/main/resources/META-INF/spring/bundle-context-osgi.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic-service/src/main/resources/META-INF/spring/bundle-context.xml b/h5c/vic-service/src/main/resources/META-INF/spring/bundle-context.xml new file mode 100644 index 000000000..652b0cf2b --- /dev/null +++ b/h5c/vic-service/src/main/resources/META-INF/spring/bundle-context.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic-service/src/main/resources/configs.properties b/h5c/vic-service/src/main/resources/configs.properties new file mode 100644 index 000000000..999dfa359 --- /dev/null +++ b/h5c/vic-service/src/main/resources/configs.properties @@ -0,0 +1 @@ +uiVersion=UI_VERSION_PLACEHOLDER diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/Common.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/Common.java new file mode 100644 index 000000000..b6f33e6d8 --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/Common.java @@ -0,0 +1,178 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import com.vmware.vic.model.ContainerVm; +import com.vmware.vic.model.VirtualContainerHostVm; +import com.vmware.vim25.ArrayOfOptionValue; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.OptionValue; +import com.vmware.vim25.VirtualMachinePowerState; +import com.vmware.vim25.VirtualMachineQuickStats; +import com.vmware.vim25.VirtualMachineStorageSummary; +import com.vmware.vim25.VirtualMachineSummary; + +public class Common { + protected VirtualContainerHostVm getMockedVirtualContainerHostVm( + String serverGuid, + String vmObjectId, + String vmName, + ManagedEntityStatus overallStatus, + VirtualMachinePowerState powerState, + String clientIpBase64Encoded) { + List dps = new ArrayList(); + DynamicProperty dpName = new DynamicProperty(); + dpName.setName("name"); + dpName.setVal(vmName); + dps.add(dpName); + + DynamicProperty dpOverallStatus = new DynamicProperty(); + dpOverallStatus.setName("overallStatus"); + dpOverallStatus.setVal(overallStatus); + dps.add(dpOverallStatus); + + DynamicProperty dpPowerState = new DynamicProperty(); + dpPowerState.setName("runtime.powerState"); + dpPowerState.setVal(powerState); + dps.add(dpPowerState); + + DynamicProperty dpSummary = new DynamicProperty(); + VirtualMachineSummary vmSummary = new VirtualMachineSummary(); + VirtualMachineQuickStats vmQuickStatsMock = mock(VirtualMachineQuickStats.class); + when(vmQuickStatsMock.getGuestMemoryUsage()).thenReturn(500); + when(vmQuickStatsMock.getOverallCpuUsage()).thenReturn(1000); + vmSummary.setQuickStats(vmQuickStatsMock); + VirtualMachineStorageSummary vmStorageSummaryMock = mock(VirtualMachineStorageSummary.class); + when(vmStorageSummaryMock.getCommitted()).thenReturn((long)123456789); + vmSummary.setStorage(vmStorageSummaryMock); + + dpSummary.setName("summary"); + dpSummary.setVal(vmSummary); + dps.add(dpSummary); + + DynamicProperty dpConfigExtraConfig = new DynamicProperty(); + ArrayOfOptionValue ovArrayMock = mock(ArrayOfOptionValue.class); + List ovList = new ArrayList(); + OptionValue ovClientIpKey = new OptionValue(); + ovClientIpKey.setKey("guestinfo.vice..init.networks|client.assigned.IP"); + ovClientIpKey.setValue(clientIpBase64Encoded); + ovList.add(ovClientIpKey); + when(ovArrayMock.getOptionValue()).thenReturn(ovList); + dpConfigExtraConfig.setName("config.extraConfig"); + dpConfigExtraConfig.setVal(ovArrayMock); + dps.add(dpConfigExtraConfig); + + ManagedObjectReference mor = new ManagedObjectReference(); + mor.setType("VirtualMachine"); + mor.setValue(vmObjectId); + + ObjectContent objContent = mock(ObjectContent.class); + when(objContent.getObj()).thenReturn(mor); + when(objContent.getPropSet()).thenReturn(dps); + + VirtualContainerHostVm vm = new VirtualContainerHostVm(objContent, serverGuid); + return vm; + } + + protected ContainerVm getMockedContainerVm( + String serverGuid, + String vmObjectId, + String vmName, + ManagedEntityStatus overallStatus, + VirtualMachinePowerState powerState, + String containerName, + String imageName, + String portMapping) { + // mock ObjectContent object and its members + List dps = new ArrayList(); + DynamicProperty dpName = new DynamicProperty(); + dpName.setName("name"); + dpName.setVal(vmName); + dps.add(dpName); + + DynamicProperty dpOverallStatus = new DynamicProperty(); + dpOverallStatus.setName("overallStatus"); + dpOverallStatus.setVal(overallStatus); + dps.add(dpOverallStatus); + + DynamicProperty dpPowerState = new DynamicProperty(); + dpPowerState.setName("runtime.powerState"); + dpPowerState.setVal(powerState); + dps.add(dpPowerState); + + DynamicProperty dpSummary = new DynamicProperty(); + VirtualMachineSummary vmSummary = new VirtualMachineSummary(); + VirtualMachineQuickStats vmQuickStatsMock = mock(VirtualMachineQuickStats.class); + when(vmQuickStatsMock.getGuestMemoryUsage()).thenReturn(500); + when(vmQuickStatsMock.getOverallCpuUsage()).thenReturn(1000); + vmSummary.setQuickStats(vmQuickStatsMock); + VirtualMachineStorageSummary vmStorageSummaryMock = mock(VirtualMachineStorageSummary.class); + when(vmStorageSummaryMock.getCommitted()).thenReturn((long)123456789); + vmSummary.setStorage(vmStorageSummaryMock); + + dpSummary.setName("summary"); + dpSummary.setVal(vmSummary); + dps.add(dpSummary); + + DynamicProperty dpConfigExtraConfig = new DynamicProperty(); + ArrayOfOptionValue ovArrayMock = mock(ArrayOfOptionValue.class); + List ovList = new ArrayList(); + + OptionValue ovContainerNameKey = new OptionValue(); + ovContainerNameKey.setKey("common/name"); + ovContainerNameKey.setValue(containerName); + ovList.add(ovContainerNameKey); + + OptionValue ovImageNameKey = new OptionValue(); + ovImageNameKey.setKey("guestinfo.vice./repo"); + ovImageNameKey.setValue(imageName); + ovList.add(ovImageNameKey); + + if (portMapping != null) { + OptionValue ovPortMappingKey = new OptionValue(); + ovPortMappingKey.setKey("guestinfo.vice./networks|bridge/ports~"); + ovPortMappingKey.setValue(portMapping); + ovList.add(ovPortMappingKey); + } + + when(ovArrayMock.getOptionValue()).thenReturn(ovList); + dpConfigExtraConfig.setName("config.extraConfig"); + dpConfigExtraConfig.setVal(ovArrayMock); + dps.add(dpConfigExtraConfig); + + ManagedObjectReference mor = new ManagedObjectReference(); + mor.setType("VirtualMachine"); + mor.setValue(vmObjectId); + + ObjectContent objContent = mock(ObjectContent.class); + when(objContent.getObj()).thenReturn(mor); + when(objContent.getPropSet()).thenReturn(dps); + + ContainerVm vm = new ContainerVm(objContent, serverGuid); + return vm; + } +} diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/ConfigLoaderTest.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/ConfigLoaderTest.java new file mode 100644 index 000000000..a47603b26 --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/ConfigLoaderTest.java @@ -0,0 +1,57 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import static org.junit.Assert.*; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.junit.Test; + +import com.vmware.vic.utils.ConfigLoader; + +@SuppressWarnings("unused") +public class ConfigLoaderTest { + @Test + public void testConfigLoaderLoads() { + try { + ConfigLoader configLoader = new ConfigLoader("configs.properties"); + assertNotNull(configLoader); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + @Test + public void testWrongConfigsFile() { + try { + ConfigLoader configLoader = new ConfigLoader("meh"); + } catch (FileNotFoundException ex) { + assertNotNull(ex.getMessage()); + } catch (IOException e) { + assertNotNull(e); + } + } + + @Test + public void testGetPropertyUiVersion() throws Exception { + ConfigLoader configLoader = new ConfigLoader("configs.properties"); + assertTrue(configLoader.getProp("uiVersion").length() > 0); + } +} diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/ContainerVmTest.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/ContainerVmTest.java new file mode 100644 index 000000000..7bda5cd62 --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/ContainerVmTest.java @@ -0,0 +1,176 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import static org.junit.Assert.*; + +import java.net.URI; + +import org.junit.Test; + +import com.vmware.vic.ModelObjectUriResolver; +import com.vmware.vic.model.ContainerVm; +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vic.model.constants.Container; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.VirtualMachinePowerState; + +public class ContainerVmTest extends Common { + private static final String VM_TYPE_CONTAINERVM = "vic:ContainerVm"; + private ContainerVm _vm; + + private ContainerVm createMockContainerVmWithoutPortMapping() { + ContainerVm vm = getMockedContainerVm( + "server1", + "id1", + "container vm 1", + ManagedEntityStatus.GREEN, + VirtualMachinePowerState.POWERED_ON, + "container without portmapping info", + "busybox", + null); + return vm; + } + + private ContainerVm createMockContainerVmWithPortMapping() { + ContainerVm vm = getMockedContainerVm( + "server2", + "id2", + "container vm 2", + ManagedEntityStatus.GRAY, + VirtualMachinePowerState.SUSPENDED, + "container with portmapping info", + "nginx", + "8080:80/tcp"); + return vm; + } + + @Test + public void testGetTypeForContainerVmWithoutPortMapping() { + _vm = createMockContainerVmWithoutPortMapping(); + assertNotNull(_vm); + assertEquals(VM_TYPE_CONTAINERVM, _vm.getType()); + } + + @Test + public void testGetIdForContainerVmWithoutPortMapping() { + _vm = createMockContainerVmWithoutPortMapping(); + assertEquals("server1/id1", _vm.getId()); + } + + @Test + public void testGetUriForContainerVmWithoutPortMapping() { + _vm = createMockContainerVmWithoutPortMapping(); + ModelObjectUriResolver uriResolver = new ModelObjectUriResolver(); + URI uri = _vm.getUri(uriResolver); + assertEquals(String.format( + "urn:vic:%s:%s", VM_TYPE_CONTAINERVM, "server1/id1"), + uri.toString()); + } + + @Test + public void testGetPropertyForContainerVmWithoutPortMapping() { + _vm = createMockContainerVmWithoutPortMapping(); + assertTrue(_vm.getProperty(BaseVm.VM_NAME).equals("container vm 1")); + assertTrue(_vm.getProperty(BaseVm.VM_OVERALL_STATUS) + .equals(ManagedEntityStatus.GREEN)); + assertTrue(_vm.getProperty(BaseVm.Runtime.VM_POWERSTATE_FULLPATH) + .equals(VirtualMachinePowerState.POWERED_ON)); + assertTrue(_vm.getProperty(Container.VM_CONTAINERNAME_KEY) + .equals("container without portmapping info")); + assertTrue(_vm.getProperty(Container.VM_IMAGENAME_KEY) + .equals("busybox")); + assertTrue(_vm.getProperty(BaseVm.VM_OVERALLCPUUSAGE).equals(1000)); + assertTrue(_vm.getProperty(BaseVm.VM_GUESTMEMORYUSAGE).equals(500)); + assertTrue(_vm.getProperty(BaseVm.VM_COMMITTEDSTORAGE) + .equals((long)123456789)); + assertTrue(_vm.getProperty("iDontExist") + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + } + + @Test + public void testGettersForContainerVmWithoutPortMapping() { + _vm = createMockContainerVmWithoutPortMapping(); + assertEquals(_vm.getName(), "container vm 1"); + assertEquals(_vm.getOverallStatus(), "GREEN"); + assertEquals(_vm.getPowerState(), "POWERED_ON"); + assertEquals(_vm.getContainerName(), "container without portmapping info"); + assertEquals(_vm.getPortMapping(), null); + assertEquals(_vm.getImageName(), "busybox"); + assertEquals(_vm.getOverallCpuUsage(), 1000); + assertEquals(_vm.getGuestMemoryUsage(), 500); + assertEquals(_vm.getCommittedStorage(), (long)123456789); + } + + @Test + public void testGetTypeForContainerVmWithPortMapping() { + _vm = createMockContainerVmWithPortMapping(); + assertNotNull(_vm); + assertEquals("vic:ContainerVm", _vm.getType()); + } + + @Test + public void testGetIdForContainerVmWithPortMapping() { + _vm = createMockContainerVmWithPortMapping(); + assertEquals("server2/id2", _vm.getId()); + } + + @Test + public void testGetUriForContainerVmWithPortMapping() { + _vm = createMockContainerVmWithPortMapping(); + ModelObjectUriResolver uriResolver = new ModelObjectUriResolver(); + URI uri = _vm.getUri(uriResolver); + assertEquals("urn:vic:vic:ContainerVm:server2/id2", uri.toString()); + } + + @Test + public void testGetPropertyForContainerVmWithPortMapping() { + _vm = createMockContainerVmWithPortMapping(); + assertTrue(_vm.getProperty(BaseVm.VM_NAME).equals("container vm 2")); + assertTrue(_vm.getProperty(BaseVm.VM_OVERALL_STATUS) + .equals(ManagedEntityStatus.GRAY)); + assertTrue(_vm.getProperty(BaseVm.Runtime.VM_POWERSTATE_FULLPATH) + .equals(VirtualMachinePowerState.SUSPENDED)); + assertTrue(_vm.getProperty(Container.VM_CONTAINERNAME_KEY) + .equals("container with portmapping info")); + assertTrue(_vm.getProperty(Container.VM_IMAGENAME_KEY).equals("nginx")); + assertTrue(_vm.getProperty(Container.VM_PORTMAPPING_KEY) + .equals("8080:80/tcp")); + assertTrue(_vm.getProperty(BaseVm.VM_OVERALLCPUUSAGE).equals(1000)); + assertTrue(_vm.getProperty(BaseVm.VM_GUESTMEMORYUSAGE).equals(500)); + assertTrue(_vm.getProperty(BaseVm.VM_COMMITTEDSTORAGE) + .equals((long)123456789)); + assertTrue(_vm.getProperty("iDontExist") + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + } + + @Test + public void testGettersForContainerVmWithPortMapping() { + _vm = createMockContainerVmWithPortMapping(); + assertEquals(_vm.getName(), "container vm 2"); + assertEquals(_vm.getOverallStatus(), "GRAY"); + assertEquals(_vm.getPowerState(), "SUSPENDED"); + assertEquals(_vm.getContainerName(), "container with portmapping info"); + assertEquals(_vm.getImageName(), "nginx"); + assertEquals(_vm.getPortMapping(), "8080:80/tcp"); + assertEquals(_vm.getOverallCpuUsage(), 1000); + assertEquals(_vm.getGuestMemoryUsage(), 500); + assertEquals(_vm.getCommittedStorage(), (long)123456789); + } +} diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/ModelObjectUriResolverTest.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/ModelObjectUriResolverTest.java new file mode 100644 index 000000000..ab6a85aae --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/ModelObjectUriResolverTest.java @@ -0,0 +1,64 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import static org.junit.Assert.*; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.Before; +import org.junit.Test; + +import com.vmware.vic.ModelObjectUriResolver; + +public class ModelObjectUriResolverTest { + private ModelObjectUriResolver moUriResolver; + + @Before + public void setModelObjectUriResolver() { + moUriResolver = new ModelObjectUriResolver(); + } + + @Test + public void testGetResourceType() throws URISyntaxException { + URI uri = new URI("urn", "vic:vic:Root:server1/rootObject", null); + String resourceType = moUriResolver.getResourceType(uri); + assertEquals("vic:Root", resourceType); + } + + @Test + public void testGetServerGuid() throws URISyntaxException { + URI uri = new URI("urn", "vic:vic:Root:server1/rootObject", null); + String serverGuid = moUriResolver.getServerGuid(uri); + assertEquals("server1", serverGuid); + } + + @Test + public void testGetObjectId() throws URISyntaxException { + URI uri = new URI("urn", "vic:vic:VirtualContainerHostVm:vic/ALL", null); + String objectId = moUriResolver.getObjectId(uri); + assertEquals("ALL", objectId); + } + + @Test + public void testCreateUri() { + URI uri = moUriResolver.createUri("vic:Root", "server1/rootObject"); + assertEquals("urn:vic:vic:Root:server1/rootObject", uri.toString()); + } +} diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/RootTest.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/RootTest.java new file mode 100644 index 000000000..f16c7d1c1 --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/RootTest.java @@ -0,0 +1,70 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import static org.junit.Assert.*; + +import java.net.URI; + +import org.junit.Before; +import org.junit.Test; + +import com.vmware.vic.ModelObjectUriResolver; +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.Root; +import com.vmware.vic.model.RootInfo; + +public class RootTest { + private Root _root; + + @Before + public void setModelObject() { + _root = new Root( + new RootInfo(new String[]{"1.0"}), 0, 0); + } + + @Test + public void testGetType() { + assertEquals("vic:Root", _root.getType()); + } + + @Test + public void testGetId() { + assertEquals("vic/vic-root", _root.getId()); + } + + @Test + public void testGetUri() { + ModelObjectUriResolver uriResolver = new ModelObjectUriResolver(); + URI actual = _root.getUri(uriResolver); + assertEquals("urn:vic:vic:Root:vic/vic-root", actual.toString()); + } + + @Test + public void testGetProperty() { + assertFalse(_root.getProperty("uiVersion").equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_root.getProperty("vchVmsLen").equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_root.getProperty("containerVmsLen").equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertTrue(_root.getProperty("iDontExist").equals(ModelObject.UNSUPPORTED_PROPERTY)); + } + + @Test + public void testToString() { + assertTrue(_root.toString().contentEquals("uiVersion: 1.0, vchVms.length: 0, containerVms.length: 0")); + } +} diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/VirtualContainerHostVmTest.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/VirtualContainerHostVmTest.java new file mode 100644 index 000000000..5c410026d --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/VirtualContainerHostVmTest.java @@ -0,0 +1,98 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +import java.net.URI; + +import com.vmware.vic.ModelObjectUriResolver; +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.VirtualContainerHostVm; +import com.vmware.vic.model.constants.BaseVm; +import com.vmware.vic.model.constants.Vch; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.VirtualMachinePowerState; + +public class VirtualContainerHostVmTest extends Common { + private static final String VM_TYPE_VCHVM = "vic:VirtualContainerHostVm"; + private VirtualContainerHostVm _vm; + + @Before + public void setModelObject() { + _vm = getMockedVirtualContainerHostVm( + "server1", + "id1", + "test vm", + ManagedEntityStatus.GREEN, + VirtualMachinePowerState.POWERED_ON, + "ChFtuw=="); + } + + @Test + public void testGetType() { + assertEquals(VM_TYPE_VCHVM, _vm.getType()); + } + + @Test + public void testGetId() { + assertEquals("server1/id1", _vm.getId()); + } + + @Test + public void testGetUri() { + ModelObjectUriResolver uriResolver = new ModelObjectUriResolver(); + URI uri = _vm.getUri(uriResolver); + assertEquals(String.format( + "urn:vic:%s:%s", VM_TYPE_VCHVM, "server1/id1"), + uri.toString()); + } + + @Test + public void testGetProperty() { + assertFalse(_vm.getProperty(BaseVm.VM_NAME) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_vm.getProperty(BaseVm.VM_OVERALL_STATUS) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_vm.getProperty(BaseVm.Runtime.VM_POWERSTATE_FULLPATH) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_vm.getProperty(Vch.VM_CLIENT_IP) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_vm.getProperty(BaseVm.VM_OVERALLCPUUSAGE) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_vm.getProperty(BaseVm.VM_GUESTMEMORYUSAGE) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertFalse(_vm.getProperty(BaseVm.VM_COMMITTEDSTORAGE) + .equals(ModelObject.UNSUPPORTED_PROPERTY)); + assertTrue(_vm.getProperty("iDontExist").equals(ModelObject.UNSUPPORTED_PROPERTY)); + } + + @Test + public void testGetters() { + assertEquals(_vm.getName(), "test vm"); + assertEquals(_vm.getOverallStatus(), "GREEN"); + assertEquals(_vm.getPowerState(), "POWERED_ON"); + assertEquals(_vm.getClientIp(), "10.17.109.187"); + assertEquals(_vm.getOverallCpuUsage(), 1000); + assertEquals(_vm.getGuestMemoryUsage(), 500); + assertEquals(_vm.getCommittedStorage(), (long)123456789); + } +} diff --git a/h5c/vic-service/src/test/java/com/vmware/vic/test/VmQueryResultTest.java b/h5c/vic-service/src/test/java/com/vmware/vic/test/VmQueryResultTest.java new file mode 100644 index 000000000..b18ef89de --- /dev/null +++ b/h5c/vic-service/src/test/java/com/vmware/vic/test/VmQueryResultTest.java @@ -0,0 +1,100 @@ +/* + +Copyright 2017 VMware, Inc. All Rights Reserved. + +Licensed 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.vmware.vic.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.vmware.vic.model.ContainerVm; +import com.vmware.vic.model.ModelObject; +import com.vmware.vic.model.VirtualContainerHostVm; +import com.vmware.vic.model.VmQueryResult; +import com.vmware.vim25.ManagedEntityStatus; +import com.vmware.vim25.VirtualMachinePowerState; +import com.vmware.vise.data.query.PropertyValue; +import com.vmware.vise.data.query.ResultItem; +import com.vmware.vise.vim.data.VimObjectReferenceService; + +import static org.mockito.Mockito.*; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +public class VmQueryResultTest extends Common { + private VirtualContainerHostVm getMockedVchVm() { + return getMockedVirtualContainerHostVm( + "server3", + "id5", + "vch vm", + ManagedEntityStatus.GREEN, + VirtualMachinePowerState.POWERED_ON, + "ChFtuw=="); + } + + private ContainerVm getMockedContainerVm() { + return getMockedContainerVm( + "server5", + "id7", + "container vm", + ManagedEntityStatus.GREEN, + VirtualMachinePowerState.POWERED_ON, + "container name", + "nginx:alpine", + "8088:80/tcp"); + } + + /** + * Set up vApp where there is one vApp which has one VCH VM + and one Container VM in it. + */ + @Test + public void vmQueryResultForVch() { + Map vmsMap = new HashMap(); + vmsMap.put("id5", getMockedVchVm()); + + VimObjectReferenceService objRefService = mock(VimObjectReferenceService.class); + + // create an instance based on mocked data + VmQueryResult vmQueryResult = new VmQueryResult(vmsMap, objRefService); + assertNotNull(vmQueryResult); + assertEquals(vmQueryResult.getProperty("match"), 1); + } + + /** + * Set up vApp where there is one vApp which has one VCH VM + and one Container VM in it. + */ + @Test + public void vmQueryResultForContainer() { + Map vmsMap = new HashMap(); + vmsMap.put("id7", getMockedContainerVm()); + + VimObjectReferenceService objRefService = mock(VimObjectReferenceService.class); + + // create an instance based on mocked data + VmQueryResult vmQueryResult = new VmQueryResult(vmsMap, objRefService); + assertNotNull(vmQueryResult); + assertEquals(vmQueryResult.getProperty("match"), 1); + } +} diff --git a/h5c/vic-uia/.classpath b/h5c/vic-uia/.classpath new file mode 100644 index 000000000..2823cd217 --- /dev/null +++ b/h5c/vic-uia/.classpath @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic-uia/.project b/h5c/vic-uia/.project new file mode 100644 index 000000000..d1877c9c1 --- /dev/null +++ b/h5c/vic-uia/.project @@ -0,0 +1,29 @@ + + + vic-uia + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/h5c/vic-uia/.settings/org.eclipse.core.resources.prefs b/h5c/vic-uia/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..e9441bb12 --- /dev/null +++ b/h5c/vic-uia/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding/=UTF-8 diff --git a/h5c/vic-uia/.settings/org.eclipse.jdt.core.prefs b/h5c/vic-uia/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..443e08599 --- /dev/null +++ b/h5c/vic-uia/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/h5c/vic-uia/.settings/org.eclipse.m2e.core.prefs b/h5c/vic-uia/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/h5c/vic-uia/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/h5c/vic-uia/.settings/org.eclipse.wst.common.project.facet.core.xml b/h5c/vic-uia/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 000000000..bc0009a45 --- /dev/null +++ b/h5c/vic-uia/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,4 @@ + + + + diff --git a/h5c/vic-uia/build-java.xml b/h5c/vic-uia/build-java.xml new file mode 100644 index 000000000..f0e009c92 --- /dev/null +++ b/h5c/vic-uia/build-java.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic-uia/prepare-testbed.sh b/h5c/vic-uia/prepare-testbed.sh new file mode 100755 index 000000000..40e2beb8f --- /dev/null +++ b/h5c/vic-uia/prepare-testbed.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Copyright 2016-2017 VMware, Inc. All Rights Reserved. +# +# Licensed 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. +# + +MVN_REPO_LOCATION=$1 +SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]}) && pwd) +GOVC_VMINFO=`govc vm.info -u $VC_ADMIN_USERNAME@$VC_ADMIN_DOMAIN:$VC_ADMIN_PW@$VC_IP -k --vm.ip=$VCH_IP --json=true 2>&1` + +if [[ $(echo $GOVC_VMINFO | grep -i "no such VM") ]] ; then + echo "Error! Please make sure you provided a right IP for the VCH VM." + echo "If the IP is correct, turn on the VM first" + exit 1 +fi + +VCH_MOID=`echo $GOVC_VMINFO | jq .VirtualMachines[0].Self.Value | sed 's/\"//g'` +PROVIDERS_GENERATOR="$SCRIPT_DIR/support/runner-generateProviders" +GEN_ARGS="--admin.user=$VC_ADMIN_USERNAME --admin.domain=$VC_ADMIN_DOMAIN --admin.password=$VC_ADMIN_PW --selenium.ip=$SELENIUM_IP --selenium.port=$SELENIUM_PORT --browser.type=$BROWSER --workDir=$MVN_REPO_LOCATION --vc.ip=$VC_IP --host.ip=$HOST_IP --h5client.ip=$H5C_IP --h5client.port=$H5C_PORT --host.datastore_name=$HOST_DATASTORE_NAME --vch.name=$VCH_NAME --vch.moid=$VCH_MOID --container.name=$CONTAINER_NAME" + +# Note: reverting back to manual entry of test suites, as there doesn't seem to be an optimal way +# to preserve a certain order of listing test suites such that we can ensure the solid run and more consistent outcomes. +FOUND_TESTS=($(cd $SCRIPT_DIR/src/main/java && find com/vmware/vsphere/client/automation/test -name "*.java" | sed -e "s/\.java//g" -e "s|\/|\.|g")) +TESTS=( + "com.vmware.vsphere.client.automation.test.common.DoNothingExceptLoginTest" + "com.vmware.vsphere.client.automation.test.vch.VchPortletExistsTest" + "com.vmware.vsphere.client.automation.test.vch.VchPortletDisplaysInfoWhileOffTest" + "com.vmware.vsphere.client.automation.test.vch.VchPortletDisplaysInfoWhileOnTest" + "com.vmware.vsphere.client.automation.test.objectworkspace.SummaryTabTest" + "com.vmware.vsphere.client.automation.test.objectworkspace.EntryTest" + "com.vmware.vsphere.client.automation.test.container.ContainerPortletExistsTest" +) + +if [ ${#FOUND_TESTS[@]} != ${#TESTS[@]} ] ; then + echo "${#FOUND_TESTS[@]} != ${#TESTS[@]}" + echo Number of test cases doesn\'t match between the provided list and what were found in the file system! Please check the TESTS array and make sure you have listed test cases correctly + exit 1 +fi + +echo "Generating testbed providers..." +echo "==================================" + +CONCATENATED="" +for line in ${TESTS[@]} ; do + CONCATENATED="$CONCATENATED $line" +done + +$PROVIDERS_GENERATOR $GEN_ARGS $VC_ADMIN_PW_ARG $CONCATENATED +echo Added the following test cases: ${TESTS[*]} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/MaximizeWindowStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/MaximizeWindowStep.java new file mode 100644 index 000000000..d0e1cc35f --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/MaximizeWindowStep.java @@ -0,0 +1,27 @@ +package com.vmware.vsphere.client.automation.step.common; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +import java.awt.Toolkit; + +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; + +public class MaximizeWindowStep extends UiTestStep { + private static final int TARGET_WINDOW_WIDTH = 1280; + private static final int TARGET_WINDOW_HEIGHT = 900; + + @Override + public String getDescription() { + return String.format("%s", "Sets the browser window to a certain size"); + } + + @Override + public void execute() throws Throwable { + Dimension screenSize = new Dimension( + TARGET_WINDOW_WIDTH, TARGET_WINDOW_HEIGHT); + getDriver().manage().window().setSize(screenSize); + getDriver().manage().window().maximize(); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/VerifyPortletVisibilityStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/VerifyPortletVisibilityStep.java new file mode 100644 index 000000000..48401d2b0 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/VerifyPortletVisibilityStep.java @@ -0,0 +1,56 @@ +package com.vmware.vsphere.client.automation.step.common; + +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; + +import com.vmware.automation.core.annotation.UsesSpec; +import com.vmware.vsphere.client.automation.spec.PortletSpec; +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifyPortletVisibilityStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private final int WAIT_TIMEOUT = 5; + private static final String PORTLET_TITLEBAR_TEXT_SELECTOR = "//div[contains(@class, 'vx-portlet')]/div[contains(@class, 'portlet-titlebar')]/span[@title=\"%s\"]"; + private String _labelToNavigate; + private boolean _isExpectedToExist = true; + + @UsesSpec + private PortletSpec _pSpec; + + public VerifyPortletVisibilityStep() { + super(); + } + + public VerifyPortletVisibilityStep(boolean isExpectedExist) { + super(); + _isExpectedToExist = isExpectedExist; + } + + @Override + public String getDescription() { + return String.format("Verifies if VCH portlet exists and displays proper information"); + } + + @Override + public void execute() throws Throwable { + waitForAngularTestability(); + _labelToNavigate = _pSpec.getTitle(); + verify.fatal(hasPortlet(), _isExpectedToExist, + "The '" + _labelToNavigate + "' portlet does" + + (_isExpectedToExist ? "" : " not") + " exist"); + } + + private boolean hasPortlet() { + boolean existing = true; + String labelToNavigate = String.format(PORTLET_TITLEBAR_TEXT_SELECTOR, _labelToNavigate); + + try { + element(By.xpath(labelToNavigate)).waitToBeVisible(WAIT_TIMEOUT); + } catch (TimeoutException e) { + existing = false; + } + + return existing; + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/VicCommon.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/VicCommon.java new file mode 100644 index 000000000..4b8f875bc --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/common/VicCommon.java @@ -0,0 +1,16 @@ +package com.vmware.vsphere.client.automation.step.common; + +import com.vmware.vsphere.client.automation.spec.PortletSpec; + +public class VicCommon { + public static final String VCH_PORTLET_TITLE = "Virtual Container Host"; + public static final String CONTAINER_PORTLET_TITLE = "Container"; + + public static PortletSpec buildPortletSpec(String name) { + PortletSpec pSpec = new PortletSpec(); + pSpec.setTitle(name); + pSpec.setIsExpectedToExist(true); + + return pSpec; + } +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/container/ContainerPortletHasProperLocaleStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/container/ContainerPortletHasProperLocaleStep.java new file mode 100644 index 000000000..3a026b657 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/container/ContainerPortletHasProperLocaleStep.java @@ -0,0 +1,95 @@ +package com.vmware.vsphere.client.automation.step.container; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import com.vmware.vsphere.client.automation.core.annotation.View; + +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; +import com.vmware.vsphere.client.automation.view.sdk.GlobalView; + +public class ContainerPortletHasProperLocaleStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String IFRAME_SELECTOR = "//iframe[contains(@src,\"/ui/vic\")]"; + private static final String CONTAINER_PORTLET_TITLE_SELECTOR_FORMAT = "//span[contains(@title, \"%s\")]"; + private static final String NAME_TH_SELECTOR = "//vic-container-portlet/table/tbody/tr[1]/th"; + private static final String IMAGE_TH_SELECTOR = "//vic-container-portlet/table/tbody/tr[2]/th"; + private static final String PORTMAPPING_TH_SELECTOR = "//vic-container-portlet/table/tbody/tr[3]/th"; + private static final String LOCALE_PATH = "locale/%s/com_vmware_vic.properties"; + private static String BROWSER_LOCALE; + + @View + GlobalView globalView; + + private Properties _props = new Properties(); + + private void getLocale() throws IOException { + final JavascriptExecutor jsExecutor = ((JavascriptExecutor) getDriver()); + BROWSER_LOCALE = (String) jsExecutor.executeScript("return WEB_PLATFORM.getLocale()"); + // in case locale is not defined in browser URL default it to en_US + if (BROWSER_LOCALE == null) { + BROWSER_LOCALE = "en_US"; + } + System.out.println("Browser locale is: " + BROWSER_LOCALE); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try (InputStream resourceStream = loader.getResourceAsStream( + String.format(LOCALE_PATH, BROWSER_LOCALE))) { + _props.load(resourceStream); + } + } + + private String getLocalizedValue(String key) throws IOException { + if (_props == null) { + + } + return _props.getProperty(key); + } + + private void switchToIframe() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By iframe = By.xpath(IFRAME_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(iframe)); + getDriver().switchTo().frame(getDriver().findElement(iframe)); + } + + @Override + public String getDescription() { + return String.format("Verifies if Container portlet shows information in correct locale"); + } + + @Override + public void execute() throws Throwable { + getLocale(); + final String localizedContainerPortletTitle = getLocalizedValue("container.label"); + final String localizedNameLabel = getLocalizedValue("container.name.label"); + final String localizedImageLabel = getLocalizedValue("container.image.label"); + final String localizedPortmappingLabel = getLocalizedValue("container.portmapping.label"); + final String containerPortletTitleSelector = String.format(CONTAINER_PORTLET_TITLE_SELECTOR_FORMAT, localizedContainerPortletTitle); + + verify.fatal(getDriver().findElement(By.xpath(containerPortletTitleSelector)).getText(), + localizedContainerPortletTitle, + String.format("Container portlet label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedContainerPortletTitle)); + switchToIframe(); + + verify.safely(getDriver().findElement(By.xpath(NAME_TH_SELECTOR)).getText(), + localizedNameLabel, + String.format("Container name label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedNameLabel)); + verify.safely(getDriver().findElement(By.xpath(IMAGE_TH_SELECTOR)).getText(), + localizedImageLabel, + String.format("Container image label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedImageLabel)); + try { + verify.safely(getDriver().findElement(By.xpath(PORTMAPPING_TH_SELECTOR)).getText(), + localizedPortmappingLabel, + String.format("Container port mapping label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedPortmappingLabel)); + } catch (Exception e) { + System.out.println("This container does not have any port mapping information"); + } + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenVicWorkspaceLandingPageStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenVicWorkspaceLandingPageStep.java new file mode 100644 index 000000000..83c79e5d6 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenVicWorkspaceLandingPageStep.java @@ -0,0 +1,25 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class OpenVicWorkspaceLandingPageStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String XPATH_DATAGRID_VIC_SELECTOR = + "//div[@vx-extension-view-id=\"com.vmware.vic.objectView.list\"]" + + "//div[contains(@class, \"k-grid-content\")][1]" + + "//td[@role=\"gridcell\"][1]//a"; + + @Override + public String getDescription() { + return String.format("%s", "Clicks 'vSphere Integrated Containers' in the data grid to naviagate to the landing page"); + } + + @Override + public void execute() throws Throwable { + element(By.xpath(XPATH_DATAGRID_VIC_SELECTOR)) + .waitToBeClickable().click(); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenWorkspaceByGIListStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenWorkspaceByGIListStep.java new file mode 100644 index 000000000..46acaab56 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenWorkspaceByGIListStep.java @@ -0,0 +1,22 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class OpenWorkspaceByGIListStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String XPATH_GI_VIC_SELECTOR = + "//span[contains(@title, \"vSphere Integrated Containers\")]"; + + @Override + public String getDescription() { + return String.format("%s", "Clicks the 'vSphere Integrated Containers' in the Global Invetory List"); + } + + @Override + public void execute() throws Throwable { + element(By.xpath(XPATH_GI_VIC_SELECTOR)).waitToBeClickable().click(); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenWorkspaceByHomeShortcutStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenWorkspaceByHomeShortcutStep.java new file mode 100644 index 000000000..e220c54c9 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/OpenWorkspaceByHomeShortcutStep.java @@ -0,0 +1,22 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class OpenWorkspaceByHomeShortcutStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String XPATH_VIC_ICON_CSS_SELECTOR = + "//div[contains(text(), \"vSphere Integrated Containers\")]"; + + @Override + public String getDescription() { + return String.format("%s", "Clicks the 'vSphere Integrated Containers' home shortcut"); + } + + @Override + public void execute() throws Throwable { + element(By.xpath(XPATH_VIC_ICON_CSS_SELECTOR)).waitToBeClickable().click(); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifySuccessfulEntryStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifySuccessfulEntryStep.java new file mode 100644 index 000000000..8dcaf1352 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifySuccessfulEntryStep.java @@ -0,0 +1,33 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifySuccessfulEntryStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String XPATH_VIC_HEADER_SELECTOR = + "//div/span[text()=\"vSphere Integrated Containers\"]"; + + @Override + public String getDescription() { + return String.format("%s", "Verifies if the landing page of VIC Workspace is shown"); + } + + @Override + public void execute() throws Throwable { + verify.fatalTrue(hasVicHeaderText(), + String.format("Header should exist for VIC")); + } + + private boolean hasVicHeaderText() { + boolean result = true; + try { + element(By.xpath(XPATH_VIC_HEADER_SELECTOR)).waitToBeVisible(); + } catch (TimeoutException e) { + return false; + } + return result; + } +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyUiVersionStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyUiVersionStep.java new file mode 100644 index 000000000..f10191720 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyUiVersionStep.java @@ -0,0 +1,68 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifyUiVersionStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String IFRAME_SELECTOR = "//iframe[contains(@src,\"/ui/vic\")]"; + private static final String CSS_UI_VERSION_SELECTOR = + "ul.summary-items-list li#version span.summary-value"; + + @Override + public String getDescription() { + return String.format("%s", "Verifies UI version of VIC UI in the Summary page"); + } + + private void switchToIframe() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By iframe = By.xpath(IFRAME_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(iframe)); + getDriver().switchTo().frame(getDriver().findElement(iframe)); + } + + private void switchToParentFrame() { + getDriver().switchTo().parentFrame(); + } + + private boolean checkIfVersionShowsCorrectly() throws InterruptedException { + By uiVersionElement = By.cssSelector(CSS_UI_VERSION_SELECTOR); + int maxTries= 10; + int tries = 0; + + try { + for (int i = 0; i < maxTries; i++) { + String value = getDriver().findElement(uiVersionElement).getText(); + tries++; + if (!value.isEmpty()) { + System.out.println("try " + tries + ": found " + value); + return true; + } + + if (tries > 10) { + System.out.println("maximum tries reached"); + return false; + } + + System.out.println("try " + tries + ": string not found yet. retrying..."); + Thread.sleep(1000); + } + } catch (InterruptedException e) { + return false; + } + return false; + } + + @Override + public void execute() throws Throwable { + switchToIframe(); + verify.safelyTrue(checkIfVersionShowsCorrectly(), + String.format("%s", "Version string should not be empty") + ); + switchToParentFrame(); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVchNumberStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVchNumberStep.java new file mode 100644 index 000000000..06e98274e --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVchNumberStep.java @@ -0,0 +1,43 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifyVchNumberStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final long ELEMENT_TIMEOUT = 30; + private static final String IFRAME_SELECTOR = "//iframe[contains(@src,\"/ui/vic\")]"; + private static final String CSS_VCH_LEN_SELECTOR = + "ul.summary-items-list li#vch_len span.summary-value"; + + @Override + public String getDescription() { + return String.format("%s", "Verifies # of Virtual Container Hosts in the Summary page"); + } + + private void switchToIframe() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By iframe = By.xpath(IFRAME_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(iframe)); + getDriver().switchTo().frame(getDriver().findElement(iframe)); + } + + private void switchToParentFrame() { + getDriver().switchTo().parentFrame(); + } + + @Override + public void execute() throws Throwable { + switchToIframe(); + verify.safely( + getDriver().findElement(By.cssSelector(CSS_VCH_LEN_SELECTOR)) + .getText(), + "1", + String.format("%s", "VCH length should be 1")); + switchToParentFrame(); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVendorStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVendorStep.java new file mode 100644 index 000000000..7ff5ac2ab --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVendorStep.java @@ -0,0 +1,41 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import org.openqa.selenium.By; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifyVendorStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String IFRAME_SELECTOR = "//iframe[contains(@src,\"/ui/vic\")]"; + private static final String CSS_VENDOR_SELECTOR = + "ul.summary-items-list li#vendor span.summary-value"; + + @Override + public String getDescription() { + return String.format("%s", "Verifies if vendor is set properly"); + } + + private void switchToIframe() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By iframe = By.xpath(IFRAME_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(iframe)); + getDriver().switchTo().frame(getDriver().findElement(iframe)); + } + + private void switchToParentFrame() { + getDriver().switchTo().parentFrame(); + } + + @Override + public void execute() throws Throwable { + switchToIframe(); + verify.safely( + getDriver().findElement(By.cssSelector(CSS_VENDOR_SELECTOR)) + .getText(), + "VMware", + String.format("%s", "Vendor should be 'VMware'")); + switchToParentFrame(); + } +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVmNumbersStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVmNumbersStep.java new file mode 100644 index 000000000..80cdba572 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/objectworkspace/VerifyVmNumbersStep.java @@ -0,0 +1,43 @@ +package com.vmware.vsphere.client.automation.step.objectworkspace; + +import com.vmware.vsphere.client.automation.core.wrapper.By; +import com.vmware.vsphere.client.automation.core.wrapper.ElementState; +import com.vmware.vsphere.client.automation.core.wrapper.IElement; +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifyVmNumbersStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String XPATH_DATAGRID_VCH_LEN_SELECTOR = + "//div[@vx-extension-view-id=\"com.vmware.vic.objectView.list\"]" + + "//div[contains(@class, \"k-grid-content\")][1]" + + "//td[@role=\"gridcell\"][2]"; + private static final String XPATH_DATAGRID_CONTAINER_LEN_SELECTOR = + "//div[@vx-extension-view-id=\"com.vmware.vic.objectView.list\"]" + + "//div[contains(@class, \"k-grid-content\")][1]" + + "//td[@role=\"gridcell\"][3]"; + + @Override + public String getDescription() { + return String.format("%s", "Verifies if Datagrid shows correctly that there are one VCH and one Container"); + } + + @Override + public void execute() throws Throwable { + IElement elementVchLen = UI().element(By.xpath(XPATH_DATAGRID_VCH_LEN_SELECTOR)); + elementVchLen.waitToBe(ElementState.visible); + verify.safely( + elementVchLen.getText(), + "1", + String.format("%s", "# of VCHs should strictly be 1. If multiple VCHs show up," + + "remove manually installed VCHs")); + + IElement elementContainersLen = UI().element(By.xpath(XPATH_DATAGRID_CONTAINER_LEN_SELECTOR)); + elementContainersLen.waitToBe(ElementState.visible); + verify.fatal( + elementContainersLen.getText(), + "1", + String.format("%s", "# of Containers should strictly be 1. " + + "If multiple VCHs show up, remove manually installed Containers")); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/vch/VchPortletHasProperLocaleStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/vch/VchPortletHasProperLocaleStep.java new file mode 100644 index 000000000..bb9a16062 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/vch/VchPortletHasProperLocaleStep.java @@ -0,0 +1,78 @@ +package com.vmware.vsphere.client.automation.step.vch; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VchPortletHasProperLocaleStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String VCH_PORTLET_TITLE_SELECTOR_FORMAT = "//span[contains(@title, \"%s\")]"; + private static final String IFRAME_SELECTOR = "//iframe[contains(@src,\"/ui/vic\")]"; + private static final String DOCKER_API_ENDPOINT_TH_SELECTOR = "//vic-vch-portlet/table/tbody/tr[1]/th"; + private static final String VCH_ADMIN_PORTAL_TH_SELECTOR = "//vic-vch-portlet/table/tbody/tr[2]/th"; + private static final String LOCALE_PATH = "locale/%s/com_vmware_vic.properties"; + private static String BROWSER_LOCALE; + + private Properties _props = new Properties(); + + private void getLocale() throws IOException { + final JavascriptExecutor jsExecutor = ((JavascriptExecutor) getDriver()); + BROWSER_LOCALE = (String) jsExecutor.executeScript("return WEB_PLATFORM.getLocale()"); + // in case locale is not defined in browser URL default it to en_US + if (BROWSER_LOCALE == null) { + BROWSER_LOCALE = "en_US"; + } + System.out.println("Browser locale is: " + BROWSER_LOCALE); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try (InputStream resourceStream = loader.getResourceAsStream( + String.format(LOCALE_PATH, BROWSER_LOCALE))) { + _props.load(resourceStream); + } + } + + private String getLocalizedValue(String key) throws IOException { + if (_props == null) { + + } + return _props.getProperty(key); + } + + private void switchToIframe() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By iframe = By.xpath(IFRAME_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(iframe)); + getDriver().switchTo().frame(getDriver().findElement(iframe)); + } + + @Override + public String getDescription() { + return String.format("Verifies if VCH portlet shows information in correct locale"); + } + + @Override + public void execute() throws Throwable { + getLocale(); + final String localizedVchPortletTitle = getLocalizedValue("vch.label"); + final String localizedDockerApiEndpointLabel = getLocalizedValue("vch.dockerApiEndpoint.label"); + final String localizedVchAdminPortalLabel = getLocalizedValue("vch.vchAdminPortal.label"); + final String vchPortletTitleSelector = String.format(VCH_PORTLET_TITLE_SELECTOR_FORMAT, localizedVchPortletTitle); + verify.fatal(getDriver().findElement(By.xpath(vchPortletTitleSelector)).getText(), + localizedVchPortletTitle, + String.format("VCH portlet label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedVchPortletTitle)); + switchToIframe(); + verify.fatal(getDriver().findElement(By.xpath(DOCKER_API_ENDPOINT_TH_SELECTOR)).getText(), + localizedDockerApiEndpointLabel, + String.format("Docker API endpoint label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedDockerApiEndpointLabel)); + verify.fatal(getDriver().findElement(By.xpath(VCH_ADMIN_PORTAL_TH_SELECTOR)).getText(), + localizedVchAdminPortalLabel, + String.format("VCH Admin portal label for locale \"%s\" should be \"%s\"", BROWSER_LOCALE, localizedVchAdminPortalLabel)); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/vch/VerifyVchPortletContentStep.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/vch/VerifyVchPortletContentStep.java new file mode 100644 index 000000000..365a369f0 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/step/vch/VerifyVchPortletContentStep.java @@ -0,0 +1,118 @@ +package com.vmware.vsphere.client.automation.step.vch; + +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.vmware.automation.core.annotation.UsesSpec; +import com.vmware.vim.binding.vim.VirtualMachine.PowerState; +import com.vmware.vsphere.client.automation.core.spec.SingleValueSpec; +import com.vmware.vsphere.client.automation.step.ui.UiTestStep; + +public class VerifyVchPortletContentStep extends UiTestStep { + private static final long serialVersionUID = 1L; + private static final String ATTRIBUTE_PLACEHOLDER_VAL = "-"; + private static final String IFRAME_SELECTOR = "//iframe[contains(@src,\"/ui/vic\")]"; + private static final String DOCKER_API_ENDPOINT_TD_SELECTOR = "//vic-vch-portlet/table/tbody/tr[1]/td"; + private static final String VCH_ADMIN_PORTAL_TD_SELECTOR = "//vic-vch-portlet/table/tbody/tr[2]/td"; + private final String REFRESH_BUTTON_CSS_SELECTOR = "span[class~='vui-icon-refresh']"; + + private static String dockerApiEndpointActualVal; + private static String vchAdminPortalActualVal; + + @UsesSpec + private SingleValueSpec powerStateSpec; + + @Override + public String getDescription() { + return String.format("Verifies the VCH portlet content when VM is: %s", powerStateSpec.getValue()); + } + + @Override + public void execute() throws Throwable { + verify.fatal(tdsExist(), Boolean.TRUE, "The VCH portlet should have table rows each for Docker API endpoint and VCH Admin Portal"); + verify.fatal(waitIfVchIsInExpectedState(), Boolean.TRUE, String.format("Wait until VCH VM is ready after \"%s\" operation", powerStateSpec.getValue())); + verify.fatal(dockerApiEndpointActualVal.equals(ATTRIBUTE_PLACEHOLDER_VAL), + (powerStateSpec.getValue() == PowerState.poweredOff ? Boolean.TRUE : Boolean.FALSE), + "Docker API Endpoint value should " + + (powerStateSpec.getValue() == PowerState.poweredOff ? "be " : "not be ") + + ATTRIBUTE_PLACEHOLDER_VAL + ". Actual value is: " + dockerApiEndpointActualVal); + verify.fatal(vchAdminPortalActualVal.equals(ATTRIBUTE_PLACEHOLDER_VAL), + (powerStateSpec.getValue() == PowerState.poweredOff ? Boolean.TRUE : Boolean.FALSE), + "VCH Admin Portal value should " + + (powerStateSpec.getValue() == PowerState.poweredOff ? "be " : "not be ") + + ATTRIBUTE_PLACEHOLDER_VAL + ". Actual value is: " + vchAdminPortalActualVal); + } + + private boolean waitIfVchIsInExpectedState() throws InterruptedException { + boolean vchInExpectedState = false; + int retryMax = 30; + int tried = 0; + try { + while(!vchInExpectedState) { + retrieveVchInformation(); + + if (powerStateSpec.getValue() == PowerState.poweredOn) { + vchInExpectedState = !(dockerApiEndpointActualVal.equals(ATTRIBUTE_PLACEHOLDER_VAL)) && !(vchAdminPortalActualVal.equals(ATTRIBUTE_PLACEHOLDER_VAL)); + } else { + vchInExpectedState = dockerApiEndpointActualVal.equals(ATTRIBUTE_PLACEHOLDER_VAL) && vchAdminPortalActualVal.equals(ATTRIBUTE_PLACEHOLDER_VAL); + } + + if (vchInExpectedState) { + break; + } else { + switchToParentFrame(); + refreshUi(); + switchToIframe(); + tried++; + if (tried > retryMax) { + throw new TimeoutException("Retry max reached while refreshing the Summary view"); + } + Thread.sleep(2000); + } + } + } catch (TimeoutException e) { + vchInExpectedState = false; + } catch (InterruptedException e) { + vchInExpectedState = false; + } + return vchInExpectedState; + } + + private void switchToIframe() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By iframe = By.xpath(IFRAME_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(iframe)); + getDriver().switchTo().frame(getDriver().findElement(iframe)); + } + + private void switchToParentFrame() { + getDriver().switchTo().parentFrame(); + } + + private void retrieveVchInformation() { + dockerApiEndpointActualVal = getDriver().findElement(By.xpath(DOCKER_API_ENDPOINT_TD_SELECTOR)).getText(); + vchAdminPortalActualVal = getDriver().findElement(By.xpath(VCH_ADMIN_PORTAL_TD_SELECTOR)).getText(); + } + + private void refreshUi() { + WebDriverWait wait = new WebDriverWait(getDriver(), 30); + By vuiRefresh = By.cssSelector(REFRESH_BUTTON_CSS_SELECTOR); + wait.until(ExpectedConditions.visibilityOfElementLocated(vuiRefresh)); + getDriver().findElement(vuiRefresh).click(); + } + + private boolean tdsExist() { + boolean tdsFound = true; + try { + waitForAngularTestability(); + switchToIframe(); + retrieveVchInformation(); + } catch (TimeoutException e) { + tdsFound = false; + } + return tdsFound; + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/common/DoNothingExceptLoginTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/common/DoNothingExceptLoginTest.java new file mode 100644 index 000000000..52f5fcd82 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/common/DoNothingExceptLoginTest.java @@ -0,0 +1,39 @@ +package com.vmware.vsphere.client.automation.test.common; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vsphere.client.automation.component.navigation.HomeNavigator; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +/** + * Do Nothing Except Login Test: + * The purpose of this test is to ensure resources data such as + * locale are loaded and referenced properly by the actual test cases + * by simply logging into the H5 Client and then logging out + */ +public class DoNothingExceptLoginTest extends WebClientTest { + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + @Override + public String getDescription() { + return String.format("%s", "Navigates to the home navigator"); + } + + @Override + protected void addTestSteps(StepComposition steps) { + steps.add(new HomeNavigator()); + } +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/container/ContainerPortletExistsTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/container/ContainerPortletExistsTest.java new file mode 100644 index 000000000..4ba5dbb15 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/container/ContainerPortletExistsTest.java @@ -0,0 +1,74 @@ +package com.vmware.vsphere.client.automation.test.container; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.workflow.IWorkflowSpec; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vsphere.client.automation.component.navigation.ObjectNavigator; +import com.vmware.vsphere.client.automation.component.navigation.TabNavigator; +import com.vmware.vsphere.client.automation.constants.L10NVmConst; +import com.vmware.vsphere.client.automation.spec.PortletSpec; +import com.vmware.vsphere.client.automation.step.common.MaximizeWindowStep; +import com.vmware.vsphere.client.automation.step.common.VerifyPortletVisibilityStep; +import com.vmware.vsphere.client.automation.step.common.VicCommon; +import com.vmware.vsphere.client.automation.step.container.ContainerPortletHasProperLocaleStep; +import com.vmware.vsphere.client.automation.step.ui.VerifyLoadingIsDoneStep; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +/** + * TODO: update VicTestbed and this test case such that it has image name and portmapping information that can be tested in HSUIA + * + * Container Portlet Exists Test: + * 1. Navigate to Container VM + * 2. Verify portlet 'Container' exists + * 3. Verify if portlet displays information in proper locale +*/ +public class ContainerPortletExistsTest extends WebClientTest { + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + private static String CONTAINER_VM_TITLE; + private PortletSpec _containerPortletSpec; + + @Override + protected void addSpecsToWorkflow(IWorkflowSpec specsToAdd) { + super.addSpecsToWorkflow(specsToAdd); + CONTAINER_VM_TITLE = _vicTestbed.containerVmSpec.getName(); + _containerPortletSpec = VicCommon.buildPortletSpec(VicCommon.CONTAINER_PORTLET_TITLE); + specsToAdd.addSpec(_containerPortletSpec); + } + + @Override + public String getDescription() { + return String.format("Checking if Container portlet exists"); + } + + @Override + protected void addSetupSteps(StepComposition steps) { + steps.add(new MaximizeWindowStep()); + } + + @Override + protected void addTestSteps(StepComposition steps) { + steps + .add(new ObjectNavigator(L10NVmConst.Navigator.VC_INVENTORY_LISTS)) + .add(new ObjectNavigator(L10NVmConst.Navigator.VMS)) + .add(new ObjectNavigator(CONTAINER_VM_TITLE)) + .add(new TabNavigator(L10NVmConst.Tab.SUMMARY)) + .add(new VerifyLoadingIsDoneStep()) + .add(new VerifyPortletVisibilityStep(), _containerPortletSpec) + .add(new ContainerPortletHasProperLocaleStep()); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/objectworkspace/EntryTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/objectworkspace/EntryTest.java new file mode 100644 index 000000000..a6025e187 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/objectworkspace/EntryTest.java @@ -0,0 +1,64 @@ +package com.vmware.vsphere.client.automation.test.objectworkspace; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vsphere.client.automation.component.navigation.HomeNavigator; +import com.vmware.vsphere.client.automation.component.navigation.ObjectNavigator; +import com.vmware.vsphere.client.automation.constants.L10NVmConst; +import com.vmware.vsphere.client.automation.step.common.MaximizeWindowStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.OpenWorkspaceByGIListStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.OpenWorkspaceByHomeShortcutStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.VerifySuccessfulEntryStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.VerifyVmNumbersStep; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +/** + * Entry Test: + * 1. Navigate to the home screen + * 2. Open VIC Workspace by clicking the shortcut + * 3. Verify if the navigation was successful + * 4. Navigate to the Global Inventory List + * 5. Open VIC Workspace by clicking vSphere Integrated Containers in the inventory list + * 6. Verify if the navigation was successful + * 7. Check if data grid displays the right number of VCHs and Containers +*/ +public class EntryTest extends WebClientTest { + + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + @Override + public String getDescription() { + return String.format("%s", "Checks if VIC Object Workspace can be accessed"); + } + + @Override + protected void addSetupSteps(StepComposition steps) { + steps.add(new MaximizeWindowStep()); + } + + @Override + protected void addTestSteps(StepComposition steps) { + steps.add(new HomeNavigator()); + steps.add(new OpenWorkspaceByHomeShortcutStep()); + steps.add(new VerifySuccessfulEntryStep()); + steps.add(new HomeNavigator()); + steps.add(new ObjectNavigator(L10NVmConst.Navigator.VC_INVENTORY_LISTS)); + steps.add(new OpenWorkspaceByGIListStep()); + steps.add(new VerifySuccessfulEntryStep()); + steps.add(new VerifyVmNumbersStep()); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/objectworkspace/SummaryTabTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/objectworkspace/SummaryTabTest.java new file mode 100644 index 000000000..731e33203 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/objectworkspace/SummaryTabTest.java @@ -0,0 +1,52 @@ +package com.vmware.vsphere.client.automation.test.objectworkspace; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vsphere.client.automation.component.navigation.HomeNavigator; +import com.vmware.vsphere.client.automation.step.common.MaximizeWindowStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.OpenVicWorkspaceLandingPageStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.OpenWorkspaceByHomeShortcutStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.VerifySuccessfulEntryStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.VerifyUiVersionStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.VerifyVchNumberStep; +import com.vmware.vsphere.client.automation.step.objectworkspace.VerifyVendorStep; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +public class SummaryTabTest extends WebClientTest { + + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + @Override + public String getDescription() { + return String.format("%s", "Tests the Summary tab of VIC Workspace functionality"); + } + + @Override + protected void addSetupSteps(StepComposition steps) { + steps.add(new MaximizeWindowStep()); + } + @Override + protected void addTestSteps(StepComposition steps) { + steps.add(new HomeNavigator()); + steps.add(new OpenWorkspaceByHomeShortcutStep()); + steps.add(new VerifySuccessfulEntryStep()); + steps.add(new OpenVicWorkspaceLandingPageStep()); + steps.add(new VerifyUiVersionStep()); + steps.add(new VerifyVchNumberStep()); + steps.add(new VerifyVendorStep()); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletDisplaysInfoWhileOffTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletDisplaysInfoWhileOffTest.java new file mode 100644 index 000000000..550872071 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletDisplaysInfoWhileOffTest.java @@ -0,0 +1,98 @@ +package com.vmware.vsphere.client.automation.test.vch; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.workflow.IWorkflowSpec; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vim.binding.vim.VirtualMachine.PowerState; +import com.vmware.vsphere.client.automation.component.navigation.ObjectNavigator; +import com.vmware.vsphere.client.automation.component.navigation.TabNavigator; +import com.vmware.vsphere.client.automation.component.tasks.RecentTaskFilterSpec; +import com.vmware.vsphere.client.automation.component.tasks.RecentTaskStatus; +import com.vmware.vsphere.client.automation.constants.L10NVmConst; +import com.vmware.vsphere.client.automation.core.spec.SingleValueSpec; +import com.vmware.vsphere.client.automation.core.spec.VmSpec; +import com.vmware.vsphere.client.automation.spec.PortletSpec; +import com.vmware.vsphere.client.automation.step.common.MaximizeWindowStep; +import com.vmware.vsphere.client.automation.step.common.VerifyPortletVisibilityStep; +import com.vmware.vsphere.client.automation.step.common.VicCommon; +import com.vmware.vsphere.client.automation.step.srv.vm.SetVmPowerStateByApiStep; +import com.vmware.vsphere.client.automation.step.ui.ClarityAlertClickYesStep; +import com.vmware.vsphere.client.automation.step.ui.InvokeActionMenuItemStep; +import com.vmware.vsphere.client.automation.step.ui.VerifyRecentTaskStep; +import com.vmware.vsphere.client.automation.step.vch.VerifyVchPortletContentStep; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +/** + * VCH Portlet Displays Info While Off Test: + * 1. Make sure Virtual Container Host VM is at poweredOn state + * 2. Navigate to VM and open Summary tab + * 3. Turn off VM and wait the powerOff task is done + * 4. Verify portlet 'Virtual Container Host' exists + * 5. Verify portlet 'Virtual Container Host' displays the placeholder value for Docker API endpoint + * 6. Verify portlet 'Virtual Container Host' displays the placeholder value for VCH Admin portal +*/ +public class VchPortletDisplaysInfoWhileOffTest extends WebClientTest { + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + private VmSpec _vchVmSpec; + private PortletSpec _vchPortletSpec; + private SingleValueSpec _poweredOffSpec; + private RecentTaskFilterSpec _powerOffVMTaskSpec; + + @Override + public String getDescription() { + return String.format("Checks if VCH portlet displays placeholder values while VM is off"); + } + + @Override + protected void addSpecsToWorkflow(IWorkflowSpec specsToAdd) { + super.addSpecsToWorkflow(specsToAdd); + + _vchVmSpec = _vicTestbed.vchVmSpec; + _vchPortletSpec = VicCommon.buildPortletSpec(VicCommon.VCH_PORTLET_TITLE); + + _poweredOffSpec = new SingleValueSpec(PowerState.poweredOff); + _powerOffVMTaskSpec = new RecentTaskFilterSpec( + L10NVmConst.Task.POWER_OFF_VM, + _vchVmSpec.getName(), + RecentTaskStatus.COMPLETED + ); + + specsToAdd.addSpec(_vchVmSpec, _vchPortletSpec, _poweredOffSpec, _powerOffVMTaskSpec); + } + + @Override + protected void addSetupSteps(StepComposition steps) { + steps.add(new SetVmPowerStateByApiStep(PowerState.poweredOn), _vchVmSpec); + steps.add(new MaximizeWindowStep()); + } + + @Override + protected void addTestSteps(StepComposition steps) { + steps + .add(new MaximizeWindowStep()) + .add(new ObjectNavigator(L10NVmConst.Navigator.VC_INVENTORY_LISTS)) + .add(new ObjectNavigator(L10NVmConst.Navigator.VMS)) + .add(new ObjectNavigator(_vicTestbed.vchVmSpec.getName())) + .add(new TabNavigator(L10NVmConst.Tab.SUMMARY)) + .add(new InvokeActionMenuItemStep(L10NVmConst.Action.POWER, L10NVmConst.Action.POWER_OFF)) + .add(new ClarityAlertClickYesStep()) + .add(new VerifyRecentTaskStep()) + .add(new VerifyPortletVisibilityStep(), _vchVmSpec) + .add(new VerifyVchPortletContentStep(), _poweredOffSpec); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletDisplaysInfoWhileOnTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletDisplaysInfoWhileOnTest.java new file mode 100644 index 000000000..a8d7ee6d4 --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletDisplaysInfoWhileOnTest.java @@ -0,0 +1,94 @@ +package com.vmware.vsphere.client.automation.test.vch; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.workflow.IWorkflowSpec; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vim.binding.vim.VirtualMachine.PowerState; +import com.vmware.vsphere.client.automation.component.navigation.ObjectNavigator; +import com.vmware.vsphere.client.automation.component.navigation.TabNavigator; +import com.vmware.vsphere.client.automation.component.tasks.RecentTaskFilterSpec; +import com.vmware.vsphere.client.automation.component.tasks.RecentTaskStatus; +import com.vmware.vsphere.client.automation.constants.L10NVmConst; +import com.vmware.vsphere.client.automation.core.spec.SingleValueSpec; +import com.vmware.vsphere.client.automation.core.spec.VmSpec; +import com.vmware.vsphere.client.automation.spec.PortletSpec; +import com.vmware.vsphere.client.automation.step.common.MaximizeWindowStep; +import com.vmware.vsphere.client.automation.step.common.VerifyPortletVisibilityStep; +import com.vmware.vsphere.client.automation.step.common.VicCommon; +import com.vmware.vsphere.client.automation.step.srv.vm.SetVmPowerStateByApiStep; +import com.vmware.vsphere.client.automation.step.ui.InvokeActionMenuItemStep; +import com.vmware.vsphere.client.automation.step.ui.VerifyRecentTaskStep; +import com.vmware.vsphere.client.automation.step.vch.VerifyVchPortletContentStep; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +/** + * VCH Portlet Displays Info While On Test: + * 1. Make sure Virtual Container Host VM is at poweredOff state + * 2. Navigate to VM and open Summary tab + * 3. Turn on VM and wait the powerOn task is done + * 4. Verify portlet 'Virtual Container Host' exists + * 5. Verify portlet 'Virtual Container Host' displays a correct value for Docker API endpoint + * 6. Verify portlet 'Virtual Container Host' displays a correct value for VCH Admin portal + */ +public class VchPortletDisplaysInfoWhileOnTest extends WebClientTest { + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + private SingleValueSpec _poweredOnSpec = new SingleValueSpec(PowerState.poweredOn); + private VmSpec _vchVmSpec; + private PortletSpec _vchPortletSpec; + private RecentTaskFilterSpec _powerOnVMTaskSpec; + + @Override + public String getDescription() { + return String.format("Checks if VCH portlet displays valid values while VM is on"); + } + + @Override + protected void addSpecsToWorkflow(IWorkflowSpec specsToAdd) { + super.addSpecsToWorkflow(specsToAdd); + + _vchVmSpec = _vicTestbed.vchVmSpec; + _vchPortletSpec = VicCommon.buildPortletSpec(VicCommon.VCH_PORTLET_TITLE); + + _powerOnVMTaskSpec = new RecentTaskFilterSpec( + L10NVmConst.Task.POWER_ON_VM, + _vchVmSpec.getName(), + RecentTaskStatus.COMPLETED + ); + + specsToAdd.addSpec(_vchVmSpec, _vchPortletSpec, _powerOnVMTaskSpec, _poweredOnSpec); + } + + @Override + protected void addSetupSteps(StepComposition steps) { + steps.add(new SetVmPowerStateByApiStep(PowerState.poweredOff), _vchVmSpec); + steps.add(new MaximizeWindowStep()); + } + + @Override + protected void addTestSteps(StepComposition steps) { + steps + .add(new ObjectNavigator(L10NVmConst.Navigator.VC_INVENTORY_LISTS)) + .add(new ObjectNavigator(L10NVmConst.Navigator.VMS)) + .add(new ObjectNavigator(_vicTestbed.vchVmSpec.getName())) + .add(new TabNavigator(L10NVmConst.Tab.SUMMARY)) + .add(new InvokeActionMenuItemStep(L10NVmConst.Action.POWER, L10NVmConst.Action.POWER_ON)) + .add(new VerifyRecentTaskStep()) + .add(new VerifyPortletVisibilityStep(), _vchVmSpec) + .add(new VerifyVchPortletContentStep(), _poweredOnSpec); + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletExistsTest.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletExistsTest.java new file mode 100644 index 000000000..fd5a129da --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/test/vch/VchPortletExistsTest.java @@ -0,0 +1,75 @@ +package com.vmware.vsphere.client.automation.test.vch; + +import com.vmware.automation.core.annotation.RequiresTestbed; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.automation.core.testbed.ITestbedRequester.TestbedAllocation; +import com.vmware.automation.core.workflow.IWorkflowSpec; +import com.vmware.automation.core.workflow.StepComposition; +import com.vmware.automation.core.workflow.WorkflowStep; +import com.vmware.vim.binding.vim.VirtualMachine.PowerState; +import com.vmware.vsphere.client.automation.component.navigation.ObjectNavigator; +import com.vmware.vsphere.client.automation.component.navigation.TabNavigator; +import com.vmware.vsphere.client.automation.constants.L10NVmConst; +import com.vmware.vsphere.client.automation.spec.PortletSpec; +import com.vmware.vsphere.client.automation.step.common.MaximizeWindowStep; +import com.vmware.vsphere.client.automation.step.common.VerifyPortletVisibilityStep; +import com.vmware.vsphere.client.automation.step.common.VicCommon; +import com.vmware.vsphere.client.automation.step.srv.vm.SetVmPowerStateByApiStep; +import com.vmware.vsphere.client.automation.step.ui.VerifyLoadingIsDoneStep; +import com.vmware.vsphere.client.automation.step.vch.VchPortletHasProperLocaleStep; +import com.vmware.vsphere.client.automation.test.WebClientTest; +import com.vmware.vsphere.client.automation.testbed.HostTestbed; +import com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed; +import com.vmware.vsphere.client.automation.testbed.VicTestbed; + +/** + * VCH Portlet Exists Test: + * 1. Navigate to Virtual Container Host VM + * 2. Verify portlet 'Virtual Container Host' exists + * 3. Verify if portlet displays information in proper locale + * +*/ +public class VchPortletExistsTest extends WebClientTest { + @RequiresTestbed(clazz = SingleNgcTestbed.class, allocation = TestbedAllocation.SHARED) + private Testbed _singleNgcTestbed; + + @RequiresTestbed(clazz = HostTestbed.class) + private HostTestbed _hostTestbed; + + @RequiresTestbed(clazz = VicTestbed.class) + private VicTestbed _vicTestbed; + + private static String VCH_VM_TITLE; + private PortletSpec _vchPortletSpec; + + @Override + protected void addSpecsToWorkflow(IWorkflowSpec specsToAdd) { + super.addSpecsToWorkflow(specsToAdd); + VCH_VM_TITLE = _vicTestbed.vchVmSpec.getName(); + _vchPortletSpec = VicCommon.buildPortletSpec(VicCommon.VCH_PORTLET_TITLE); + specsToAdd.addSpec(_vchPortletSpec); + } + + public String getDescription() { + return "Checking if VCH portlet exists"; + } + + @Override + protected void addSetupSteps(StepComposition steps) { + steps.add(new MaximizeWindowStep()); + } + + @Override + protected void addTestSteps(StepComposition steps) { + steps + .add(new ObjectNavigator(L10NVmConst.Navigator.VC_INVENTORY_LISTS)) + .add(new ObjectNavigator(L10NVmConst.Navigator.VMS)) + .add(new ObjectNavigator(VCH_VM_TITLE)) + .add(new TabNavigator(L10NVmConst.Tab.SUMMARY)) + .add(new VerifyLoadingIsDoneStep()) + .add(new VerifyPortletVisibilityStep(), _vchPortletSpec) + .add(new VchPortletHasProperLocaleStep()); + + } + +} diff --git a/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/testbed/VicTestbed.java b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/testbed/VicTestbed.java new file mode 100644 index 000000000..47b31f87c --- /dev/null +++ b/h5c/vic-uia/src/main/java/com/vmware/vsphere/client/automation/testbed/VicTestbed.java @@ -0,0 +1,26 @@ +package com.vmware.vsphere.client.automation.testbed; + +import com.vmware.automation.core.annotation.TestbedInfo; +import com.vmware.automation.core.testbed.Testbed; +import com.vmware.vsphere.client.automation.core.spec.SingleValueSpec; +import com.vmware.vsphere.client.automation.core.spec.VmSpec; + +/** + * Defines VIC testbed to be used in vSphere tests + */ + +@TestbedInfo(version = 1) +public class VicTestbed extends Testbed { + + private static final long serialVersionUID = 1L; + + /* + * Spec defining VCH VM + */ + public VmSpec vchVmSpec; + + /* + * Spec defining test Container VM + */ + public VmSpec containerVmSpec; +} diff --git a/h5c/vic-uia/src/main/resources/locale/de_DE/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/de_DE/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/de_DE/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/src/main/resources/locale/en_US/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/en_US/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/en_US/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/src/main/resources/locale/fr_FR/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/fr_FR/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/fr_FR/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/src/main/resources/locale/ja_JP/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/ja_JP/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/ja_JP/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/src/main/resources/locale/ko_KR/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/ko_KR/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/ko_KR/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/src/main/resources/locale/zh_CN/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/zh_CN/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/zh_CN/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/src/main/resources/locale/zh_TW/com_vmware_vic.properties b/h5c/vic-uia/src/main/resources/locale/zh_TW/com_vmware_vic.properties new file mode 100644 index 000000000..0914d4558 --- /dev/null +++ b/h5c/vic-uia/src/main/resources/locale/zh_TW/com_vmware_vic.properties @@ -0,0 +1,10 @@ +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic-uia/support/runner-generateProviders b/h5c/vic-uia/support/runner-generateProviders new file mode 100755 index 000000000..29c2a474a --- /dev/null +++ b/h5c/vic-uia/support/runner-generateProviders @@ -0,0 +1,370 @@ +#!/usr/bin/env python + +import os +import os.path +import sys + +from optparse import OptionParser +from exceptions import ValueError + +def parse_arguments(): + parser = OptionParser() + parser.add_option("--admin.user", dest="admin.user", default="Administrator") + parser.add_option("--admin.domain", dest="admin.domain", default="vsphere.local") + parser.add_option("--admin.password", dest="admin.password", default="Admin!23") + parser.add_option("--h5client.ip", dest="h5client.ip") + parser.add_option("--h5client.port", dest="h5client.port") + parser.add_option("--h5client.url.suffix", dest="h5client.url.suffix", default="?debug=true") + parser.add_option("--selenium.ip", dest="selenium.ip", default="localhost") + parser.add_option("--browser.os", dest="browser.os", default="MacOSX") + parser.add_option("--selenium.port", dest="selenium.port", default="4444") + parser.add_option("--browser.type", dest="browser.type", default="Chrome") + parser.add_option("--workDir", dest="workDir", default=os.path.join(os.path.dirname(__file__), "work")) + parser.add_option("--vc.ip", dest="vc.ip") + parser.add_option("--vc.name", dest="vc.name", default="null") + parser.add_option("--host.ip", dest="host.ip") + parser.add_option("--host.license", dest="host.license", default="null") + parser.add_option("--host.datastore_name", dest="host.datastore_name", default='datastore1') + parser.add_option("--secondHost.ip", dest="secondHost.ip") + parser.add_option("--secondHost.datastore_name", dest="secondHost.datastore_name", default='datastore1 (1)') + parser.add_option("--secondHost.license", dest="secondHost.license", default="null") + parser.add_option("--iscsi.ip", dest="iscsi.ip") + parser.add_option("--iscsi.port", dest="iscsi.port") + parser.add_option("--nfs.ip", dest="nfs.ip") + parser.add_option("--nfs.shareMount", dest="nfs.shareMount") + parser.add_option("--vch.name", dest="vch.name") + parser.add_option("--vch.moid", dest="vch.moid") + parser.add_option("--container.name", dest="container.name") + + (options, args) = parser.parse_args() + options = options.__dict__ + + validateRequiredOption('vc.ip', options) + validateRequiredOption('host.ip', options) + validateRequiredOption('vch.name', options) + validateRequiredOption('vch.moid', options) + validateRequiredOption('container.name', options) + validateRequiredOption('h5client.ip', options) + validateRequiredOption('h5client.port', options) + + return (options, args) + +def validateRequiredOption(requiredOption, options): + if not options[requiredOption] : + raise ValueError("Requred parameter " + requiredOption + " is missing!!!") + + +def getUserSpec(username, domain, password): + return '{username:"' + username + '",password:"' + password + '",domain:"' + domain + '"}' + +def getVcSpec(vcIp, adminUser, vcName): + return '{_endpoint:"' + vcIp + '",_user:' + adminUser + ',user:' + adminUser + ',endpoint:"' + vcIp + '", _vcName:"' + vcName + '", vcName:"' + vcName + '"}' + +def getHostSpec(hostIp, adminUser, license): + _license = '"'+license+'"' if license!='null' else 'null' + return '{_name:"' + hostIp + '",_user:' + adminUser + ',user:' + adminUser + ',name:"' + hostIp + '",mor:null,_mor:null,license:' + _license +'}' + +def getVmSpec(name, vmId=None): + mor = 'null' if vmId is None else '{_type:"VirtualMachine",_value:"' + vmId + '",type:"VirtualMachine",value:"' + vmId + '"}' + return '{_name:"' + name + '",_mor:' + mor + ',name:"' + name + '",mor:' + mor + '}' + +def getIscsiShareSpec(iscsiIp, port): + _port = port if port is not None else '3260' + return '{"_iscsiIpAddress": "' + iscsiIp + '", "_port": ' + _port + '}' + +def getNfsShareSpec(nfsIp, nfsVersion, nfsShareMountPoint, nfsShareAccessMode): + return '{_nfsServerVersion:"' + nfsVersion + '",_nfsServerIpAddress:"' + nfsIp + '",_nfsShareMountPoint:"' + nfsShareMountPoint + '",_nfsShareAccessMode:"' + nfsShareAccessMode + '",serverVersion:"' + nfsVersion + '",shareMountPoint:"' + nfsShareMountPoint + '",shareAccessMode:"' + nfsShareAccessMode + '",serverIpAddress:"' + nfsIp + '"}' + +def getDatastoreSpec(datastoreName): + return '{"_name": "' + datastoreName + '","_mor": null,"name": "' + datastoreName + '","mor": null}' + +def getH5cSpec(h5cIp, h5cPort, adminUser, urlSuffix): + return '{_endpoint:"https://' + h5cIp + ':' + h5cPort + '/ui/' + urlSuffix + '" ,_user:' + adminUser + ',user:' + adminUser + ',endpoint:"https://' + h5cIp + ':' + h5cPort + '/ui/' + urlSuffix + '"}' + +def getSsoSpec(ssoIp, adminUser): + return '{_endpoint:"' + ssoIp + '",_user:' + adminUser + ',componentManagerEndpoint:"https://' + ssoIp + '/cm/sdk",ssoVersion:"com.vmware.vim.binding.sso.version.version3_1",user:' + adminUser + ',endpoint:"' + ssoIp + '"}' + +def getSingleNgcTestbedSpec(vcSpec, h5cSpec, ssoSpec): + return '{clazz:"com.vmware.vsphere.client.automation.testbed.SingleNgcTestbed",vcSpec:' + vcSpec + ',ngcSpec:' + h5cSpec + ',ssoSpec: ' + ssoSpec + '}' + +def getBrowserTestbedSpec(seleniumIp, seleniumPort, browserType, os): + return '{clazz:"com.vmware.vsphere.client.automation.testbed.BrowserTestbed",browserSpec:{ip:"' + seleniumIp + '",' + 'os:"' + os + '",port:' + seleniumPort + ',browserType:"' + browserType + '",specId:"0d9d3965-c48b-400d-9a2c-26cf575fca6c"}}' + +def getHostTestbedSpec(hostSpec, datastoreSpec): + return '{clazz:"com.vmware.vsphere.client.automation.testbed.HostTestbed","hostSpec":' + hostSpec + ',"localDatastoreSpec":' + datastoreSpec + '}' + +def getVicTestbedSpec(vchVmSpec, containerVmSpec): + return '{clazz:"com.vmware.vsphere.client.automation.testbed.VicTestbed","vchVmSpec":' + vchVmSpec + ',"containerVmSpec":' + containerVmSpec + '}' + +def getIscsiShareTestbed(iscsiShareSpec): + return '{"clazz": "com.vmware.vsphere.client.automation.testbed.IscsiShareTestbed", "iscsiShareSpec": ' + iscsiShareSpec + '}' + +def getNfsShareTestbed(nfsSharedSpec): + return '{clazz:"com.vmware.vsphere.client.automation.testbed.NfsShareTestbed","nfsShareSpec":' + nfsSharedSpec + '}' + +def getSingleNgcTestbedData(): + adminUser = getUserSpec(options['admin.user'], options['admin.domain'], options['admin.password']) + vcSpec = getVcSpec(options['vc.ip'], adminUser, options['vc.name']) + h5cSpec = getH5cSpec(options['h5client.ip'], options['h5client.port'], adminUser, options['h5client.url.suffix']) + ssoSpec = getSsoSpec(options['vc.ip'], adminUser) + return vcSpec, h5cSpec, ssoSpec + +def getSingleNgcTestbedJson(): + vcSpec, h5cSpec, ssoSpec = getSingleNgcTestbedData() + return getSingleNgcTestbedSpec(vcSpec, h5cSpec, ssoSpec) + +def getBrowserTestbedJson(): + return getBrowserTestbedSpec(options['selenium.ip'], options['selenium.port'], options['browser.type'], options['browser.os']) + +def getHostTestbedJson(hostIp, datastore, license): + adminUser = getUserSpec('root', '', 'ca$hc0w') + hostSpec = getHostSpec(hostIp, adminUser, license) + datastoreSpec = getDatastoreSpec(datastore) + return getHostTestbedSpec(hostSpec, datastoreSpec) + +def getVicTestbedJson(vchName, containerName): + vchVmSpec = getVmSpec(vchName, options['vch.moid']) + containerVmSpec = getVmSpec(containerName) + return getVicTestbedSpec(vchVmSpec, containerVmSpec) + +def getIscsiShareTestbedJson(iscsiIp, port): + iscsiShareSpec = getIscsiShareSpec(iscsiIp, port) + return getIscsiShareTestbed(iscsiShareSpec) + +def getNfsShareTestbedJson(nfsIp, nfsShareMount): + nfsShareSpec = getNfsShareSpec(nfsIp, 'NFS_3', nfsShareMount, 'READ_WRITE') + return getNfsShareTestbed(nfsShareSpec) + +def getVmGenericProvisioningTestbedJson(): + vcSpec, h5cSpec, ssoSpec = getSingleNgcTestbedData() + testbedJson = '''{{ + "clazz": "com.vmware.vsphere.client.automation.testbed.VmGenericProvisioningTestbed", + "vcSpec" : {0}, + "ngcSpec" : {1}, + "ssoSpec" : {2}, + "datacenterSpec": {{ + "name": null, + "mor": {{ + "type": "Datacenter", + "value": null, + "serverGuid": null + }} + }}, + "standaloneHostsFolderSpec": {{ + "name": null, + "mor": {{ + "type": "Folder", + "value": null, + "serverGuid": null + }} + }}, + "hostASpec" : {{ + "user": {{ + "username": null, + "password": null, + "domain": "" + }}, + "name": null, + "mor": {{ + "type": "HostSystem", + "value": null, + "serverGuid": null + }} + }}, + "hostBSpec" : {{ + "user": {{ + "username": null, + "password": null, + "domain": "" + }}, + "name": null, + "mor": {{ + "type": "HostSystem", + "value": null, + "serverGuid": null + }} + }}, + "clusterDrsASpec" : {{ + "name": null, + "mor": {{ + "type" : "ClusterComputeResource", + "value" : null, + "serverGuid" : null + }} + }}, + "hostCSpec" : {{ + "name": null, + "mor": {{ + "type": "HostSystem", + "value": null, + "serverGuid": null + }} + }}, + "hostDSpec" : {{ + "name": null, + "mor": {{ + "type": "HostSystem", + "value": null, + "serverGuid": null + }} + }}, + "clusterDrsBSpec" : {{ + "name": null, + "mor": {{ + "type" : "ClusterComputeResource", + "value" : null, + "serverGuid" : null + }} + }}, + "hostESpec" : {{ + "name": null, + "mor": {{ + "type": "HostSystem", + "value": null, + "serverGuid": null + }} + }}, + "hostFSpec" : {{ + "name": null, + "mor": {{ + "type": "HostSystem", + "value": null, + "serverGuid": null + }} + }}, + "localDatastoreHostASpec" : {{ + "name" : null, + "mor" : {{ + "type" : "Datastore", + "value" : null, + "serverGuid" : null + }} + }}, + "localDatastoreHostBSpec" : {{ + "name" : null, + "mor" : {{ + "type" : "Datastore", + "value" : null, + "serverGuid" : null + }} + }}, + "localDatastoreHostCSpec" : {{ + "name" : null, + "mor" : {{ + "type" : "Datastore", + "value" : null, + "serverGuid" : null + }} + }}, + "localDatastoreHostDSpec" : {{ + "name" : null, + "mor" : {{ + "type" : "Datastore", + "value" : null, + "serverGuid" : null + }} + }}, + "localDatastoreHostESpec" : {{ + "name" : null, + "mor" : {{ + "type" : "Datastore", + "value" : null, + "serverGuid" : null + }} + }}, + "localDatastoreHostFSpec" : {{ + "name" : null, + "mor" : {{ + "type" : "Datastore", + "value" : null, + "serverGuid" : null + }} + }}, + "sharedDatastoreASpec": {{ + "clazz": "com.vmware.vsphere.client.automation.core.spec.NfsDatastoreSpec", + "name": null, + "mor": {{ + "type": "Datastore", + "value": null, + "serverGuid": null + }} + }} +}} +'''.format(vcSpec, h5cSpec, ssoSpec) + + return testbedJson + +def getRunlistContents(testbeds, resultsPath, tests): + contents = "" + for testbed in testbeds: + contents = contents + "registry-add-testbed " + testbed + "\n" + for test in tests: + contents = contents + "run-test " + test + " " + os.path.join(resultsPath, test.replace(".", "_")) + "\n" + return contents + +def saveToFile(dir, filename, contents): + targetFile = os.path.join(dir, filename) + file = open(targetFile, 'w') + file.write(contents) + file.close() + return targetFile + +if __name__ == '__main__': + (options, args) = parse_arguments() + # create work folder if not exists + if (not os.path.exists(options['workDir'])): + os.mkdir(options['workDir']) + # create testbeds folder if not exists + testbedsPath = os.path.join(options['workDir'], 'testbeds') + if (not os.path.exists(testbedsPath)): + os.mkdir(testbedsPath) + + # create runlists folder if not exists + runlistsPath = os.path.join(options['workDir'], 'runlists') + if (not os.path.exists(runlistsPath)): + os.mkdir(runlistsPath) + + # create results folder if not exists + resultsPath = os.path.join(options['workDir'], 'results') + if (not os.path.exists(resultsPath)): + os.mkdir(resultsPath) + + testbeds = [] + print "Generating BrowserTestbed.json..." + browserTestbed = saveToFile(testbedsPath, 'BrowserTestbed.json', getBrowserTestbedJson()) + testbeds.append(browserTestbed) + + print "Generating SingleNgcTestbed.json..." + singleNgcTestbed = saveToFile(testbedsPath, 'SingleNgcTestbed.json', getSingleNgcTestbedJson()) + testbeds.append(singleNgcTestbed) + + print "Generating HostTestbed.json..." + hostTestbed = saveToFile(testbedsPath, 'HostTestbed.json', getHostTestbedJson(options['host.ip'], options['host.datastore_name'], options['host.license'])) + testbeds.append(hostTestbed) + + if options['secondHost.ip'] is not None : + print "Generating SecondHostTestbed.json..." + secondHostTestbed = saveToFile(testbedsPath, 'SecondHostTestbed.json', getHostTestbedJson(options['secondHost.ip'], options['secondHost.datastore_name'], options['secondHost.license'])) + testbeds.append(secondHostTestbed) + + if options['nfs.ip'] is not None and options['nfs.shareMount'] is not None : + print "Generating NfsShareTestbed.json..." + nfsShareTestbed = saveToFile(testbedsPath, 'NfsShareTestbed.json', getNfsShareTestbedJson(options['nfs.ip'], options['nfs.shareMount'])) + testbeds.append(nfsShareTestbed) + + if options['iscsi.ip'] is not None : + print "Generating IscsiShareTestbed.json..." + iscsiShareTestbed = saveToFile(testbedsPath, 'IscsiShareTestbed.json', getIscsiShareTestbedJson(options['iscsi.ip'], options['iscsi.port'])) + testbeds.append(iscsiShareTestbed) + + print "Generating VmGenericProvisioningTestbed.json..." + genericVmPrivisioningTestbed = saveToFile(testbedsPath, + 'VmGenericProvisioningTestbed.json', + getVmGenericProvisioningTestbedJson()) + + print "Generating VicTestbed.json..." + vicTestbed = saveToFile(testbedsPath, 'VicTestbed.json', getVicTestbedJson(options['vch.name'], options['container.name'])) + testbeds.append(vicTestbed) + + print "Generating HSUIA runlist..." + saveToFile(runlistsPath, 'default.runlist', getRunlistContents(testbeds, resultsPath, args)) diff --git a/h5c/vic/.externalToolBuilders/org.eclipse.wst.jsdt.core.javascriptValidator.launch b/h5c/vic/.externalToolBuilders/org.eclipse.wst.jsdt.core.javascriptValidator.launch new file mode 100644 index 000000000..627021fb9 --- /dev/null +++ b/h5c/vic/.externalToolBuilders/org.eclipse.wst.jsdt.core.javascriptValidator.launch @@ -0,0 +1,7 @@ + + + + + + + diff --git a/h5c/vic/.project b/h5c/vic/.project new file mode 100644 index 000000000..8b39fa5a7 --- /dev/null +++ b/h5c/vic/.project @@ -0,0 +1,35 @@ + + + vic + + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.wst.jsdt.core.javascriptValidator.launch + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + com.eclipsesource.jshint.ui.builder + + + + + + org.eclipse.virgo.ide.facet.core.bundlenature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/h5c/vic/.settings/.jsdtscope b/h5c/vic/.settings/.jsdtscope new file mode 100644 index 000000000..a0d53c388 --- /dev/null +++ b/h5c/vic/.settings/.jsdtscope @@ -0,0 +1,6 @@ + + + + + + diff --git a/h5c/vic/.settings/com.eclipsesource.jshint.ui.prefs b/h5c/vic/.settings/com.eclipsesource.jshint.ui.prefs new file mode 100644 index 000000000..a092ce028 --- /dev/null +++ b/h5c/vic/.settings/com.eclipsesource.jshint.ui.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +excluded=//*.min.js +included=//*.js diff --git a/h5c/vic/.settings/org.eclipse.wst.common.component b/h5c/vic/.settings/org.eclipse.wst.common.component new file mode 100644 index 000000000..86190b422 --- /dev/null +++ b/h5c/vic/.settings/org.eclipse.wst.common.component @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/h5c/vic/.settings/org.eclipse.wst.common.project.facet.core.xml b/h5c/vic/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 000000000..5ea20af1a --- /dev/null +++ b/h5c/vic/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/h5c/vic/build-plugin-package.bat b/h5c/vic/build-plugin-package.bat new file mode 100644 index 000000000..acb216d99 --- /dev/null +++ b/h5c/vic/build-plugin-package.bat @@ -0,0 +1,33 @@ +@echo off +REM Copyright 2017 VMware, Inc. All Rights Reserved. +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM 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, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +@setlocal +@IF not defined ANT_HOME ( + @echo BUILD FAILED: You must set the env variable ANT_HOME to your Apache Ant folder + goto end +) +@IF not defined VSPHERE_SDK_HOME ( + @echo BUILD FAILED: You must set the env variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + goto end +) +@IF not exist "%VSPHERE_SDK_HOME%\libs\vsphere-client-lib.swc" ( + @echo BUILD FAILED: VSPHERE_SDK_HOME is not set to a valid vSphere Client SDK folder + @echo %VSPHERE_SDK_HOME%\libs\vsphere-client-lib.swc is missing + goto end +) + +@call "%ANT_HOME%\bin\ant" -f build-plugin-package.xml + +:end diff --git a/h5c/vic/build-plugin-package.sh b/h5c/vic/build-plugin-package.sh new file mode 100644 index 000000000..91ef43cef --- /dev/null +++ b/h5c/vic/build-plugin-package.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Copyright 2017 VMware, Inc. All Rights Reserved. +# +# Licensed 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. +# +# Mac OS script +# Note: if Ant runs out of memory try defining ANT_OPTS=-Xmx512M + +if [ -z "$ANT_HOME" ] || [ ! -f "${ANT_HOME}"/bin/ant ] +then + echo BUILD FAILED: You must set the environment variable ANT_HOME to your Apache Ant folder + exit 1 +fi + +if [ -z "$VSPHERE_SDK_HOME" ] || [ ! -f "${VSPHERE_SDK_HOME}"/libs/vsphere-client-lib.jar ] +then + echo BUILD FAILED: You must set the environment variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + exit 1 +fi + +"${ANT_HOME}"/bin/ant -f build-plugin-package.xml + +exit 0 diff --git a/h5c/vic/build-plugin-package.xml b/h5c/vic/build-plugin-package.xml new file mode 100644 index 000000000..cdd6992d8 --- /dev/null +++ b/h5c/vic/build-plugin-package.xml @@ -0,0 +1,36 @@ + + + Creates the plugin package folder from vic and vic-service: + vic + plugin-package.xml + /plugins + gson-2.3.1.jar + vic.war + vic-service.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic/build-resources.bat b/h5c/vic/build-resources.bat new file mode 100644 index 000000000..a81887581 --- /dev/null +++ b/h5c/vic/build-resources.bat @@ -0,0 +1,33 @@ +@echo off +REM Copyright 2017 VMware, Inc. All Rights Reserved. +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM 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, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +@setlocal +@IF not defined ANT_HOME ( + @echo BUILD FAILED: You must set the env variable ANT_HOME to your Apache Ant folder + goto end +) +@IF not defined VSPHERE_SDK_HOME ( + @echo BUILD FAILED: You must set the env variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + goto end +) +@IF not exist "%VSPHERE_SDK_HOME%\libs\vsphere-client-lib.jar" ( + @echo BUILD FAILED: VSPHERE_SDK_HOME is not set to a valid vSphere Client SDK folder + @echo %VSPHERE_SDK_HOME%\libs\vsphere-client-lib.jar is missing + goto end +) + +@call "%ANT_HOME%\bin\ant" -f build-resources.xml + +:end diff --git a/h5c/vic/build-resources.sh b/h5c/vic/build-resources.sh new file mode 100644 index 000000000..e6b50d77d --- /dev/null +++ b/h5c/vic/build-resources.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# Copyright 2017 VMware, Inc. All Rights Reserved. +# +# Licensed 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. + +if [ -z "$ANT_HOME" ] || [ ! -f "${ANT_HOME}"/bin/ant ] +then + echo BUILD FAILED: You must set the environment variable ANT_HOME to your Apache Ant folder + exit 1 +fi + +if [ -z "$VSPHERE_SDK_HOME" ] || [ ! -f "${VSPHERE_SDK_HOME}"/libs/vsphere-client-lib.jar ] +then + echo BUILD FAILED: You must set the environment variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + exit 1 +fi + +"${ANT_HOME}"/bin/ant -f build-resources.xml + +exit 0 diff --git a/h5c/vic/build-resources.xml b/h5c/vic/build-resources.xml new file mode 100644 index 000000000..5ccc2b896 --- /dev/null +++ b/h5c/vic/build-resources.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/h5c/vic/build-war.bat b/h5c/vic/build-war.bat new file mode 100644 index 000000000..ab6ae14c1 --- /dev/null +++ b/h5c/vic/build-war.bat @@ -0,0 +1,33 @@ +@echo off +REM Copyright 2017 VMware, Inc. All Rights Reserved. +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM 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, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +@setlocal +@IF not defined ANT_HOME ( + @echo BUILD FAILED: You must set the env variable ANT_HOME to your Apache Ant folder + goto end +) +@IF not defined VSPHERE_SDK_HOME ( + @echo BUILD FAILED: You must set the env variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + goto end +) +@IF not exist "%VSPHERE_SDK_HOME%\libs\vsphere-client-lib.jar" ( + @echo BUILD FAILED: VSPHERE_SDK_HOME is not set to a valid vSphere Client SDK folder + @echo %VSPHERE_SDK_HOME%\libs\vsphere-client-lib.jar is missing + goto end +) + +@call "%ANT_HOME%\bin\ant" -f build-war.xml + +:end diff --git a/h5c/vic/build-war.sh b/h5c/vic/build-war.sh new file mode 100644 index 000000000..dd59acf54 --- /dev/null +++ b/h5c/vic/build-war.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# Copyright 2017 VMware, Inc. All Rights Reserved. +# +# Licensed 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. + +if [ -z "$ANT_HOME" ] || [ ! -f "${ANT_HOME}"/bin/ant ] +then + echo BUILD FAILED: You must set the environment variable ANT_HOME to your Apache Ant folder + exit 1 +fi + +if [ -z "$VSPHERE_SDK_HOME" ] || [ ! -f "${VSPHERE_SDK_HOME}"/libs/vsphere-client-lib.jar ] +then + echo BUILD FAILED: You must set the environment variable VSPHERE_SDK_HOME to your vSphere Client SDK folder + exit 1 +fi + +"${ANT_HOME}"/bin/ant -f build-war.xml + +exit 0 diff --git a/h5c/vic/build-war.xml b/h5c/vic/build-war.xml new file mode 100644 index 000000000..ce0dbd37e --- /dev/null +++ b/h5c/vic/build-war.xml @@ -0,0 +1,112 @@ + + + Ant build script for HTML plugin. It outputs a .war file that can be + copied in the "plugins" folder of the plugin-package. + ----------------------------------------------------------------------- + NOTE: you don't need to use this script during development with Eclipse + except to compile resources, see the compile-resources target below. + ----------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic/plugin-package.xml b/h5c/vic/plugin-package.xml new file mode 100644 index 000000000..485f99078 --- /dev/null +++ b/h5c/vic/plugin-package.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic/src/main/locale/de_DE/com_vmware_vic.properties b/h5c/vic/src/main/locale/de_DE/com_vmware_vic.properties new file mode 100644 index 000000000..2e4d0474a --- /dev/null +++ b/h5c/vic/src/main/locale/de_DE/com_vmware_vic.properties @@ -0,0 +1,44 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/locale/en_US/com_vmware_vic.properties b/h5c/vic/src/main/locale/en_US/com_vmware_vic.properties new file mode 100644 index 000000000..46f07ac5f --- /dev/null +++ b/h5c/vic/src/main/locale/en_US/com_vmware_vic.properties @@ -0,0 +1,51 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# vch compute capacity +vch.computeCapacity.vmHostAffinity.title=VM-Host Affinity +vch.computeCapacity.vmHostAffinity.label=Create a DRS VM Group for this VCH +vch.computeCapacity.vmHostAffinity.info=Checking this box will create a DRS VM Group with the same name as the VCH. Container VMs created via the VCH will automatically be added to the group. The group will be deleted when the VCH is deleted. To make use of this group, create a VM-Host Affinity rule on the cluster containing the VCH. +vch.computeCapacity.vmHostAffinity.groupNameError=A DRS VM Group with the same name already exists +vch.computeCapacity.vmHostAffinity.drsError=Cluster DRS is disabled + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/locale/fr_FR/com_vmware_vic.properties b/h5c/vic/src/main/locale/fr_FR/com_vmware_vic.properties new file mode 100644 index 000000000..2e4d0474a --- /dev/null +++ b/h5c/vic/src/main/locale/fr_FR/com_vmware_vic.properties @@ -0,0 +1,44 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/locale/ja_JP/com_vmware_vic.properties b/h5c/vic/src/main/locale/ja_JP/com_vmware_vic.properties new file mode 100644 index 000000000..2e4d0474a --- /dev/null +++ b/h5c/vic/src/main/locale/ja_JP/com_vmware_vic.properties @@ -0,0 +1,44 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/locale/ko_KR/com_vmware_vic.properties b/h5c/vic/src/main/locale/ko_KR/com_vmware_vic.properties new file mode 100644 index 000000000..2e4d0474a --- /dev/null +++ b/h5c/vic/src/main/locale/ko_KR/com_vmware_vic.properties @@ -0,0 +1,44 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/locale/zh_CN/com_vmware_vic.properties b/h5c/vic/src/main/locale/zh_CN/com_vmware_vic.properties new file mode 100644 index 000000000..2e4d0474a --- /dev/null +++ b/h5c/vic/src/main/locale/zh_CN/com_vmware_vic.properties @@ -0,0 +1,44 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/locale/zh_TW/com_vmware_vic.properties b/h5c/vic/src/main/locale/zh_TW/com_vmware_vic.properties new file mode 100644 index 000000000..2e4d0474a --- /dev/null +++ b/h5c/vic/src/main/locale/zh_TW/com_vmware_vic.properties @@ -0,0 +1,44 @@ +# portlets +container.label=Container +container.name.label=Name +container.image.label=Image +container.portmapping.label=Port Mapping +vch.label=Virtual Container Host +vch.dockerApiEndpoint.label=Docker API endpoint +vch.vchAdminPortal.label=VCH Admin portal + +# vic object workspace: vic obj datagrid +vic_workspace.datagrid.columns.name=Name +vic_workspace.datagrid.columns.vchVmsLen=# of VCH VMs +vic_workspace.datagrid.columns.containerVmsLen=# of Container VMs + +# vic object workspace: summary +vic_workspace.summary.tab.label=Summary +vic_workspace.summary.vendor.label=Vendor +vic_workspace.summary.version.label=Version +vic_workspace.summary.vch.label=Virtual Container Hosts + +# vic object workspace: vch view +vic_workspace.vch.tab.label=Virtual Container Hosts +vic_workspace.vch.datagrid.columns.name=Name +vic_workspace.vch.datagrid.columns.overallStatus=Status +vic_workspace.vch.datagrid.columns.dockerApiEndpoint=Docker API Endpoint +vic_workspace.vch.datagrid.columns.vchAdminPortal=VCH Admin Portal +vic_workspace.vch.datagrid.msg.waitingForIp=Waiting... + +# vic object workspace: container view +vic_workspace.container.tab.label=Containers +vic_workspace.container.datagrid.columns.containerName=Name +vic_workspace.container.datagrid.columns.powerState=State +vic_workspace.container.datagrid.columns.guestMemoryUsage=Memory Usage +vic_workspace.container.datagrid.columns.overallCpuUsage=CPU Usage +vic_workspace.container.datagrid.columns.committedStorage=Storage Usage +vic_workspace.container.datagrid.columns.portMapping=Port Mapping +vic_workspace.container.datagrid.columns.name=VM +vic_workspace.container.datagrid.columns.imageName=Image + +# resources +home.shortcut.icon = Embed("../../webapp/assets/vic-icons/32x32.png") +viinventory.vic.icon = Embed("../../webapp/assets/vic-icons/16x16.png") +addIcon = Embed("../../webapp/assets/images/addIcon.png") +editIcon = Embed("../../webapp/assets/images/editIcon.png") diff --git a/h5c/vic/src/main/webapp/META-INF/MANIFEST.MF b/h5c/vic/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e68652c83 --- /dev/null +++ b/h5c/vic/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: vic-ui +Bundle-SymbolicName: com.vmware.vic.ui +Bundle-Version: 1.0.0 +Bundle-Vendor: VMware +Web-ContextPath: vsphere-client/vic +Import-Package: com.vmware.vic.mvc;version="1.0.0", + com.vmware.vic.filters;version="1.0.0", + com.vmware.vic.services;version="1.0.0", + com.vmware.vise.data.query;version=0, + com.vmware.vise.security;version=0, + com.vmware.vise.usersession;version="0", + org.eclipse.virgo.web.dm;version="[3.6.0,4)", + org.springframework.web.context, + org.springframework.web.servlet, + org.springframework.web.servlet.config, + org.springframework.web.servlet.view, + org.springframework.web.servlet.view.json diff --git a/h5c/vic/src/main/webapp/WEB-INF/spring/bundle-context.xml b/h5c/vic/src/main/webapp/WEB-INF/spring/bundle-context.xml new file mode 100644 index 000000000..5b09be282 --- /dev/null +++ b/h5c/vic/src/main/webapp/WEB-INF/spring/bundle-context.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/h5c/vic/src/main/webapp/WEB-INF/web.xml b/h5c/vic/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..928c07ac4 --- /dev/null +++ b/h5c/vic/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,63 @@ + + + + + + vic + + + + contextConfigLocation + /WEB-INF/spring/bundle-context.xml + + + contextClass + org.eclipse.virgo.web.dm.ServerOsgiBundleXmlWebApplicationContext + + + + org.springframework.web.context.ContextLoaderListener + + + + + springServlet + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + /WEB-INF/spring/bundle-context.xml + + + 1 + + + + springServlet + /rest/* + + + + + sessionManagementFilter + com.vmware.vise.security.SessionManagementFilter + + + + vicSessionFilter + com.vmware.vic.filters.VicSessionFilter + + + + sessionManagementFilter + /* + + + + vicSessionFilter + /rest/* + + + \ No newline at end of file diff --git a/h5c/vic/src/main/webapp/assets/css/default.css b/h5c/vic/src/main/webapp/assets/css/default.css new file mode 100644 index 000000000..72bf96603 --- /dev/null +++ b/h5c/vic/src/main/webapp/assets/css/default.css @@ -0,0 +1,5 @@ +@CHARSET "US-ASCII"; +body { + font-size: 12px; + font-family: Arial; +} diff --git a/h5c/vic/src/main/webapp/assets/css/plugin-icons.css b/h5c/vic/src/main/webapp/assets/css/plugin-icons.css new file mode 100644 index 000000000..cbd7c9ce1 --- /dev/null +++ b/h5c/vic/src/main/webapp/assets/css/plugin-icons.css @@ -0,0 +1,53 @@ +/* + * EXTERNAL ICONS SUPPORT: plugin-icons.css contain "external" icons, i.e. the ones + * displayed outside plugin's views, in the main app menus, shortcuts or object lists. + * Do not include other CSS rules here! + * + * Css name format must be ".bundleName-iconKey", where bundleName is the + * defaultBundle and iconKey the resource key used in plugin.xml: + * + * + * ... + * #{iconKey} + * + * The same iconKey is defined in the .properties file used to compile Flex resources: + * iconKey = Embed("../../webapp/assets/images/someIcon.png") + * + * Follow the SDK doc or other samples for the correct attributes based on the type of icon. + */ +.com_vmware_vic-addIcon { + background: url("../images/addIcon.png"); + width: 16px; + height: 16px; + display: inline-block; + vertical-align: text-bottom; + margin: 1px 4px 0; +} +.com_vmware_vic-editIcon { + background: url("../images/editIcon.png"); + width: 16px; + height: 16px; + display: inline-block; + vertical-align: text-bottom; + margin: 1px 4px 0; +} + +.com_vmware_vic-home-shortcut-icon { + background: url("../vic-icons/32x32.png"); + width: 32px; + height: 32px; + display: inline-block; + vertical-align: top; +} + +.com_vmware_vic-viinventory-vic-icon, +.com_vmware_vic-vic-root-icon { + background: url("../vic-icons/16x16.png"); + width: 16px; + height: 16px; + display: inline-block; + margin: 1px 4px 0; + vertical-align: text-bottom; + line-height: 16px; + background-repeat: no-repeat; +} \ No newline at end of file diff --git a/h5c/vic/src/main/webapp/assets/images/addIcon.png b/h5c/vic/src/main/webapp/assets/images/addIcon.png new file mode 100644 index 000000000..b479f08ae Binary files /dev/null and b/h5c/vic/src/main/webapp/assets/images/addIcon.png differ diff --git a/h5c/vic/src/main/webapp/assets/images/editIcon.png b/h5c/vic/src/main/webapp/assets/images/editIcon.png new file mode 100644 index 000000000..3884bd17d Binary files /dev/null and b/h5c/vic/src/main/webapp/assets/images/editIcon.png differ diff --git a/h5c/vic/src/main/webapp/assets/jquery-1.10.2.min.js b/h5c/vic/src/main/webapp/assets/jquery-1.10.2.min.js new file mode 100644 index 000000000..f4c12cae1 --- /dev/null +++ b/h5c/vic/src/main/webapp/assets/jquery-1.10.2.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="

",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("