├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING_DCO.md ├── LICENSE ├── NOTICE ├── README.md ├── add-on └── multi-instance-skeleton │ ├── .e2e │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── cypress.json │ ├── cypress │ │ ├── fixtures │ │ │ └── .gitkeep │ │ ├── integration │ │ │ ├── example.feature │ │ │ └── example │ │ │ │ └── example.stepdefs.ts │ │ ├── plugins │ │ │ ├── index.js │ │ │ └── preprocess.js │ │ ├── support │ │ │ ├── commands.js │ │ │ └── index.js │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── package-lock.json │ └── package.json │ ├── .ui-emulator │ ├── .env │ │ ├── environment.json │ │ └── proxy.conf.json │ ├── .gitignore │ ├── .npmrc │ ├── config.json │ ├── package-lock.json │ └── package.json │ ├── EULA.txt │ ├── actions │ └── multipurposeaction │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── addonPreCreateMock.json │ │ ├── build.args │ │ ├── go.mod │ │ ├── main.go │ │ └── main_test.go │ ├── backend-appliance │ ├── Dockerfile │ ├── skeleton.mf │ └── skeleton.ovf │ ├── db-schemas │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── build.args │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── SkeletonDatabaseEntity.ts │ │ └── behaviors │ │ │ └── SkeletonDatabaseEntityRevealBehaviorExecutionContext.ts │ └── tsconfig.json │ ├── icon.png │ ├── index.md │ ├── manifest.yaml │ └── ui │ ├── .dockerignore │ ├── .gitignore │ ├── .npmrc │ ├── Dockerfile │ ├── angular.json │ ├── build.args │ ├── docker.md │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── favicon.ico │ ├── index.html │ ├── main │ │ ├── actions │ │ │ ├── index.ts │ │ │ ├── vapp.restore.action.component.html │ │ │ ├── vapp.restore.action.component.spec.ts │ │ │ ├── vapp.restore.action.component.ts │ │ │ ├── vm.backup.action.component.html │ │ │ └── vm.backup.action.component.ts │ │ ├── application │ │ │ ├── application.component.html │ │ │ ├── application.component.spec.ts │ │ │ └── application.component.ts │ │ ├── common │ │ │ └── entity-context-extension.interface.ts │ │ ├── create-ip-bindings │ │ │ ├── index.ts │ │ │ ├── ip-bindings.create.wizard.action.component.html │ │ │ ├── ip-bindings.create.wizard.action.component.spec.ts │ │ │ └── ip-bindings.create.wizard.action.component.ts │ │ ├── create-org │ │ │ ├── index.ts │ │ │ ├── org.create.wizard.action.component.html │ │ │ └── org.create.wizard.action.component.ts │ │ ├── create-vapp │ │ │ ├── index.ts │ │ │ ├── vapp.create.wizard.action.component.html │ │ │ └── vapp.create.wizard.action.component.ts │ │ ├── create-vm │ │ │ ├── index.ts │ │ │ ├── vm.create.wizard.action.component.html │ │ │ └── vm.create.wizard.action.component.ts │ │ ├── datacenter-compute │ │ │ ├── datacenter-compute.component.html │ │ │ └── datacenter-compute.component.ts │ │ ├── datacenter-network │ │ │ ├── datacenter-network.component.html │ │ │ └── datacenter-network.component.ts │ │ ├── datacenter-overview │ │ │ ├── datacenter.container.component.html │ │ │ ├── datacenter.container.component.scss │ │ │ └── datacenter.container.component.ts │ │ ├── datacenter-storage │ │ │ ├── datacenter-storage.component.html │ │ │ └── datacenter-storage.component.ts │ │ ├── extension-container-guard.ts │ │ ├── networking │ │ │ ├── networking.component.html │ │ │ └── networking.component.ts │ │ ├── services │ │ │ └── modal-wizard-ext-point.service.ts │ │ ├── showcase-plugin.module.ts │ │ ├── simple │ │ │ ├── simple-nav.module.ts │ │ │ ├── simple.component.html │ │ │ └── simple.component.ts │ │ └── subnav │ │ │ ├── about.component.ts │ │ │ ├── status.component.ts │ │ │ ├── sub-nav.module.ts │ │ │ ├── subnav.component.html │ │ │ ├── subnav.component.scss │ │ │ └── subnav.component.ts │ ├── plugin.main.ts │ ├── public │ │ ├── assets │ │ │ └── monkey.jpg │ │ ├── i18n.json │ │ └── manifest.json │ └── styles.scss │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── vcd-sdk-polyfill.js └── documentation ├── extensibility-platform ├── api-extensibility.md ├── defined-entities │ ├── aws-lambda-behaviors.md │ ├── behaviors-general-concepts.md │ ├── defined-entities-lifecycle.md │ ├── defined-entities-overview.md │ ├── defined-entity-operations.md │ ├── defined-entity-types.md │ ├── defined-interfaces.md │ ├── mqtt-behaviors.md │ ├── no-op-behaviors.md │ ├── rde-access-control.md │ ├── rde-hooks.md │ ├── rde-queries.md │ ├── rde-versions.md │ ├── vro-behaviors.md │ └── webhook-behaviors.md ├── extensibility-platform.md ├── message-broker.md ├── object-extensibility.md ├── object-metadata-2.md └── ui-plugins.md ├── extension-sdk ├── behavior.md ├── elements.md ├── extension-sdk.md ├── lifecycle.md ├── playground.md ├── setup.md └── troubleshoot.md ├── images ├── api_extensibility_1.png ├── api_extensibility_2.png ├── api_extensibility_3.png ├── clone.jpeg ├── compose-recompose.jpeg ├── extensibility-overview.gif ├── instantiateVappTemplate.jpeg ├── object-extensions.jpg ├── overview.png ├── rde-multi-stage-delete-async-1.png ├── rde-multi-stage-delete-async-2.png ├── rde-multi-stage-delete-sync-1.png ├── rde-multi-stage-delete-sync-2.png ├── rde-post-create-hook-entity-states.png ├── rde-sync-deletion-entity-states-1.png ├── rde-sync-deletion-entity-states-2.png ├── rde_concepts.png ├── rde_lifecycle.png ├── reconfigureVm.jpeg ├── relocate.jpeg ├── sample-ext-action.png ├── sample-ext-create-entity.png ├── sample-ext-create-ip-binding.png ├── sample-ext-main-navigation.png ├── sample-ext-network-side-navigation.png ├── sample-ext-networking-navigation.png ├── sample-ext-side-navigation-entity.png ├── sample-ext-top-level-navigation.png ├── under-the-hood.png └── webHook-behavior-slack-example.png ├── introduction.md ├── schemas ├── obj-ext-channel-msg.zip ├── obj-ext-channel-msg │ └── envelopes.yaml ├── obj-ext-phase-payloads.zip └── obj-ext-phase-payloads │ ├── extensibility-core.xsd │ ├── master.xjb │ ├── master.xsd │ ├── network.xsd │ ├── placement.xsd │ ├── preVcApiInvocation.xsd │ └── provisioning.xsd └── toc.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | **To Reproduce** 15 | 16 | Setup to reproduce the behavior: 17 | 1. Relative path to the add-on, element or tool under the Cloud Director Standard Library repository including branch name. 18 | 2. Cloud Director SDK version 19 | - `vcd-ext-shell` version 20 | - OS and version running the `vcd-ext-shell` 21 | 3. Cloud Director version against the add-on or element have been tested. 22 | - Client OS and version 23 | - Client Browser version 24 | 25 | Steps to reproduce the behavior: 26 | - Command Line Interface 27 | 1. Applied the following configuration in Cloud Director 28 | 2. Checkout branch ... 29 | 3. Run `vcd-ext-shell` command 30 | 4. Error message 31 | 5. Applicable logs from: 32 | - `vcd-ext-shell` 33 | - `add-on ISO instaler` 34 | - Cloud Director `/opt/vmware/vcloud/logs/vcloud-container-*.log` 35 | - User Interface 36 | 1. Go to '...' 37 | 2. Click on '....' 38 | 3. Scroll down to '....' 39 | 4. See error 40 | 41 | **Expected behavior** 42 | 43 | A clear and concise description of what you expected to happen. 44 | 45 | **Screenshots** 46 | 47 | If applicable, add screenshots to help explain your problem. 48 | 49 | **Additional context** 50 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Except this file 2 | !.gitignore 3 | 4 | .DS_Store 5 | .idea/ 6 | dist/ 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in cloud-director-solution-standard-library project and our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at oss-coc@vmware.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or 92 | permanent ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, available at 118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at 127 | https://www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /CONTRIBUTING_DCO.md: -------------------------------------------------------------------------------- 1 | # Contributing to cloud-director-extension-standard-library 2 | 3 | We welcome contributions from the community and first want to thank you for taking the time to contribute! 4 | 5 | Please familiarize yourself with the [Code of Conduct](https://github.com/vmware/.github/blob/main/CODE_OF_CONDUCT.md) before contributing. 6 | 7 | Before you start working with cloud-director-extension-standard-library, please read our [Developer Certificate of Origin](https://cla.vmware.com/dco). All contributions to this repository must be signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on as an open-source patch. 8 | 9 | ## Ways to contribute 10 | 11 | We welcome many types of contributions and not all of them need a Pull request. Contributions may include: 12 | 13 | * New features and proposals 14 | * Documentation 15 | * Bug fixes 16 | * Issue Triage 17 | * Answering questions and giving feedback 18 | * Helping to onboard new contributors 19 | * Other related activities 20 | 21 | ## Getting started 22 | 23 | Before you begin, download [Cloud Director Extension SDK](https://developer.vmware.com/sdk/cloud-director-extension) and follow the [Setup Development Environment](https://developer.vmware.com/sdk/cloud-director-extension) guide. This step will give you all the tools you need to build, test, run and package solutions add-ons or their component elements. 24 | 25 | ### Enhance or fix issue for existing solution add-on 26 | 1. Open your terminal and navigate to add-on folder. 27 | 2. Execute `vcd-ext-shell` 28 | 3. Follow [Understanding Solution Add-On Lifecycle and Understanding Solution Add-On Elements](https://developer.vmware.com/sdk/cloud-director-extension) documentation. 29 | 30 | ### Create new solution add-on 31 | 1. Open your terminal and navigate to repository add-on folder. 32 | 2. Execute `vcd-ext-shell` 33 | 3. Follow [Building Simple Solution Add-On](https://developer.vmware.com/sdk/cloud-director-extension) documentation. 34 | 35 | 36 | ### Enhance or fix issue for existing standalone element 37 | 1. Open your terminal and navigate to your working directory of choice. 38 | 2. Execute `vcd-ext-shell` 39 | 3. Create new solution add-on by follow [Building Simple Solution Add-On](https://developer.vmware.com/sdk/cloud-director-extension) documentation. 40 | > Note the folder name should follow the *Kebab case* naming convention (ex. word-word-work). 41 | 4. Include element into your add-on via `vcd-ext-shell # element add` command 42 | 5. Work on the enhancement or issue. 43 | 6. Extract the element segment from your add-on `manifest.yaml` and replace it in the `manifest.yaml` into repository element subject of change. 44 | 7. Replace the repository element source folder with the one from the add-on. 45 | 46 | 47 | ### Create new standalone element 48 | 1. Open your terminal and navigate to your working directory of choice. 49 | 2. Execute `vcd-ext-shell` 50 | 3. Create new solution add-on by follow [Building Simple Solution Add-On](https://developer.vmware.com/sdk/cloud-director-extension) documentation 51 | 4. Work on the element. 52 | 5. Create folder under repository `element` named after the element use case. 53 | > Note the folder name should follow the *Kebab case* naming convention (ex. word-word-work). 54 | 6. Extract the element segment from your add-on `manifest.yaml` and save it in the `manifest.yaml` into the new element in the repository. 55 | 7. Copy the repository element source folder into the new element in the repository. 56 | 57 | ## Contribution Flow 58 | 59 | This is a rough outline of what a contributor's workflow looks like: 60 | 61 | * Make a fork of the repository within your GitHub account 62 | * Create a topic branch in your fork from where you want to base your work 63 | * Make commits of logical units 64 | * Make sure your commit messages are with the proper format, quality and descriptiveness (see below) 65 | * Push your changes to the topic branch in your fork 66 | * Create a pull request containing that commit 67 | 68 | We follow the GitHub workflow, and you can find more details on the [GitHub flow documentation](https://docs.github.com/en/get-started/quickstart/github-flow). 69 | 70 | ### Pull Request Checklist 71 | 72 | Before submitting your pull request, we advise you to use the following: 73 | 74 | 1. Check if your code changes will pass both code linting checks and unit tests. 75 | 2. Ensure your commit messages are descriptive. We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). Be sure to include any related GitHub issue references in the commit message. See [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) for referencing issues and commits. 76 | 3. Check the commits and commits messages and ensure they are free from typos. 77 | 78 | ## Reporting Bugs and Creating Issues 79 | 80 | Open GitHub issue. 81 | 82 | ## Ask for Help 83 | 84 | The best way to reach us with a question when contributing is to open an original GitHub issue. 85 | 86 | ## Additional Resources 87 | 88 | * [Cloud Director Extension SDK](https://developer.vmware.com/sdk/cloud-director-extension) 89 | * [Service Provider Admin Guide for Solution Add-Ons](https://gitlab.eng.vmware.com/cloud-director-solutions/care-package-go-poc/-/tree/topic/tsimchev/STAR-6754/docs2#:~:text=Read%20the%20Service%20Provider%20Admin%20Guide%20for%20Solution%20Add%2DOns) 90 | 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are 3 | met: 4 | 5 | 1. Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided 11 | with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2023 VMware, Inc. 2 | 3 | This product is licensed to you under the BSD 2 clause (the "License"). You may not use this product except in compliance with the License. 4 | 5 | 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloud Director Extension Standard Library 2 | The VMware `vcd-ext-shell` command line interface allows vendors to use starter templates for creating solution add-ons and add-on elements. The source of the available templates is located in this Git repository. 3 | 4 | All components of this repository are validated by VMware or its partners against particular Cloud Director version. 5 | 6 | ## Documentation 7 | 8 | Get familiar with Cloud Director Solution Add-Ons. 9 | * [Cloud Director Extensibility Platform](documentation/extensibility-platform/extensibility-platform.md) 10 | * [Cloud Director Extension SDK](https://developer.vmware.com/sdk/cloud-director-extension) 11 | * [Service Provider Admin Guide for Solution Add-Ons](https://docs.vmware.com/en/VMware-Cloud-Director/10.4/VMware-Cloud-Director-Service-Provider-Admin-Portal-Guide/GUID-4F12C8F7-7CD3-44E8-9711-A5F43F8DCEB5.html) 12 | 13 | ## Repository Filesystem Layout 14 | Every branch of the repository contains two folders at the root level. 15 | - `add-on` containing a solution add-on template per sub-folder. 16 | - `element/` containing a solution add-on element template per sub-folder. 17 | 18 | > **Important** 19 | > The folder name for every template must be named after the major use case it delivers following the Kebab case naming convention (ex. work-word-word). 20 | 21 | Both `add-on` and `element/` folders might contain folders starting with `.`. These folders contain helper tools for building, testing and documenting an add-on or its elements. 22 | 23 | Filesystem Layout: 24 | ```shell 25 | 26 | - add-on/ 27 | - add-on/. 28 | - element// 29 | - element//. 30 | ``` 31 | 32 | The templates and tools are further organized by branches. The names of the branches follow `cloud-director-X.X.X` pattern and claim that all add-ons and elements templates in that branch have been validated against an environment running Cloud Director version `X.X.X`. The `main` branch contains alpha versions of the add-ons and elements for the next upcoming release. 33 | 34 | > **Important** 35 | Upon receiving a complaint from a provider running a newer version of Cloud Director `X.Y.Z`, a vendor who has bootstrapped his add-on with a minimal version of Cloud Director `X.X.X` using a starter template from `cloud-director-X.X.X` can review directly the fix made by VMware or its partners on the starter template by comparing the branches `cloud-director-X.X.X` and `cloud-director-X.Y.Z` and find out the exact line causing the issue. 36 | 37 | Branching organization: 38 | ```shell 39 | main 40 | cloud-director-10.4.1 41 | ... 42 | cloud-director-10.5 43 | ``` 44 | 45 | ## Contributing 46 | 47 | The cloud-director-extension-standard-library project team welcomes contributions from the community. Before you start working with cloud-director-extension-standard-library, please 48 | read our [Developer Certificate of Origin](https://cla.vmware.com/dco). All contributions to this repository must be 49 | signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on 50 | as an open-source patch. For more detailed information, refer to [CONTRIBUTING_DCO.md](CONTRIBUTING_DCO.md). 51 | 52 | ## License 53 | BSD-2-Clause 54 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | reports 3 | screenshots 4 | videos -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/README.md: -------------------------------------------------------------------------------- 1 | # Cypress E2E Testing 2 | ## Setup 3 | Install all NPM dependencies in `add-on/multi-instance-skeleton/.e2e`. 4 | ```bash 5 | npm i 6 | ``` 7 | ## Development of E2E Tests 8 | ```bash 9 | export set VCD_URL=https://my.vcd.cell 10 | export set VCD_PASSWORD=secret 11 | npm run cypress:open 12 | ``` 13 | ## Run E2E Tests 14 | To run the E2E tests simply run 15 | ```bash 16 | export set VCD_URL=https://my.vcd.cell 17 | export set VCD_PASSWORD=secret 18 | npm run e2e 19 | ``` 20 | Reports, screenshots and video will be generated in the E2E testing folder after the run. 21 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "testFiles": "**/*.{feature,features}", 3 | "reporter": "junit", 4 | "reporterOptions": { 5 | "mochaFile": "reports/report_[hash].xml", 6 | "toConsole": false, 7 | "outputs": true, 8 | "antMode": false, 9 | "jenkinsMode": false 10 | } 11 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/add-on/multi-instance-skeleton/.e2e/cypress/fixtures/.gitkeep -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/integration/example.feature: -------------------------------------------------------------------------------- 1 | Feature: Example E2E Scenarios 2 | Background: 3 | Given I logged into to VMware Cloud Director Portal 4 | | Username | test | 5 | | Password | test | 6 | 7 | Scenario: Example Scenario 8 | Given I am logged in to VMware Cloud Director Portal 9 | Then I navigate to XYZ 10 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/integration/example/example.stepdefs.ts: -------------------------------------------------------------------------------- 1 | import { Given, Then } from "cypress-cucumber-preprocessor/steps"; 2 | 3 | export interface DataTable { 4 | rawTable: any[][]; 5 | } 6 | 7 | Given("I logged into to VMware Cloud Director Portal", (dataTable: DataTable) => { 8 | cy.viewport(1920, 1080); 9 | 10 | const username: string = dataTable.rawTable[0][1]; 11 | const password: string = dataTable.rawTable[1][1]; 12 | 13 | cy.visit(`${Cypress.env("VCD_URL")}/provider`); 14 | 15 | fillLoginForm(username, password); 16 | }); 17 | 18 | Given("I am logged in to VMware Cloud Director Portal", (dataTable: DataTable) => { 19 | // Check you are logged in to VMware Cloud Director Portal 20 | }); 21 | 22 | Then("I navigate to XYZ", () => { 23 | // Navigate to page XYZ 24 | }); 25 | 26 | function fillLoginForm(username: string, password: string) { 27 | cy.get("input[id='usernameInput']", {timeout: 60000}).should("be.visible"); 28 | cy.get("input[id='usernameInput']").type(username); 29 | cy.get("input[id='passwordInput']").type(password); 30 | cy.get("button[id='loginButton']").click(); 31 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | const preprocess = require("./preprocess"); 2 | 3 | module.exports = (on, config) => { 4 | on("file:preprocessor", preprocess); 5 | 6 | config.env.VCD_URL = process.env.VCD_URL; 7 | config.env.VCD_PASSWORD = process.env.VCD_PASSWORD; 8 | // config.chromeWebSecurity = false; 9 | 10 | return config; 11 | }; 12 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/plugins/preprocess.js: -------------------------------------------------------------------------------- 1 | const webpack = require("@cypress/webpack-preprocessor"); 2 | 3 | const options = { 4 | webpackOptions: require("../webpack.config.js"), 5 | }; 6 | 7 | module.exports = webpack(options); 8 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | 27 | // Fix the error trown by vcd common components due their usag of systeme js for some non-testing purposes 28 | window["System"] = { 29 | registry: { 30 | get: () => true 31 | } 32 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | 22 | Cypress.on('uncaught:exception', (err, runnable) => { 23 | // we expect a 3rd party library error with message 'list not defined' 24 | // and don't want to fail the test so we return false 25 | // if (err.message.includes("Cannot read property 'length' of undefined")) { 26 | // return false; 27 | // } 28 | // we still want to ensure there are no other unexpected 29 | // errors, so we let them fail the test 30 | }) -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["cypress"], 4 | "target": "es2015", 5 | "module": "es2015", 6 | "moduleResolution": "node", 7 | "lib": ["es2015", "dom"], 8 | "sourceMap": true, 9 | "declaration": false, 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "removeComments": false, 13 | "noImplicitAny": true, 14 | "resolveJsonModule": true, 15 | "allowSyntheticDefaultImports": true, 16 | "skipLibCheck": true, 17 | "downlevelIteration": true, 18 | "baseUrl": "./" 19 | }, 20 | "exclude": [ 21 | "node_modules" 22 | ] 23 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/cypress/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | 3 | module.exports = { 4 | resolve: { 5 | extensions: [".ts", ".js"], 6 | fallback: { 7 | "path": require.resolve("path-browserify") 8 | } 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.ts$/, 14 | exclude: [/node_modules/], 15 | use: [ 16 | { 17 | loader: "ts-loader", 18 | options: { 19 | transpileOnly: true, 20 | }, 21 | }, 22 | ], 23 | }, 24 | { 25 | test: /\.feature$/, 26 | use: [ 27 | { 28 | loader: "cypress-cucumber-preprocessor/loader", 29 | }, 30 | ], 31 | }, 32 | ], 33 | }, 34 | plugins: [ 35 | new webpack.ProvidePlugin({ 36 | process: 'process/browser', 37 | }), 38 | ], 39 | }; 40 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "cypress:open": "cypress open", 8 | "cy:verify": "cypress verify", 9 | "e2e": "cypress run" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@cypress/webpack-preprocessor": "5.8.0", 15 | "@types/cypress-cucumber-preprocessor": "4.0.0", 16 | "@vcd/ui-components": "2.0.0-dev.21", 17 | "cypress": "7.3.0", 18 | "cypress-cucumber-preprocessor": "4.1.0", 19 | "mocha-junit-reporter": "2.0.0", 20 | "path-browserify": "1.0.1", 21 | "process": "0.11.10", 22 | "ts-loader": "9.1.2", 23 | "typescript": "3.6.4", 24 | "webpack": "5.37.0", 25 | "xml2js": "0.4.23" 26 | }, 27 | "cypress-cucumber-preprocessor": { 28 | "nonGlobalStepDefinitions": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.ui-emulator/.env/environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "production": false, 3 | "branding": { 4 | "headerTitle": "VMware Cloud Director" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.ui-emulator/.env/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api/*": { 3 | "target": "https://", 4 | "secure": false, 5 | "logLevel": "debug", 6 | "changeOrigin": true 7 | }, 8 | "/cloudapi/*": { 9 | "target": "https://", 10 | "secure": false, 11 | "logLevel": "debug", 12 | "changeOrigin": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.ui-emulator/.gitignore: -------------------------------------------------------------------------------- 1 | .env/environment.runtime.json 2 | .env/proxy.conf.runtime.json 3 | angular.json 4 | tsconfig.emulator.json 5 | 6 | node_modules -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.ui-emulator/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.ui-emulator/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginServeList": [ 3 | "ui" 4 | ], 5 | "vcdHost": "https://vcd.cell.com", 6 | "jwt": "eyJhbGciOiJSUzI1NiJ9...", 7 | "authtoken": "c14c79...", 8 | "scope": "service-provider", 9 | "orgName": "System", 10 | "orgId": "", 11 | "emulatorConfig": { 12 | "relativePath": "./../.ui-emulator", 13 | "relativePathToUIPlugins": "./..", 14 | "watch": true, 15 | "ignoreFilesRegex": null, 16 | "devServerArguments": [ 17 | "--host", 18 | "0.0.0.0", 19 | "--port", 20 | "4200" 21 | ] 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/.ui-emulator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emulator", 3 | "version": "1.0.0", 4 | "description": "This project is used to configure and serve @vcd/ui-emulator", 5 | "author": "VMware Cloud Directory Extensions CLI", 6 | "private": true, 7 | "engines": { 8 | "node": "14.20.0" 9 | }, 10 | "license": "BSD-2-Clause", 11 | "dependencies": { 12 | "@vcd/ui-emulator": "15.0.7" 13 | }, 14 | "scripts": { 15 | "start": "node ./node_modules/@vcd/ui-emulator/utils/lib/emulator-start.js ./config.json" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/Dockerfile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------ 2 | # Copyright 2023 VMware, Inc. 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | # ------------------------------------------------------ 5 | # Build element 6 | # ------------------------------------------------------ 7 | ARG DOCKER_REPO 8 | 9 | FROM $DOCKER_REPO/golang:1.19.2-bullseye AS GOLANG 10 | 11 | ADD . /opt/src 12 | WORKDIR /opt/src 13 | RUN env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o dist/linux . 14 | RUN env GOARCH=amd64 GOOS=darwin go build -ldflags="-s -w" -o dist/darwin . 15 | RUN env GOARCH=amd64 GOOS=windows go build -ldflags="-s -w" -o dist/windows.exe . 16 | 17 | 18 | # ------------------------------------------------------ 19 | # Output element artifacts 20 | # ------------------------------------------------------ 21 | FROM scratch AS export 22 | COPY --from=GOLANG /opt/src/dist ./ 23 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/addonPreCreateMock.json: -------------------------------------------------------------------------------- 1 | {"cloudDirector":{"accessToken":"eyJhbGc..fJQ","apiVersion":"38.0","certificates":"-----BEGIN CERTIFICATE-----\n..==\n-----END CERTIFICATE-----","host":"cloud.director.local","port":443,"productVersion":"10.5.0.22007244","session":{"org":{"id":"urn:vcloud:org:a93c9db9-7471-3192-8d09-a8f7eeda85f9","name":"System"},"role":{"id":"urn:vcloud:role:67e119b7-083b-349e-8dfd-6cf0c19b83cf","name":"System Administrator"},"user":{"id":"urn:vcloud:user:037db74c-a21e-4e9d-9d63-4e68eb96344c","name":"administrator"}},"thumbprint":"F9:28:85:C0:8C:41:68:88:9F:34:1A:49:0C:11:19:B5:90:7B:86:3D:A2:4B:D8:AC:6F:88:73:6D:2B:CF:DB:77"},"dataCenter":{"capabilities":[],"computePolicies":[],"id":"urn:vcloud:vdc:5476cf4d-7fd1-4681-a351-89e6e54ddbcf","isDefault":true,"name":"SlzOrgVdc-2023-07-19-21-06-48.189","networkManagerId":"","networks":[{"capabilities":[],"id":"urn:vcloud:network:40867c60-12c8-4780-914f-ba2ff7698dae","isDefault":true,"name":"SlzOrgVdcNetwork"}],"storagePolicies":[{"capabilities":[],"id":"urn:vcloud:vdcstorageProfile:131519d8-731c-417e-8235-67dcab6c1fbe","isDefault":true,"name":"*"}]},"element":"","event":"PreCreate","execution":{"invocationId":"","owner":"","taskId":""},"logging":{"debug":true,"format":"text","trace":true},"manifest":{"capabilities":[],"description":"This solution add-on skeleton represents a typical multi-instance solution.","elements":[{"description":"User interface","name":"ui","spec":{"publish":{"provider":true}},"triggers":null,"type":"ui-plugin"},{"description":"Business Objects Schemas","name":"db-schemas","spec":null,"triggers":null,"type":"defined-entity"},{"description":"Business objects rights","name":"rights","spec":{"description":"This rights bundle is created by Skeleton Add-On","name":"Skeleton-{{ instance `name` }}","publish":{"solutionLandingZone":true},"rights":["urn:vcloud:type:vmware:skeleton_database_entity","urn:vcloud:type:vmware:skeleton_database_entity:admin"]},"triggers":null,"type":"rights-bundle"},{"description":"Business objects role","name":"role","spec":{"description":"This role is created by Skeleton Add-On","global":false,"name":"Skeleton-{{ instance `name` }}","publish":{"solutionLandingZone":true},"rights":["urn:vcloud:type:vmware:skeleton_database_entity","urn:vcloud:type:vmware:skeleton_database_entity:admin"],"systemScope":false},"triggers":null,"type":"role"},{"description":"User interacting with Cloud Director from backend","name":"cloud-director-user","spec":{"description":"User sva.{{ property `provider-business-scope-property` }} with role {{ property `role.name` }}","fullName":"Skeleton backend system account","password":"{{ property `password` }}","roleName":"{{ property `role.name` }}","systemScope":false,"username":"sva.{{ property `provider-business-scope-property` }}"},"triggers":[{"action":"actions/multipurposeaction","event":"PostCreate","timeout":30}],"type":"user"},{"description":"Backend processor of business objects","name":"backend-appliance","spec":{"hardwareCustomization":{"memorySize":512,"numberOfCpus":1},"networks":[{"capabilities":[],"primary":true}],"ovfProperties":[{"key":"provider-business-scope-property","value":"{{ property `provider-business-scope-property` }}"},{"key":"cloud-director-host","value":"{{ vcd `host` }}"},{"key":"cloud-director-host-certificates","value":"{{ vcd `certificates` }}"},{"key":"api-token","value":"{{ property `api-token` }}"}],"readyCondition":null,"timeoutMinutes":10},"triggers":null,"type":"vapp"}],"friendlyName":"Skeleton Started","inputs":[{"default":null,"delete":false,"description":"This is some Business Scope Property required for every add-on instance","isArray":false,"maxLength":24,"maxValue":null,"minLength":2,"minValue":null,"name":"provider-business-scope-property","required":true,"secure":false,"shared":false,"title":"Some Business Scope Property","type":"String","validation":"","values":null,"view":""},{"default":null,"delete":false,"description":"The password of the new local Cloud Director account about to be created and associated with a business scope","isArray":false,"maxLength":16,"maxValue":null,"minLength":8,"minValue":null,"name":"password","required":true,"secure":true,"shared":false,"title":"Password","type":"String","validation":"","values":null,"view":""},{"default":null,"delete":true,"description":"Why do you delete this Solution instance","isArray":false,"maxLength":256,"maxValue":null,"minLength":5,"minValue":null,"name":"justification","required":false,"secure":false,"shared":false,"title":"Justification","type":"String","validation":"","values":null,"view":""}],"metadata":{},"name":"skeleton","operations":[],"policies":{"supportsMultipleInstances":true,"tenantScoped":false,"upgradesFrom":""},"resources":[],"runtime":{"sdkVersion":""},"tags":[],"triggers":[{"action":"actions/multipurposeaction","event":"PreCreate","timeout":30},{"action":"actions/multipurposeaction","event":"PostDelete","timeout":30}],"vcdVersion":"10.4.1","vendor":"vmware","version":"1.0.0"},"operation":"CREATE","organization":{"href":"","id":"urn:vcloud:org:dc15825e-d2f3-4fdf-914d-b75fdfe4339b","name":"SlzOrg","type":""},"properties":{"password":"secret","provider-business-scope-property":"skeleton01"},"runtime":{"sdkVersion":"1.1.0.7077883","goVersion":"go1.20.5","vcdVersion":"10.5.0.22007244","environment":"Development"},"transaction":{}} -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/build.args: -------------------------------------------------------------------------------- 1 | DOCKER_REPO=docker.io -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/go.mod: -------------------------------------------------------------------------------- 1 | // Copyright 2023 VMware, Inc. 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | 4 | module example.com/hook 5 | 6 | go 1.19 7 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 VMware, Inc. 2 | // SPDX-License-Identifier: BSD-2-Clause 3 | package main 4 | 5 | import ( 6 | "bufio" 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "os" 11 | "time" 12 | ) 13 | 14 | type Context struct { 15 | Element string `json:"element"` 16 | Event string `json:"event"` 17 | Operation string `json:"operation"` 18 | Properties map[string]any `json:"properties"` 19 | CloudDirector map[string]any `json:"cloudDirector"` 20 | } 21 | 22 | type TransactionLog map[string]any 23 | 24 | type Task struct { 25 | Operation string `json:"operation"` 26 | } 27 | 28 | type Log struct { 29 | Level string `json:"level"` 30 | Msg string `json:"msg"` 31 | } 32 | 33 | type Property struct { 34 | Name string `json:"name"` 35 | Value any `json:"value"` 36 | Secure bool `json:"secure"` 37 | } 38 | 39 | type OutputTask struct { 40 | Task Task `json:"task"` 41 | } 42 | 43 | type OutputLog struct { 44 | Log Log `json:"log"` 45 | } 46 | 47 | type OutputTransactionLog struct { 48 | Transaction TransactionLog `json:"transaction"` 49 | } 50 | 51 | type OutputProperty struct { 52 | Property Property `json:"property"` 53 | } 54 | 55 | func writeContext(contexts ...any) (err error) { 56 | for _, context := range contexts { 57 | var ctxJson []byte 58 | if ctxJson, err = json.Marshal(context); err == nil { 59 | 60 | var outputKey string 61 | 62 | switch value := context.(type) { 63 | case Task: 64 | outputKey = "task" 65 | case Log: 66 | outputKey = "log" 67 | case TransactionLog: 68 | outputKey = "transaction" 69 | case Property: 70 | outputKey = "property" 71 | default: 72 | return fmt.Errorf("unknown type %v", value) 73 | } 74 | 75 | _, err = fmt.Printf("%s:%s\n", outputKey, string(ctxJson)) 76 | } 77 | if err != nil { 78 | break 79 | } 80 | } 81 | return 82 | } 83 | 84 | func readContext() Context { 85 | scanner := bufio.NewScanner(os.Stdin) 86 | if !scanner.Scan() { 87 | exitIfErrorExists(errors.New("no standard input"), "error reading from standard input") 88 | } 89 | inputJson := scanner.Text() 90 | 91 | // DEVELOPMENT ONLY! Print standard ctx for examination. 92 | // Note all secrets will be visible in the standard output log. 93 | fmt.Println(inputJson) 94 | 95 | ctx := Context{} 96 | err := json.Unmarshal([]byte(inputJson), &ctx) 97 | exitIfErrorExists(err, "error reading JSON from standard ctx") 98 | return ctx 99 | } 100 | 101 | func exitIfErrorExists(err error, message string) { 102 | if err != nil { 103 | fmt.Fprintf(os.Stderr, "%s: %v", message, err) 104 | os.Exit(1) 105 | } 106 | } 107 | 108 | const eventPreCreate = "PreCreate" 109 | const eventPostCreate = "PostCreate" 110 | const eventPostDelete = "PostDelete" 111 | const eventOnOperation = "OnOperation" 112 | const elementNone = "" 113 | const elementCloudDirectorUser = "cloud-director-user" 114 | 115 | const operationUpdateCloudDirectorCertificate = "UPDATECLOUDDIRECTORCERTIFICATE" 116 | const operationUpdateTrustedStore = "UPDATETRUSTEDSTORE" 117 | 118 | // This is the multipurpose action handler that can be referenced multiple times 119 | // in the manifest.yaml under triggers and element triggers sections. 120 | // 121 | // Utilize this pattern for code simplicity, reduced source code size, and improved usability. 122 | func main() { 123 | // Write into action standard out put, not visible to the user, used for debugging purposes 124 | fmt.Println("Solution add-on trigger has been called") 125 | 126 | // Read context from the standard input stream 127 | ctx := readContext() 128 | 129 | // Example: Handle solution add-on global triggers 130 | if ctx.Element == elementNone && ctx.Event == eventPreCreate { 131 | // Example: Forward output into user log 132 | writeContext( 133 | Log{Level: "info", Msg: "Executing solution pre-create event"}, 134 | ) 135 | 136 | // Example: Write state in transaction log 137 | writeContext( 138 | TransactionLog{"Atomic Operation A": "Begin"}, 139 | ) 140 | 141 | // Example: Beginning of long-running task 142 | writeContext( 143 | Task{Operation: "Perform time consuming operation A"}, 144 | ) 145 | 146 | // Example: Long-running tasks is executing 147 | time.Sleep(time.Second) 148 | 149 | // Example: Write state in transaction log 150 | writeContext( 151 | TransactionLog{"Atomic Operation A": "Completed"}, 152 | ) 153 | 154 | // Example: Completing the previous long-running task and beginning of a new long-running task. 155 | // Task B will be completed automatically on action completion. 156 | writeContext( 157 | Task{Operation: "Perform time consuming operation B"}, 158 | ) 159 | 160 | // Example: set or update multiple solution add-on global properties at once 161 | writeContext( 162 | Property{Name: "exampleKeyMap", Value: map[string]any{"k1": "v1", "k2": "v2"}, Secure: false}, 163 | Property{Name: "exampleKeyArrayAny", Value: []any{1, "v", true, map[string]bool{"k": true}}, Secure: false}, 164 | ) 165 | } 166 | 167 | if ctx.Element == elementNone && ctx.Event == eventPostDelete { 168 | writeContext( 169 | Log{Level: "info", Msg: "Executing solution post-delete event"}, 170 | ) 171 | } 172 | 173 | // Example: Handle solution add-on trigger for specific element 174 | if ctx.Element == elementCloudDirectorUser && ctx.Event == eventPostCreate { 175 | writeContext( 176 | Log{Level: "info", Msg: "Executing element user pre-create event"}, 177 | ) 178 | 179 | // Example: Set or update a solution add-on global property 180 | writeContext( 181 | Property{Name: "api-token", Value: "XXX API Token XXX", Secure: true}, 182 | ) 183 | } 184 | 185 | if ctx.Element == elementNone && ctx.Event == eventOnOperation { 186 | 187 | if ctx.Operation == operationUpdateCloudDirectorCertificate { 188 | vcdCerts := ctx.CloudDirector["certificates"] 189 | 190 | writeContext( 191 | Log{Level: "info", Msg: "Adding the Cloud Director certificate into trusted store"}, 192 | Log{Level: "debug", Msg: fmt.Sprintf("Cloud Director Certificate: %v", vcdCerts)}, 193 | ) 194 | 195 | } else if ctx.Operation == operationUpdateTrustedStore { 196 | cert := ctx.Properties["certificate"] 197 | writeContext( 198 | Log{Level: "info", Msg: "Adding a certificate into trusted store"}, 199 | Log{Level: "debug", Msg: fmt.Sprintf("Certificate: %v", cert)}, 200 | ) 201 | } else { 202 | panic(fmt.Errorf("Unrecognized operation: %s", ctx.Operation)) 203 | } 204 | } 205 | 206 | fmt.Println("Solution add-on trigger terminated") 207 | } 208 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/actions/multipurposeaction/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "log" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | //go:embed addonPreCreateMock.json 11 | var inputContext []byte 12 | 13 | func mock(t *testing.T, input []byte) (funcDefer func(), err error) { 14 | t.Helper() 15 | var file *os.File 16 | file, err = os.CreateTemp(os.TempDir(), "test") 17 | if _, err = file.Write(input); err == nil { 18 | if _, err = file.Seek(0, 0); err == nil { 19 | oldOsStdin := os.Stdin 20 | os.Stdin = file 21 | 22 | funcDefer = func() { 23 | os.Stdin = oldOsStdin 24 | os.Remove(file.Name()) 25 | } 26 | } 27 | } 28 | return 29 | } 30 | 31 | func Test(t *testing.T) { 32 | deferFunc, err := mock(t, inputContext) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | defer deferFunc() 37 | main() 38 | } 39 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/backend-appliance/Dockerfile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------ 2 | # Copyright 2023 VMware, Inc. 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | # ------------------------------------------------------ 5 | # Output element artifacts 6 | # ------------------------------------------------------ 7 | FROM scratch AS export 8 | COPY skeleton* . 9 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/backend-appliance/skeleton.mf: -------------------------------------------------------------------------------- 1 | SHA256(skeleton.ovf)= a44fb4bd39299642153840a8bac1f27143d24527249c62b3de2f0d42173e966b 2 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/backend-appliance/skeleton.ovf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Virtual disk information 6 | 7 | 8 | The list of logical networks 9 | 10 | The nat network 11 | 12 | 13 | 14 | Skeleton Virtual Machine 15 | skeleton-vm 16 | 17 | A human-readable annotation 18 | A small footprint applaince for testing purposes 19 | 20 | 21 | The kind of installed guest operating system 22 | 23 | 24 | Virtual hardware requirements 25 | 26 | Virtual Hardware Family 27 | 0 28 | void-vm 29 | vmx-18 30 | 31 | 32 | hertz * 10^6 33 | Number of Virtual CPUs 34 | 1 virtual CPU(s) 35 | 1 36 | 3 37 | 1 38 | 39 | 40 | byte * 2^20 41 | Memory Size 42 | 256MB of memory 43 | 2 44 | 4 45 | 256 46 | 47 | 48 | 1 49 | IDE Controller 50 | ideController1 51 | 3 52 | 5 53 | 54 | 55 | 0 56 | false 57 | cdrom0 58 | 4 59 | 3 60 | 15 61 | 62 | 63 | 64 | 1 65 | true 66 | nat 67 | PCNet32 ethernet adapter on "nat" 68 | ethernet0 69 | 5 70 | PCNet32 71 | 10 72 | 73 | 74 | 75 | 76 | false 77 | sound 78 | 6 79 | vmware.soundcard.ensoniq1371 80 | 1 81 | 82 | 83 | 84 | false 85 | video 86 | 7 87 | 24 88 | 89 | 90 | 91 | false 92 | vmci 93 | 8 94 | vmware.vmci 95 | 1 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | VMware Skeleton Applaince 107 | VMware Skeleton Applaince 108 | VMware Inc. 109 | 1.0 110 | 1.0.0 111 | http://www.vmware.com 112 | Appliance Customization Fields 113 | 114 | 115 | This value can be accessed inside the appliance via 'vmtoolsd --cmd "info-get guestinfo.ovfenv"' 116 | 117 | 118 | 119 | This value can be accessed inside the appliance via 'vmtoolsd --cmd "info-get guestinfo.ovfenv"' 120 | 121 | 122 | 123 | This value can be accessed inside the appliance via 'vmtoolsd --cmd "info-get guestinfo.ovfenv"' 124 | 125 | 126 | 127 | This value can be accessed inside the appliance via 'vmtoolsd --cmd "info-get guestinfo.ovfenv"' 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/Dockerfile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------ 2 | # Copyright 2023 VMware, Inc. 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | # ------------------------------------------------------ 5 | # Build element 6 | # ------------------------------------------------------ 7 | ARG DOCKER_REPO 8 | 9 | FROM $DOCKER_REPO/node:16-slim as NODE 10 | WORKDIR /usr/app 11 | ADD . . 12 | RUN npm ci && npm run build 13 | 14 | # ------------------------------------------------------ 15 | # Output element artifacts 16 | # ------------------------------------------------------ 17 | FROM scratch AS export 18 | COPY --from=NODE /usr/app/lib ./ 19 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/README.md: -------------------------------------------------------------------------------- 1 | # Network Manager for VMware Cloud on AWS 2 | 3 | ## Network Manager 4 | 5 | Network manager type has the following properties: 6 | 7 | | Name | Type | Description | Mandatory | 8 | |---|---|---|---| 9 | | host | string | Hostname of IP address of the NSX manager. | Yes | 10 | | port | number | HTTPS port of the NSX manager. Default is 443. | No | 11 | | username | string | NSX manager user with sufficient privileges to perform CRUD operations on solutions Tier1 gateway. | Yes | 12 | | password | string | Password for the NSX manager user. | Yes | 13 | | proxyHost | string | Hostname or IP address of a proxy used to connect to the NSX manager. | No | 14 | | proxyPort | number | Port of the NSX manager proxy. | No | 15 | | proxyUsername | string | Username of the NSX manager proxy. | No | 16 | | proxyPassword | string | Password of the NSX manager proxy. | No | 17 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/build.args: -------------------------------------------------------------------------------- 1 | DOCKER_REPO=docker.io -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skeleton_types", 3 | "version": "1.0.0", 4 | "vendor": "vmware", 5 | "description": "", 6 | "keywords": [ 7 | "vcd", 8 | "extension" 9 | ], 10 | "scripts": { 11 | "build": "vcd-ext-ts-rde build --minify" 12 | }, 13 | "author": "vCloud Directory Extensions CLI", 14 | "license": "BSD-2-Clause", 15 | "dependencies": { 16 | "@vcd-ext/rde-faas-library": "^1.0.3" 17 | }, 18 | "devDependencies": { 19 | "@vcd-ext/ts-rde": "^1.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/src/SkeletonDatabaseEntity.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 VMware, Inc. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | */ 5 | import * as tsrde from "@vcd-ext/ts-rde"; 6 | 7 | @tsrde.DefinedEntityType 8 | export class SkeletonDatabaseEntity { 9 | 10 | plainText: string; 11 | optionalNumber?: number 12 | 13 | /** 14 | * Only behaviors associated with instances of SkeletonDatabaseEntity will 15 | * receive this value encrypted in their execution context. 16 | * 17 | * Note, such value can be POST and PUT but never GET via regular API calls. 18 | */ 19 | @tsrde.Secure 20 | @tsrde.Private 21 | encryptedText: string; 22 | 23 | /** 24 | * Defines a behavior "revealBehaviorExecutionContext" to SkeletonDatabaseEntity interface 25 | * of type built-in provider ONLY FaaS 26 | * @param executionJustificationText 27 | */ 28 | @tsrde.FaaS 29 | @tsrde.AccessControl("urn:vcloud:accessLevel:FullControl") 30 | RevealBehaviorExecutionContext(executionJustificationText?: string) {} 31 | } 32 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/src/behaviors/SkeletonDatabaseEntityRevealBehaviorExecutionContext.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 VMware, Inc. 3 | * SPDX-License-Identifier: BSD-2-Clause 4 | */ 5 | import * as tsrde from "@vcd-ext/ts-rde"; 6 | 7 | /** 8 | * Implements revealBehaviorExecutionContext" from SkeletonDatabaseEntity interface 9 | * @param vcdContext 10 | */ 11 | 12 | export default async function (vcdContext: tsrde.VcdContext): Promise { 13 | console.log("*** revealBehaviorExecutionContext() ***") 14 | /** 15 | * Use with caution. Printing this information in logs will also print 16 | * the encrypted fields and access tokens. 17 | */ 18 | console.log(`entityId=${vcdContext.entityId}`) 19 | console.log(`hostname=${vcdContext.hostname}`) 20 | console.log(`port=${vcdContext.port}`) 21 | console.log(`actAsToken=${vcdContext.actAsToken}`) 22 | console.log(`entity=${JSON.stringify(vcdContext.entity || {}, null, 2)}`) 23 | console.log(`arguments=${JSON.stringify(vcdContext.arguments || {}, null, 2)}`) 24 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/db-schemas/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "ES6", 5 | "outDir": "build", 6 | "noImplicitReturns": true, 7 | "strictNullChecks": false, 8 | "noUnusedLocals": false, 9 | "declaration": true, 10 | "emitDeclarationOnly": false, 11 | "sourceMap": false, 12 | "experimentalDecorators": true, 13 | "esModuleInterop": true, 14 | "noImplicitUseStrict": true, 15 | "importHelpers": false, 16 | "noEmitHelpers": true 17 | }, 18 | "include": ["./src/**/*"], 19 | } 20 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/add-on/multi-instance-skeleton/icon.png -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/manifest.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 VMware, Inc. 2 | # SPDX-License-Identifier: BSD-2-Clause 3 | --- 4 | name: skeleton 5 | vendor: vmware 6 | version: 1.0.0 7 | vcdVersion: 10.4.1 8 | friendlyName: Skeleton Starter Kit 9 | description: Showcase the multi-instance add-on flavor with unique elements for each instance 10 | 11 | inputs: 12 | - name: provider-business-scope-property 13 | title: Business Scope 14 | required: true 15 | description: Define the business and operational scopes of the add-on instance 16 | minLength: 2 17 | maxLength: 24 18 | - name: password 19 | # Secure property 20 | title: Password 21 | required: true 22 | description: The password for the new local Cloud Director account to be created and linked to a business scope. 23 | secure: true 24 | minLength: 8 25 | maxLength: 16 26 | - name: certificate 27 | title: Certificate 28 | type: String 29 | view: multiline 30 | secure: true 31 | - name: justification 32 | # Property visible only on delete operation 33 | title: Justification 34 | type: String 35 | description: Why delete this Solution instance? 36 | delete: true 37 | minLength: 5 38 | maxLength: 256 39 | 40 | 41 | policies: 42 | # Enable the add-on to be deployed in multiple instances 43 | supportsMultipleInstances: true 44 | 45 | triggers: 46 | # Enable a single actions to be called on PreCreate and PostDelete events 47 | - event: PreCreate 48 | action: actions/multipurposeaction 49 | timeout: 30 50 | - event: PostDelete 51 | action: actions/multipurposeaction 52 | timeout: 30 53 | - event: PreScope 54 | action: actions/multipurposeaction 55 | timeout: 30 56 | 57 | 58 | operations: 59 | - name: updateCloudDirectorCertificate 60 | description: Add the current Cloud Director certificate into the trusted store 61 | friendlyName: Trust Current Cloud Director Certificate 62 | action: actions/multipurposeaction 63 | timeout: 10 64 | - name: updateTrustedStore 65 | description: Add a certificate into the trusted store 66 | friendlyName: Trust Certificate 67 | inputs: 68 | - name: certificate 69 | title: Certificate 70 | type: String 71 | view: multiline 72 | secure: true 73 | timeout: 10 74 | action: actions/multipurposeaction 75 | 76 | elements: 77 | - name: ui 78 | # Immutable element, shared across all instances 79 | description: User interface 80 | type: ui-plugin 81 | spec: 82 | publish: 83 | provider: true 84 | tenants: true 85 | solutionLandingZone: true 86 | triggers: 87 | - event: PreScope 88 | action: actions/multipurposeaction 89 | timeout: 30 90 | - name: db-schemas 91 | # Immutable element, shared across all instances 92 | description: Business Objects Schemas 93 | type: defined-entity 94 | - name: rights 95 | # Mutable element, it needs to define a unique specification for each add-on instance 96 | description: Business objects rights 97 | type: rights-bundle 98 | spec: 99 | name: 'Skeleton-{{ instance `name` }}' 100 | description: This rights bundle is created by Skeleton Add-On 101 | publish: 102 | solutionLandingZone: true 103 | rights: 104 | - urn:vcloud:type:vmware:skeleton_database_entity 105 | - urn:vcloud:type:vmware:skeleton_database_entity:admin 106 | - name: role 107 | # Mutable element, it needs to define a unique specification for each add-on instance 108 | description: Business objects role 109 | type: role 110 | spec: 111 | name: 'Skeleton-{{ instance `name` }}' 112 | description: This role is created by Skeleton Add-On 113 | global: false 114 | systemScope: false 115 | publish: 116 | solutionLandingZone: true 117 | rights: 118 | - urn:vcloud:type:vmware:skeleton_database_entity 119 | - urn:vcloud:type:vmware:skeleton_database_entity:admin 120 | - name: cloud-director-user 121 | # Mutable element, it needs to define a unique specification for each add-on instance 122 | description: User interacting with Cloud Director from backend 123 | type: user 124 | spec: 125 | username: 'sva.{{ property `provider-business-scope-property` }}' 126 | password: '{{ property `password` }}' 127 | fullName: Skeleton backend system account 128 | description: 'User sva.{{ property `provider-business-scope-property` }} with role {{ property `role.name` }}' 129 | roleName: '{{ property `role.name` }}' 130 | systemScope: false 131 | triggers: 132 | - event: PostCreate 133 | # This action will output property "api-token" under the element 134 | action: actions/multipurposeaction 135 | timeout: 30 136 | - name: 'backend-appliance' 137 | # Mutable element, it needs to define a unique specification for each add-on instance 138 | description: Backend processor of business objects 139 | type: vapp 140 | spec: 141 | # When the 'name' property is skipped for vApps, the SDK will generate a unique name 142 | ovfProperties: 143 | - key: provider-business-scope-property 144 | value: '{{ property `provider-business-scope-property` }}' 145 | - key: cloud-director-host 146 | value: '{{ vcd `host` }}' 147 | - key: cloud-director-host-certificates 148 | value: '{{ vcd `certificates` }}' 149 | - key: api-token 150 | value: '{{ property `api-token` }}' 151 | hardwareCustomization: 152 | numberOfCpus: 1 153 | memorySize: 512 154 | networks: 155 | - primary: true 156 | capabilities: [] 157 | readyCondition: 158 | # The "Ready" condition waits for listed properties to have any value, or a specific value if defined. 159 | # A property can be the "ip" of the virtual machine or any of the available ExtraConfig properties. 160 | # 161 | # Example: Waiting for the virtual machine to be assigned an IP address 162 | # "ip": 163 | # 164 | # Note: This is a dummy vApp and will not allocate any IP. 165 | timeoutMinutes: 10 166 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/.dockerignore: -------------------------------------------------------------------------------- 1 | out/ 2 | lib/ 3 | dist/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | lib/ 3 | dist/ 4 | node_modules/ 5 | 6 | .angular -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/Dockerfile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------ 2 | # Copyright 2023 VMware, Inc. 3 | # SPDX-License-Identifier: BSD-2-Clause 4 | # ------------------------------------------------------ 5 | # Build element 6 | # ------------------------------------------------------ 7 | ARG DOCKER_REPO 8 | FROM $DOCKER_REPO/node:18.20-slim as NODE 9 | COPY . /usr/app 10 | WORKDIR /usr/app 11 | RUN npm ci 12 | RUN npm run build 13 | 14 | # ------------------------------------------------------ 15 | # Output element artifacts 16 | # ------------------------------------------------------ 17 | FROM scratch AS export 18 | COPY --from=NODE /usr/app/dist ./ 19 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "uiPlugin": { 7 | "projectType": "library", 8 | "root": "", 9 | "sourceRoot": "src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@vcd/plugin-builders:plugin-builder", 14 | "options": { 15 | "modulePath": "src/main/showcase-plugin.module.ts#ShowcasePluginModule", 16 | "outputPath": "dist/", 17 | "index": "src/index.html", 18 | "main": "src/plugin.main.ts", 19 | "tsConfig": "tsconfig.json", 20 | "assets": [{ 21 | "glob": "**/*", 22 | "input": "./src/public", 23 | "output": "/" 24 | }], 25 | "optimization": false, 26 | "outputHashing": "all", 27 | "sourceMap": false, 28 | "extractCss": false, 29 | "namedChunks": false, 30 | "aot": false, 31 | "extractLicenses": false, 32 | "vendorChunk": false, 33 | "buildOptimizer": false 34 | } 35 | }, 36 | "serve": { 37 | "builder": "@angular-devkit/build-angular:dev-server", 38 | "options": { 39 | "browserTarget": "uiPlugin:build", 40 | "proxyConfig": ".env/proxy.conf.runtime.json" 41 | }, 42 | "configurations": { 43 | "production": { 44 | "browserTarget": "uiPlugin:build:production" 45 | } 46 | } 47 | }, 48 | "test": { 49 | "builder": "@angular-devkit/build-angular:karma", 50 | "options": { 51 | "polyfills": [ 52 | "zone.js", 53 | "zone.js/testing" 54 | ], 55 | "tsConfig": "tsconfig.spec.json", 56 | "inlineStyleLanguage": "scss", 57 | "assets": [ 58 | "src/favicon.ico", 59 | "src/assets" 60 | ], 61 | "styles": [ 62 | "src/styles.scss" 63 | ], 64 | "scripts": [ 65 | "./node_modules/systemjs/dist/system.js", 66 | "./vcd-sdk-polyfill.js" 67 | ] 68 | } 69 | }, 70 | "lint": { 71 | "builder": "@angular-devkit/build-angular:tslint", 72 | "options": { 73 | "tsConfig": [ 74 | "tsconfig.lib.json", 75 | "tsconfig.spec.json" 76 | ], 77 | "exclude": [ 78 | "**/node_modules/**" 79 | ] 80 | } 81 | } 82 | } 83 | } 84 | }, 85 | "defaultProject": "uiPlugin" 86 | } 87 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/build.args: -------------------------------------------------------------------------------- 1 | DOCKER_REPO=docker.io -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/docker.md: -------------------------------------------------------------------------------- 1 | # Building this project with Docker 2 | This project is suited for building using Docker. You are not limited to building using only 3 | Docker, however building with Docker, guarantees use of compatible 4 | nodejs and npm versions, which you shouldn't install locally. 5 | 6 | To build using Docker simply run: 7 | 8 | DOCKER_BUILDKIT=1 docker build --no-cache --target=export --output=dist . -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "0.0.2", 4 | "description": "", 5 | "scripts": { 6 | "build": "ng build", 7 | "test": "ng test --watch=false --browsers ChromeHeadlessNoSandbox", 8 | "test:watch": "ng test" 9 | }, 10 | "license": "BSD-2-Clause", 11 | "dependencies": { 12 | "@angular/animations": "17.1.2", 13 | "@angular/common": "17.1.2", 14 | "@angular/compiler": "17.1.2", 15 | "@angular/core": "17.1.2", 16 | "@angular/forms": "17.1.2", 17 | "@angular/platform-browser": "17.1.2", 18 | "@angular/platform-browser-dynamic": "17.1.2", 19 | "@angular/router": "17.1.2", 20 | "@cds/core": "6.9.2", 21 | "@clr/angular": "17.0.1", 22 | "@clr/ui": "17.0.1", 23 | "@ngrx/store": "17.0.1", 24 | "@vcd/bindings": "9.1.1", 25 | "@vcd/i18n": "17.1.2", 26 | "@vcd/sdk": "17.0.0", 27 | "rxjs": "7.8.0", 28 | "tslib": "2.3.0", 29 | "zone.js": "0.14.3" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/architect": "0.1701.2", 33 | "@angular-devkit/build-angular": "17.1.2", 34 | "@angular/cli": "17.1.2", 35 | "@angular/compiler-cli": "17.1.2", 36 | "@types/jasmine": "5.1.0", 37 | "@vcd/plugin-builders": "17.0.0", 38 | "jasmine-core": "5.1.0", 39 | "karma": "6.4.0", 40 | "karma-chrome-launcher": "3.2.0", 41 | "karma-coverage": "2.2.0", 42 | "karma-jasmine": "5.1.0", 43 | "karma-jasmine-html-reporter": "2.1.0", 44 | "systemjs": "0.20.19", 45 | "typescript": "5.3.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/add-on/multi-instance-skeleton/ui/src/favicon.ico -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/add-on/multi-instance-skeleton/ui/src/index.html -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/actions/index.ts: -------------------------------------------------------------------------------- 1 | export { VappRestoreActionComponent } from "./vapp.restore.action.component"; 2 | export { VmBackupActionComponent } from "./vm.backup.action.component"; 3 | 4 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/actions/vapp.restore.action.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 11 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/actions/vapp.restore.action.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { VappRestoreActionComponent } from './vapp.restore.action.component'; 4 | import { I18nModule } from "@vcd/i18n"; 5 | import { ClarityModule } from "@clr/angular"; 6 | 7 | describe('VappRestoreActionComponent', () => { 8 | beforeEach(async () => { 9 | await TestBed.configureTestingModule({ 10 | imports: [ 11 | RouterTestingModule, 12 | ClarityModule, 13 | I18nModule.forChild(), 14 | ], 15 | declarations: [ 16 | VappRestoreActionComponent 17 | ], 18 | }).compileComponents(); 19 | }); 20 | 21 | it('should create the VappRestoreActionComponent', () => { 22 | const fixture = TestBed.createComponent(VappRestoreActionComponent); 23 | const app = fixture.componentInstance; 24 | expect(app).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/actions/vapp.restore.action.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, OnDestroy } from "@angular/core"; 6 | import { Observable, of, Subject } from "rxjs"; 7 | import { 8 | EntityActionExtensionComponent, 9 | EntityActionExtensionMenuEntry 10 | } from "@vcd/sdk"; 11 | import { TranslationService } from "@vcd/i18n"; 12 | 13 | @Component({ 14 | selector: 'vapp-restore-action-extension', 15 | templateUrl: './vapp.restore.action.component.html' 16 | }) 17 | export class VappRestoreActionComponent extends EntityActionExtensionComponent implements OnDestroy { 18 | modalText = ""; 19 | opened = false; 20 | 21 | private result: Subject<{ refreshRequested: boolean }>; 22 | 23 | constructor(private translationService: TranslationService) { 24 | super(); 25 | } 26 | 27 | getMenuEntry(entityUrn: string): Observable { 28 | return of({ 29 | text: this.translationService.translate("vapp.action.restore"), 30 | children: [{ 31 | urn: "urn:vmware:vcloud:vapp:restore3", 32 | text: "Restore VMs", 33 | busy: false, 34 | enabled: true 35 | }, 36 | { 37 | urn: "urn:vmware:vcloud:vapp:restoreAll3", 38 | text: "Restore All VMs", 39 | busy: false, 40 | enabled: false 41 | }, 42 | { 43 | urn: "urn:vmware:vcloud:vapp:viewSnaphots3", 44 | text: "View Restore Points", 45 | busy: false, 46 | enabled: true 47 | }] 48 | }); 49 | } 50 | 51 | performAction(menuItemUrn: string, entityUrn: string): Observable<{ refreshRequested: boolean }> { 52 | this.modalText = `Entity: ${entityUrn} Action: ${menuItemUrn}`; 53 | this.opened = true; 54 | this.result = new Subject<{ refreshRequested: boolean }>(); 55 | 56 | console.log("[Showcase 3.0]", "Restore"); 57 | 58 | return this.result.asObservable(); 59 | } 60 | 61 | onClose() { 62 | this.opened = false; 63 | this.result.next({ refreshRequested: true }); 64 | this.result.complete(); 65 | } 66 | 67 | ngOnDestroy(): void { 68 | console.warn(`[Showcase 3.0]`, `${this.constructor.name} destroyed`); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/actions/vm.backup.action.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 11 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/actions/vm.backup.action.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, OnDestroy } from "@angular/core"; 6 | import { Observable, of, Subject } from "rxjs"; 7 | import { 8 | EntityActionExtensionComponent, 9 | EntityActionExtensionMenuEntry 10 | } from "@vcd/sdk"; 11 | import { TranslationService } from "@vcd/i18n"; 12 | 13 | @Component({ 14 | selector: 'vm-backup-action-extension', 15 | templateUrl: './vm.backup.action.component.html' 16 | }) 17 | export class VmBackupActionComponent extends EntityActionExtensionComponent implements OnDestroy { 18 | modalText = ""; 19 | opened = false; 20 | 21 | private result: Subject<{ refreshRequested: boolean }>; 22 | 23 | constructor(private translationService: TranslationService) { 24 | super(); 25 | } 26 | 27 | getMenuEntry(entityUrn: string): Observable { 28 | return of({ 29 | text: this.translationService.translate("vm.action.backup"), 30 | children: [{ 31 | urn: "vmware:vcloud:vm-action:backup3", 32 | text: "Backup", 33 | busy: false, 34 | enabled: true 35 | }, 36 | { 37 | urn: "urn:vmware:vcloud:vm:deleteBackup3", 38 | text: "Delete Backups", 39 | busy: false, 40 | enabled: false 41 | }] 42 | }); 43 | } 44 | 45 | performAction(menuItemUrn: string, entityUrn: string): Observable<{ refreshRequested: boolean }> { 46 | this.modalText = `Entity: ${entityUrn} Action: ${menuItemUrn}`; 47 | this.opened = true; 48 | this.result = new Subject<{ refreshRequested: boolean }>(); 49 | 50 | console.log("[Showcase 3.0]", "Backup"); 51 | 52 | return this.result.asObservable(); 53 | } 54 | 55 | onClose() { 56 | this.opened = false; 57 | this.result.next({ refreshRequested: true }); 58 | this.result.complete(); 59 | } 60 | 61 | ngOnDestroy(): void { 62 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/application/application.component.html: -------------------------------------------------------------------------------- 1 |

Application Extensibility Works!

-------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/application/application.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { ApplicationComponent } from './application.component'; 4 | import { I18nModule } from "@vcd/i18n"; 5 | import { ClarityModule } from "@clr/angular"; 6 | 7 | describe('ApplicationComponent', () => { 8 | beforeEach(async () => { 9 | await TestBed.configureTestingModule({ 10 | imports: [ 11 | RouterTestingModule, 12 | ClarityModule, 13 | I18nModule.forChild(), 14 | ], 15 | declarations: [ 16 | ApplicationComponent 17 | ], 18 | providers: [ 19 | { provide: "API_ROOT_URL", useValue: "https://localhost" }, 20 | ] 21 | }).compileComponents(); 22 | }); 23 | 24 | it('should create the app', () => { 25 | const fixture = TestBed.createComponent(ApplicationComponent); 26 | const app = fixture.componentInstance; 27 | expect(app).toBeTruthy(); 28 | expect(app.testInjectionToken).toEqual("https://localhost"); 29 | }); 30 | 31 | it('should render title', () => { 32 | const fixture = TestBed.createComponent(ApplicationComponent); 33 | fixture.detectChanges(); 34 | const compiled = fixture.nativeElement as HTMLElement; 35 | expect(compiled.querySelector('h1')?.textContent).toContain('Application Extensibility Works!'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/application/application.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Inject } from "@angular/core"; 6 | import { Component, OnDestroy, OnInit } from "@angular/core"; 7 | import { API_ROOT_URL } from "@vcd/sdk"; 8 | import { Observable } from "rxjs"; 9 | 10 | @Component({ 11 | selector: "application", 12 | templateUrl: "./application.component.html", 13 | host: {'class': 'content-container'} 14 | }) 15 | export class ApplicationComponent implements OnInit, OnDestroy { 16 | username: Observable; 17 | tenant: Observable; 18 | testInjectionToken: string; 19 | 20 | constructor( 21 | @Inject(API_ROOT_URL) API_ROOT_URL: string, 22 | ) { 23 | this.testInjectionToken = API_ROOT_URL; 24 | } 25 | 26 | ngOnInit(): void {} 27 | 28 | ngOnDestroy(): void { 29 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/common/entity-context-extension.interface.ts: -------------------------------------------------------------------------------- 1 | export interface EntityContextExtensionInterface { 2 | contextEntityId: string; 3 | 4 | // @see EntityContainerExtensionComponent in Cloud Director 5 | contextUrn(entityId: string): void; 6 | } 7 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-ip-bindings/index.ts: -------------------------------------------------------------------------------- 1 | export { IpBindingsCreateWizardExtensionPointComponent } from "./ip-bindings.create.wizard.action.component"; 2 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-ip-bindings/ip-bindings.create.wizard.action.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-ip-bindings/ip-bindings.create.wizard.action.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { IpBindingsCreateWizardExtensionPointComponent } from './ip-bindings.create.wizard.action.component'; 4 | import { I18nModule } from "@vcd/i18n"; 5 | import { ClarityModule } from "@clr/angular"; 6 | 7 | describe('IpBindingsCreateWizardExtensionPointComponent', () => { 8 | beforeEach(async () => { 9 | await TestBed.configureTestingModule({ 10 | imports: [ 11 | RouterTestingModule, 12 | ClarityModule, 13 | I18nModule.forChild(), 14 | ], 15 | declarations: [ 16 | IpBindingsCreateWizardExtensionPointComponent 17 | ], 18 | }).compileComponents(); 19 | }); 20 | 21 | it('should create the IpBindingsCreateWizardExtensionPointComponent', () => { 22 | const fixture = TestBed.createComponent(IpBindingsCreateWizardExtensionPointComponent); 23 | const app = fixture.componentInstance; 24 | expect(app).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-ip-bindings/ip-bindings.create.wizard.action.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from "@angular/core"; 2 | import { WizardExtensionWithContextComponent } from "@vcd/sdk"; 3 | 4 | @Component({ 5 | selector: 'ip-bindings-create-extension', 6 | templateUrl: './ip-bindings.create.wizard.action.component.html' 7 | }) 8 | export class IpBindingsCreateWizardExtensionPointComponent extends WizardExtensionWithContextComponent implements OnDestroy { 9 | id: string; 10 | 11 | constructor() { 12 | super(); 13 | } 14 | 15 | onContext(context: string): void { 16 | this.id = context; 17 | } 18 | 19 | performAction(payload: string, returnValue: string, error: any) { 20 | console.log("[IP Bindings Create Wizard Extension Point]", payload, returnValue, error); 21 | } 22 | 23 | ngOnDestroy() { 24 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 25 | } 26 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-org/index.ts: -------------------------------------------------------------------------------- 1 | export { OrgCreateWizardExtensionPointComponent } from "./org.create.wizard.action.component"; -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-org/org.create.wizard.action.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |
-------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-org/org.create.wizard.action.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from "@angular/core"; 2 | import { WizardExtensionState, WizardExtensionWithValidationComponent } from "@vcd/sdk"; 3 | import { FormGroup, FormBuilder, FormControl, Validators } from "@angular/forms"; 4 | import { BehaviorSubject, Observable } from "rxjs"; 5 | 6 | @Component({ 7 | selector: 'org-create-extension', 8 | templateUrl: './org.create.wizard.action.component.html' 9 | }) 10 | export class OrgCreateWizardExtensionPointComponent extends WizardExtensionWithValidationComponent implements OnDestroy { 11 | form: FormGroup; 12 | 13 | private stateSubject = new BehaviorSubject<{ 14 | isValid: boolean 15 | }>(null); 16 | private stateObs = this.stateSubject.asObservable(); 17 | 18 | constructor( 19 | private fb: FormBuilder, 20 | ) { 21 | super(); 22 | 23 | this.form = this.fb.group({ 24 | "example": new FormControl(null, Validators.required) 25 | }); 26 | this.stateSubject.next({ 27 | isValid: this.form.valid 28 | }); 29 | this.setState(); 30 | } 31 | 32 | ngOnDestroy() { 33 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 34 | } 35 | 36 | performAction(payload: string, returnValue: string, error: any) { 37 | console.log("[Org Create Wizard Extension Point]", payload, returnValue, error); 38 | } 39 | 40 | setState() { 41 | this.form.statusChanges.subscribe(() => { 42 | this.stateSubject.next({ 43 | isValid: this.form.valid 44 | }); 45 | }); 46 | } 47 | 48 | getState(): Observable { 49 | return this.stateObs; 50 | } 51 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-vapp/index.ts: -------------------------------------------------------------------------------- 1 | export { VappCreateWizardExtensionPointComponent } from "./vapp.create.wizard.action.component"; 2 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-vapp/vapp.create.wizard.action.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-vapp/vapp.create.wizard.action.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from "@angular/core"; 2 | import { WizardExtensionComponent } from "@vcd/sdk"; 3 | import { ModalWizardExtensionPointService } from "../services/modal-wizard-ext-point.service"; 4 | 5 | @Component({ 6 | selector: 'vapp-create-extension', 7 | templateUrl: './vapp.create.wizard.action.component.html' 8 | }) 9 | export class VappCreateWizardExtensionPointComponent extends WizardExtensionComponent implements OnDestroy { 10 | constructor(private modalWizardExtensionPointService: ModalWizardExtensionPointService) { 11 | super(); 12 | 13 | this.modalWizardExtensionPointService.inVappContext(); 14 | } 15 | 16 | performAction(payload: string, returnValue: string, error: any) { 17 | console.log("[vApp Create Wizard Extension Point]", payload, returnValue, error); 18 | this.modalWizardExtensionPointService.storeData([payload, returnValue]); 19 | } 20 | 21 | ngOnDestroy() { 22 | this.modalWizardExtensionPointService.exitVappContext(); 23 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 24 | } 25 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-vm/index.ts: -------------------------------------------------------------------------------- 1 | export { VmCreateWizardExtensionPointComponent } from "./vm.create.wizard.action.component"; 2 | 3 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-vm/vm.create.wizard.action.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

In vApp Context

7 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/create-vm/vm.create.wizard.action.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from "@angular/core"; 2 | import { WizardExtensionComponent } from "@vcd/sdk"; 3 | import { ModalWizardExtensionPointService } from "../services/modal-wizard-ext-point.service"; 4 | 5 | @Component({ 6 | selector: 'vm-create-extension', 7 | templateUrl: './vm.create.wizard.action.component.html' 8 | }) 9 | export class VmCreateWizardExtensionPointComponent extends WizardExtensionComponent implements OnDestroy { 10 | inVappContext = false; 11 | 12 | constructor(private modalWizardExtensionPointService: ModalWizardExtensionPointService) { 13 | super(); 14 | 15 | this.modalWizardExtensionPointService.inVappContextObs.subscribe((value) => { 16 | this.inVappContext = value; 17 | }); 18 | } 19 | 20 | ngOnDestroy() { 21 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 22 | } 23 | 24 | performAction(payload: string, returnValue: string, error: any) { 25 | console.log("[VM Create Wizard Extension Point]", payload, returnValue, error); 26 | this.modalWizardExtensionPointService.storeData([payload, returnValue]); 27 | } 28 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-compute/datacenter-compute.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Datacenter Compute Works!

3 |

In context of Virtual Datacenter ID: {{ contextEntityId }}

4 |
5 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-compute/datacenter-compute.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, OnDestroy, OnInit } from "@angular/core"; 6 | import { Observable } from "rxjs"; 7 | import { 8 | EntityContextExtensionInterface 9 | } from "../common/entity-context-extension.interface"; 10 | 11 | @Component({ 12 | selector: "datacenter-compute", 13 | templateUrl: "./datacenter-compute.component.html", 14 | host: {'class': 'content-container'} 15 | }) 16 | export class DatacenterComputeComponent implements EntityContextExtensionInterface, OnInit, OnDestroy { 17 | username: Observable; 18 | tenant: Observable; 19 | 20 | contextEntityId: string = ""; 21 | 22 | contextUrn(entityId: string) { 23 | this.contextEntityId = entityId; 24 | } 25 | 26 | constructor() {} 27 | 28 | ngOnInit(): void {} 29 | 30 | ngOnDestroy(): void { 31 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-network/datacenter-network.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Datacenter Network Works!

3 |

In context of Virtual Datacenter ID: {{ contextEntityId }}

4 |
5 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-network/datacenter-network.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, OnDestroy, OnInit } from "@angular/core"; 6 | import { Observable } from "rxjs"; 7 | import { EntityContextExtensionInterface } from "../common/entity-context-extension.interface"; 8 | 9 | @Component({ 10 | selector: "datacenter-network", 11 | templateUrl: "./datacenter-network.component.html", 12 | host: {'class': 'content-container'} 13 | }) 14 | export class DatacenterNetworkComponent implements EntityContextExtensionInterface, OnInit, OnDestroy { 15 | username: Observable; 16 | tenant: Observable; 17 | 18 | contextEntityId: string = ""; 19 | 20 | contextUrn(entityId: string): void { 21 | this.contextEntityId = entityId; 22 | } 23 | 24 | constructor() {} 25 | 26 | ngOnInit(): void {} 27 | 28 | ngOnDestroy(): void { 29 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-overview/datacenter.container.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | {{sddc.name}} 6 |
7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
CPU{{getCpuUsage(sddc.cpu)}}
Memory{{getCpuUsage(sddc.memory)}}
Running Applications{{sddc.applications}}
25 |
26 |
27 | 31 |
32 |
33 |
-------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-overview/datacenter.container.component.scss: -------------------------------------------------------------------------------- 1 | cpom-sddc-card { 2 | margin-top: 24px; 3 | min-width: 600px; 4 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-overview/datacenter.container.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from "@angular/core"; 2 | import { Observable, Subscription } from "rxjs"; 3 | 4 | @Component({ 5 | selector: 'custom-datacenter-container', 6 | templateUrl: './datacenter.container.component.html' 7 | }) 8 | export class DatacenterContainerComponent implements OnDestroy{ 9 | sddcs: any = [ 10 | {name: "Sddc 1", cpu: {used: 11.3, total: 100}, memory: {used: 512, total: 8192}, applications: 23}, 11 | {name: "Sddc 2", cpu: {used: 2.6, total: 100}, memory: {used: 16, total: 8192}, applications: 4}, 12 | {name: "Sddc 3", cpu: {used: 0, total: 100}, memory: {used: 0, total: 8192}, applications: 0} 13 | ]; 14 | 15 | constructor() {} 16 | 17 | ngOnDestroy(): void { 18 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 19 | } 20 | 21 | getCpuUsage(cpu: any): string { 22 | return `Using ${cpu.used} of ${cpu.total} GHz (${cpu.used / cpu.total * 100}%)`; 23 | } 24 | 25 | getMemUsage(memory: any): string { 26 | return `Using ${memory.used} of ${memory.total} GB (${memory.used / memory.total * 100}%)`; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-storage/datacenter-storage.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Datacenter Storage Works!

3 |

In context of Virtual Datacenter ID: {{ contextEntityId }}

4 |
5 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/datacenter-storage/datacenter-storage.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, OnDestroy, OnInit } from "@angular/core"; 6 | import { Observable } from "rxjs"; 7 | import { 8 | EntityContextExtensionInterface, 9 | } from "../common/entity-context-extension.interface"; 10 | 11 | @Component({ 12 | selector: "datacenter-storage", 13 | templateUrl: "./datacenter-storage.component.html", 14 | host: {'class': 'content-container'} 15 | }) 16 | export class DatacenterStorageComponent implements EntityContextExtensionInterface, OnInit, OnDestroy { 17 | username: Observable; 18 | tenant: Observable; 19 | 20 | contextEntityId: string = ""; 21 | 22 | contextUrn(entityId: string): void { 23 | this.contextEntityId = entityId; 24 | } 25 | 26 | constructor() {} 27 | 28 | ngOnInit(): void {} 29 | 30 | ngOnDestroy(): void { 31 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/extension-container-guard.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Injectable } from "@angular/core"; 6 | import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from "@angular/router"; 7 | import { Observable, Subject } from "rxjs"; 8 | import { take } from "rxjs/operators"; 9 | 10 | @Injectable({ providedIn: "root"}) 11 | export class ExtensionContainerGuard implements CanDeactivate { 12 | constructor() {} 13 | 14 | canDeactivate( 15 | component: any, 16 | currentRoute: ActivatedRouteSnapshot, 17 | currentState: RouterStateSnapshot, 18 | nextState: RouterStateSnapshot): Promise | boolean { 19 | console.warn("[Showcase 3.0]", "ExtensionContainerGuard called"); 20 | return true; 21 | } 22 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/networking/networking.component.html: -------------------------------------------------------------------------------- 1 |

Networking Extensibility Works!

2 |

In context of Network ID: {{ contextEntityId }}

-------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/networking/networking.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, OnDestroy, OnInit } from "@angular/core"; 6 | import { Observable } from "rxjs"; 7 | import { EntityContextExtensionInterface } from "../common/entity-context-extension.interface"; 8 | 9 | @Component({ 10 | selector: "networking", 11 | templateUrl: "./networking.component.html", 12 | host: {'class': 'content-container'} 13 | }) 14 | export class NetworkingComponent implements EntityContextExtensionInterface, OnInit, OnDestroy { 15 | username: Observable; 16 | tenant: Observable; 17 | 18 | contextEntityId: string = ""; 19 | 20 | contextUrn(entityId: string) { 21 | this.contextEntityId = entityId; 22 | } 23 | 24 | constructor() {} 25 | 26 | ngOnInit(): void {} 27 | 28 | ngOnDestroy(): void { 29 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/services/modal-wizard-ext-point.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { BehaviorSubject } from "rxjs"; 3 | 4 | @Injectable() 5 | export class ModalWizardExtensionPointService { 6 | inVappContextSubject = new BehaviorSubject(false); 7 | inVappContextObs = this.inVappContextSubject.asObservable(); 8 | 9 | constructor() { 10 | console.log("[ModalWizardExtensionPointService] Init!"); 11 | } 12 | 13 | public inVappContext() { 14 | console.log("[ModalWizardExtensionPointService] Enter vApp Context!"); 15 | this.inVappContextSubject.next(true); 16 | } 17 | 18 | public exitVappContext() { 19 | console.log("[ModalWizardExtensionPointService] Exit vApp Context!"); 20 | 21 | this.inVappContextSubject.next(false); 22 | } 23 | 24 | public storeData(data: T) { 25 | // Store your data 26 | console.log("Data stored!"); 27 | } 28 | } -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/showcase-plugin.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { CommonModule } from "@angular/common"; 6 | import { CUSTOM_ELEMENTS_SCHEMA, Inject, NgModule } from "@angular/core"; 7 | import { ReactiveFormsModule } from "@angular/forms"; 8 | import { HttpHeaders } from "@angular/common/http"; 9 | import { ClarityModule } from "@clr/angular"; 10 | import { Store } from "@ngrx/store"; 11 | import { I18nModule, TranslationService } from "@vcd/i18n"; 12 | import { 13 | PluginModule, 14 | VcdSdkModule, 15 | VcdApiClient, 16 | Query, 17 | API_ROOT_URL, 18 | AuthTokenHolderService, 19 | ExtensionNavRegistration, 20 | EXTENSION_ASSET_URL, 21 | EXTENSION_ROUTE, 22 | FLEX_APP_URL, 23 | SDK_MODE, 24 | SESSION_ORGANIZATION, 25 | SESSION_ORG_ID, 26 | SESSION_SCOPE 27 | } from "@vcd/sdk"; 28 | import { VmBackupActionComponent } from "./actions/vm.backup.action.component"; 29 | import { VappRestoreActionComponent } from "./actions/vapp.restore.action.component"; 30 | import { DatacenterContainerComponent } from "./datacenter-overview/datacenter.container.component"; 31 | import { ApplicationComponent } from './application/application.component'; 32 | import { DatacenterComputeComponent } from './datacenter-compute/datacenter-compute.component'; 33 | import { DatacenterNetworkComponent } from './datacenter-network/datacenter-network.component'; 34 | import { DatacenterStorageComponent } from './datacenter-storage/datacenter-storage.component'; 35 | import { VappCreateWizardExtensionPointComponent } from './create-vapp/vapp.create.wizard.action.component'; 36 | import { VmCreateWizardExtensionPointComponent } from './create-vm/vm.create.wizard.action.component'; 37 | import { OrgCreateWizardExtensionPointComponent } from './create-org/org.create.wizard.action.component'; 38 | import { ModalWizardExtensionPointService } from "./services/modal-wizard-ext-point.service"; 39 | import { NetworkingComponent } from "./networking/networking.component"; 40 | import { IpBindingsCreateWizardExtensionPointComponent } from "./create-ip-bindings"; 41 | // Exports 42 | export { SubnavComponent } from "./subnav/subnav.component"; 43 | export { VappRestoreActionComponent, VmBackupActionComponent } from "./actions"; 44 | export { DatacenterContainerComponent } from './datacenter-overview/datacenter.container.component'; 45 | export { ApplicationComponent } from './application/application.component'; 46 | export { NetworkingComponent } from './networking/networking.component'; 47 | export { DatacenterComputeComponent } from './datacenter-compute/datacenter-compute.component'; 48 | export { DatacenterNetworkComponent } from './datacenter-network/datacenter-network.component'; 49 | export { DatacenterStorageComponent } from './datacenter-storage/datacenter-storage.component'; 50 | export { VmCreateWizardExtensionPointComponent } from './create-vm'; 51 | export { 52 | VappCreateWizardExtensionPointComponent, 53 | } from './create-vapp'; 54 | export { 55 | OrgCreateWizardExtensionPointComponent 56 | } from './create-org'; 57 | export { 58 | IpBindingsCreateWizardExtensionPointComponent 59 | } from './create-ip-bindings'; 60 | 61 | export * from './subnav/sub-nav.module'; 62 | export * from './simple/simple-nav.module'; 63 | 64 | const components = [ 65 | DatacenterContainerComponent, 66 | VappRestoreActionComponent, 67 | VmBackupActionComponent, 68 | ApplicationComponent, 69 | NetworkingComponent, 70 | DatacenterComputeComponent, 71 | DatacenterNetworkComponent, 72 | DatacenterStorageComponent, 73 | VappCreateWizardExtensionPointComponent, 74 | VmCreateWizardExtensionPointComponent, 75 | OrgCreateWizardExtensionPointComponent, 76 | IpBindingsCreateWizardExtensionPointComponent, 77 | ] 78 | 79 | @NgModule({ 80 | imports: [ 81 | ClarityModule, 82 | CommonModule, 83 | I18nModule.forChild(EXTENSION_ASSET_URL, true), 84 | ReactiveFormsModule, 85 | VcdSdkModule.forRoot(), 86 | ], 87 | declarations: [ 88 | ...components 89 | ], 90 | exports: [], 91 | providers: [ModalWizardExtensionPointService], 92 | schemas: [CUSTOM_ELEMENTS_SCHEMA] 93 | }) 94 | export class ShowcasePluginModule extends PluginModule { 95 | constructor( 96 | appStore: Store, 97 | @Inject(AuthTokenHolderService) token: AuthTokenHolderService, 98 | @Inject(API_ROOT_URL) API_ROOT_URL: string, 99 | @Inject(FLEX_APP_URL) FLEX_APP_URL: string, 100 | @Inject(SESSION_SCOPE) SESSION_SCOPE: string, 101 | @Inject(SESSION_ORGANIZATION) SESSION_ORGANIZATION: string, 102 | @Inject(SESSION_ORG_ID) SESSION_ORG_ID: string, 103 | @Inject(EXTENSION_ASSET_URL) EXTENSION_ASSET_URL: string, 104 | @Inject(EXTENSION_ROUTE) extensionRoute: string, 105 | @Inject(SDK_MODE) SDK_MODE: string, 106 | translationService: TranslationService, 107 | private client: VcdApiClient) { 108 | 109 | super(appStore); 110 | 111 | translationService.registerTranslations(); 112 | 113 | this.registerExtension({ 114 | path: extensionRoute, 115 | icon: "page", 116 | nameCode: "nav.label.registered.extension", 117 | descriptionCode: "nav.description.registered.extension" 118 | }); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/simple/simple-nav.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { CommonModule } from "@angular/common"; 6 | import { NgModule } from "@angular/core"; 7 | import { RouterModule, Routes } from "@angular/router"; 8 | import { ClarityModule } from "@clr/angular"; 9 | import { I18nModule, TranslationService } from "@vcd/i18n"; 10 | import { EXTENSION_ASSET_URL, VcdSdkModule } from "@vcd/sdk"; 11 | import { SimpleComponent } from "./simple.component"; 12 | 13 | const components = [SimpleComponent]; 14 | 15 | const ROUTES: Routes = [ 16 | { 17 | path: "", 18 | component: SimpleComponent 19 | } 20 | ]; 21 | 22 | @NgModule({ 23 | imports: [ 24 | ClarityModule, 25 | CommonModule, 26 | RouterModule.forChild(ROUTES), 27 | I18nModule.forChild(EXTENSION_ASSET_URL, true), 28 | VcdSdkModule.forRoot(), 29 | ], 30 | declarations: [...components], 31 | exports: [...components], 32 | providers: [] 33 | }) 34 | export class SimpleNavModule { 35 | constructor(translationService: TranslationService) { 36 | translationService.registerTranslations(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/simple/simple.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ "simple.title" | translate }}

3 | 4 | 5 |

{{ "simple.login.info" | translate: [((tenant | async) || '...'), ((username | async) || '...' )] }}

6 | 7 |
8 |

{{ "tos.title" | translate }}

9 | 10 |
    11 |
  • {{ "tos.1" | translate }}
  • 12 |
  • {{ "tos.2" | translate }}
  • 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/simple/simple.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; 6 | import { EXTENSION_ASSET_URL, VcdApiClient, Query } from '@vcd/sdk'; 7 | import { Observable } from "rxjs"; 8 | 9 | /** 10 | * Simple showcase component. 11 | */ 12 | @Component({ 13 | selector: "plugin-simple", 14 | templateUrl: "./simple.component.html", 15 | styles: [` 16 | .simple-content { 17 | padding: 2rem; 18 | }` 19 | ] 20 | }) 21 | export class SimpleComponent implements OnInit, OnDestroy { 22 | username: Observable; 23 | tenant: Observable; 24 | 25 | constructor(@Inject(EXTENSION_ASSET_URL) public assetUrl: string, private client: VcdApiClient) {} 26 | 27 | ngOnInit(): void { 28 | this.tenant = this.client.organization; 29 | this.username = this.client.username 30 | 31 | this.client.query(Query.Builder.ofType("organization").links(false)).subscribe(results => { 32 | console.log(results); 33 | }); 34 | } 35 | 36 | ngOnDestroy(): void { 37 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/subnav/about.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | import {Component, Inject} from "@angular/core"; 5 | import {EXTENSION_ASSET_URL} from '@vcd/sdk'; 6 | 7 | @Component({ 8 | selector: "vcd-plugin-about", 9 | template: ` 10 |
11 |

{{"subnav.about.content.1" | translate}}

12 |

{{"subnav.about.content.2" | translate}}

13 |
14 | 15 | ` 16 | }) 17 | export class AboutComponent { 18 | constructor(@Inject(EXTENSION_ASSET_URL) public assetUrl: string) {} 19 | } 20 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/subnav/status.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | import {Component, Inject} from "@angular/core"; 5 | import {EXTENSION_ASSET_URL} from '@vcd/sdk'; 6 | 7 | @Component({ 8 | selector: "vcd-plugin-status", 9 | template: ` 10 |
11 |

{{"subnav.status.content" | translate:today}}

12 |

{{ date | date }}

13 |
14 | 15 | ` 16 | }) 17 | export class StatusComponent { 18 | today: String = new Date().toLocaleString(); 19 | date: Date = new Date(); 20 | 21 | constructor(@Inject(EXTENSION_ASSET_URL) public assetUrl: string) {} 22 | } 23 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/subnav/sub-nav.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { CommonModule } from "@angular/common"; 6 | import { Inject, NgModule } from "@angular/core"; 7 | import { RouterModule, Routes } from "@angular/router"; 8 | import { ClarityModule } from "@clr/angular"; 9 | import { I18nModule, TranslationService } from "@vcd/i18n"; 10 | import { API_ROOT_URL, AuthTokenHolderService, EXTENSION_ASSET_URL, EXTENSION_ROUTE, FLEX_APP_URL, SDK_MODE, SESSION_ORGANIZATION, SESSION_ORG_ID, SESSION_SCOPE, VcdApiClient, VcdSdkModule } from "@vcd/sdk"; 11 | import { ModalWizardExtensionPointService } from "../services/modal-wizard-ext-point.service"; 12 | import { AboutComponent } from "./about.component"; 13 | import { StatusComponent } from "./status.component"; 14 | import { SubnavComponent } from "./subnav.component"; 15 | import { Store } from "@ngrx/store"; 16 | import { ExtensionContainerGuard } from "../extension-container-guard"; 17 | 18 | const components = [AboutComponent, StatusComponent, SubnavComponent]; 19 | 20 | const ROUTES: Routes = [ 21 | { 22 | path: "", 23 | component: SubnavComponent, 24 | canDeactivate: [ExtensionContainerGuard], 25 | children: [ 26 | { path: "", redirectTo: "status", pathMatch: "full" }, 27 | { path: "status", component: StatusComponent, canDeactivate: [ExtensionContainerGuard],}, 28 | { path: "about", component: AboutComponent } 29 | ] 30 | } 31 | ]; 32 | 33 | @NgModule({ 34 | imports: [ 35 | ClarityModule, 36 | CommonModule, 37 | RouterModule.forChild(ROUTES), 38 | I18nModule.forChild(EXTENSION_ASSET_URL, true), 39 | VcdSdkModule.forRoot() 40 | ], 41 | declarations: [...components], 42 | exports: [...components], 43 | providers: [ModalWizardExtensionPointService], 44 | bootstrap: [SubnavComponent] 45 | }) 46 | export class SubNavModule { 47 | constructor( 48 | appStore: Store, 49 | @Inject(AuthTokenHolderService) token: AuthTokenHolderService, 50 | @Inject(API_ROOT_URL) API_ROOT_URL: string, 51 | @Inject(FLEX_APP_URL) FLEX_APP_URL: string, 52 | @Inject(SESSION_SCOPE) SESSION_SCOPE: string, 53 | @Inject(SESSION_ORGANIZATION) SESSION_ORGANIZATION: string, 54 | @Inject(SESSION_ORG_ID) SESSION_ORG_ID: string, 55 | @Inject(EXTENSION_ASSET_URL) EXTENSION_ASSET_URL: string, 56 | @Inject(EXTENSION_ROUTE) extensionRoute: string, 57 | @Inject(SDK_MODE) SDK_MODE: string, 58 | translationService: TranslationService, 59 | private client: VcdApiClient 60 | ) { 61 | translationService.registerTranslations(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/subnav/subnav.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | 8 | 9 | {{navItem.labelKey | translate}} 10 | 11 | 12 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/subnav/subnav.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/add-on/multi-instance-skeleton/ui/src/main/subnav/subnav.component.scss -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/main/subnav/subnav.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 VMware, Inc. All rights reserved. VMware Confidential 3 | */ 4 | 5 | import { Component, Inject, OnDestroy } from "@angular/core"; 6 | import {EXTENSION_ASSET_URL} from '@vcd/sdk'; 7 | 8 | @Component({ 9 | selector: "plugin-subnav", 10 | templateUrl: "./subnav.component.html", 11 | host: {'class': 'content-container'}, 12 | styleUrls: ["subnav.component.scss"], 13 | }) 14 | export class SubnavComponent implements OnDestroy { 15 | navItems: any[] = [ 16 | { routerLink: "./status", iconShape: "help-info", labelKey: "subnav.menu.status" }, 17 | { routerLink: "./about", iconShape: "helix", labelKey: "subnav.menu.about" } 18 | ]; 19 | 20 | constructor(@Inject(EXTENSION_ASSET_URL) public assetUrl: string) {} 21 | 22 | ngOnDestroy(): void { 23 | console.warn("[Showcase 3.0]", `${this.constructor.name} destroyed`); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/plugin.main.ts: -------------------------------------------------------------------------------- 1 | export * from 'src/main/showcase-plugin.module'; -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/public/assets/monkey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/add-on/multi-instance-skeleton/ui/src/public/assets/monkey.jpg -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/public/i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "en": { 3 | "simple.title": "Simple Top Level Navigation Plugin Showcase", 4 | "simple.login.info": "Logged in {0} as {1}", 5 | "tos.title": "Terms of Service", 6 | "tos.1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tempus urna et pharetra pharetra massa massa. Ipsum consequat nisl vel pretium. Sagittis id consectetur purus ut faucibus pulvinar elementum integer enim. Nulla pellentesque dignissim enim sit amet. Ultrices in iaculis nunc sed augue. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus. Sagittis eu volutpat odio facilisis mauris sit amet massa. Dolor magna eget est lorem ipsum. Eu mi bibendum neque egestas congue. Mi ipsum faucibus vitae aliquet nec ullamcorper sit amet. Quisque sagittis purus sit amet. Sem nulla pharetra diam sit amet nisl.", 7 | "tos.2": "Faucibus et molestie ac feugiat sed lectus vestibulum mattis. Rhoncus urna neque viverra justo nec ultrices dui. Gravida dictum fusce ut placerat. Facilisis gravida neque convallis a cras semper. Quis eleifend quam adipiscing vitae proin sagittis nisl rhoncus. Vitae auctor eu augue ut lectus arcu bibendum at. Sociis natoque penatibus et magnis. Eget lorem dolor sed viverra. Non curabitur gravida arcu ac. Viverra maecenas accumsan lacus vel facilisis volutpat est. Et malesuada fames ac turpis egestas sed tempus urna et. Viverra ipsum nunc aliquet bibendum enim. Nisl rhoncus mattis rhoncus urna neque. Risus in hendrerit gravida rutrum quisque non tellus orci. Orci eu lobortis elementum nibh. Eget mi proin sed libero enim sed faucibus turpis in.", 8 | "nav.label": "Showcase Plugin", 9 | "nav.description": "A basic plugin project", 10 | "subnav.menu.status": "Status", 11 | "subnav.menu.about": "About", 12 | "subnav.status.content": "Current time is {0} and the plugin status is WORKING", 13 | "subnav.about.content.1": "This is an example plugin that showcases sub-navigation, globalization, and styling", 14 | "subnav.about.content.2": "To build your own plugin, clone the seed project at https://github.com/vmware/vcd-ext-sdk", 15 | "vm.action.backup": "Backup", 16 | "vapp.action.restore": "Restore" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "urn": "vmware:vcloud:plugin:vmware:skeleton", 3 | "name": "Skeleton Solution UI Element", 4 | "manifestVersion": "4.0.0", 5 | "containerVersion": "9.5.0", 6 | "version": "1.0.0", 7 | "scope": [ 8 | "service-provider", 9 | "tenant" 10 | ], 11 | "permissions": [], 12 | "description": "Skeleton Add-On UI Plugin", 13 | "vendor": "VMware", 14 | "license": "BSD-2-Clause", 15 | "link": "http://www.vmware.com/support", 16 | "module": "SubNavModule", 17 | "route": "skeleton", 18 | "extensionPoints": [{ 19 | "urn": "vmware:vcloud:vm-action:backup3", 20 | "type": "vm-action", 21 | "name": "%nav.label.vm.backup.action%", 22 | "description": "%nav.description.vm.backup.action%", 23 | "component": "VmBackupActionComponent" 24 | }, 25 | { 26 | "urn": "vmware:vcloud:vapp-action:restore3", 27 | "type": "vapp-action", 28 | "name": "vApp Restore Action 3", 29 | "description": "Example of adding an action to vApps", 30 | "component": "VappRestoreActionComponent" 31 | }, 32 | { 33 | "urn": "vmware:vcloud:datacenter:sample3", 34 | "type": "datacenter-overview", 35 | "name": "Custom Datacenters", 36 | "description": "Display custom information in the Datacenter Overview", 37 | "component": "DatacenterContainerComponent" 38 | }, 39 | { 40 | "urn": "vmware:vcloud:datacenter:sample3-module", 41 | "type": "navigation:datacenter:overview", 42 | "name": "Custom Datacenters with Module (New type)", 43 | "description": "Display custom information in the Datacenter Overview", 44 | "component": "SubnavComponent", 45 | "module": "SubNavModule", 46 | "route": "vdc-overview-plugin3" 47 | }, 48 | { 49 | "urn": "vmware:vcloud:datacenter:sample3", 50 | "type": "navigation:datacenter:overview", 51 | "name": "Custom Datacenters (New type)", 52 | "description": "Display custom information in the Datacenter Overview", 53 | "component": "DatacenterContainerComponent" 54 | }, 55 | { 56 | "urn": "urn:vmware:vcd:component-scope:primary-navigation3", 57 | "type": "navigation:primary", 58 | "name": "%nav.label.primary.navigation%", 59 | "description": "%nav.description.primary.navigation%", 60 | "module": "SubNavModule", 61 | "route": "primary-plugin3" 62 | }, 63 | { 64 | "urn": "urn:vmware:vcd:component-scope:simple-navigation3", 65 | "type": "navigation:primary", 66 | "name": "%nav.label.primary.simple.navigation%", 67 | "description": "%nav.description.primary.simple.navigation%", 68 | "module": "SimpleNavModule", 69 | "route": "primary-simple-plugin3" 70 | }, { 71 | "urn": "urn:vmware:vcd:component-scope:applications3", 72 | "type": "navigation:applications", 73 | "name": "Application Extension Point (New)", 74 | "description": "Display custom information in the Application Tab", 75 | "component": "ApplicationComponent" 76 | }, 77 | { 78 | "urn": "urn:vmware:vcd:component-scope:applications:applications-module", 79 | "type": "navigation:applications", 80 | "name": "Application Extension Point with Module (New)", 81 | "description": "Display custom information in the Application Tab", 82 | "component": "SubnavComponent", 83 | "module": "SubNavModule", 84 | "route": "applications-plugin3" 85 | }, 86 | { 87 | "urn": "urn:vmware:vcd:component-scope:datacenter-compute3", 88 | "type": "navigation:datacenter:compute", 89 | "name": "Datacenter Compute Extension Point (New)", 90 | "description": "Display custom information in the Datacenter -> Compute section", 91 | "component": "DatacenterComputeComponent" 92 | }, 93 | { 94 | "urn": "urn:vmware:vcd:component-scope:applications:applications-compute-module", 95 | "type": "navigation:datacenter:compute", 96 | "name": "Datacenter Compute Extension Point with Module (New)", 97 | "description": "Display custom information in the Datacenter -> Compute section", 98 | "component": "SubnavComponent", 99 | "module": "SubNavModule", 100 | "route": "compute-plugin3" 101 | }, 102 | { 103 | "urn": "urn:vmware:vcd:component-scope:datacenter-network3", 104 | "type": "navigation:datacenter:network", 105 | "name": "Datacenter Network Extension Point (New)", 106 | "description": "Display custom information in the Datacenter -> Network section", 107 | "component": "DatacenterNetworkComponent" 108 | }, 109 | { 110 | "urn": "urn:vmware:vcd:component-scope:datacenter-network3-module", 111 | "type": "navigation:datacenter:network", 112 | "name": "Datacenter Network Extension Point with Module (New)", 113 | "description": "Display custom information in the Datacenter -> Network section", 114 | "component": "SubnavComponent", 115 | "module": "SubNavModule", 116 | "route": "network-plugin3" 117 | }, 118 | { 119 | "urn": "urn:vmware:vcd:component-scope:datacenter-storage3", 120 | "type": "navigation:datacenter:storage", 121 | "name": "Datacenter Storage Extension Point (New)", 122 | "description": "Display custom information in the Datacenter -> Storage section", 123 | "component": "DatacenterStorageComponent" 124 | }, 125 | { 126 | "urn": "urn:vmware:vcd:component-scope:datacenter-storage3-module", 127 | "type": "navigation:datacenter:storage", 128 | "name": "Datacenter Storage Extension Point with Module (New)", 129 | "description": "Display custom information in the Datacenter -> Storage section", 130 | "component": "SubnavComponent", 131 | "module": "SubNavModule", 132 | "route": "storage-plugin3" 133 | }, 134 | { 135 | "urn": "vmware:vcloud:vapp:create3", 136 | "type": "create-vapp", 137 | "name": "vApp Create Extension Point", 138 | "description": "Example of vApp Create Extensibility", 139 | "component": "VappCreateWizardExtensionPointComponent", 140 | "render": { 141 | "after": ".vapp-name-extension-point" 142 | } 143 | }, 144 | { 145 | "urn": "vmware:vcloud:vm:create3", 146 | "type": "create-vm", 147 | "name": "VM Create Extension Point", 148 | "description": "Example of VM Create Extensibility", 149 | "component": "VmCreateWizardExtensionPointComponent", 150 | "render": { 151 | "after": ".vm-description" 152 | } 153 | }, 154 | { 155 | "urn": "vmware:vcloud:org:create3", 156 | "type": "create-org", 157 | "name": "Org Create Extension Point", 158 | "description": "Example of Org Create Extensibility", 159 | "component": "OrgCreateWizardExtensionPointComponent", 160 | "render": { 161 | "after": ".description" 162 | } 163 | }, 164 | { 165 | "urn": "urn:vmware:vcd:component-scope:networking3", 166 | "type": "navigation:networking", 167 | "name": "Networking (New type)", 168 | "description": "Display custom information in the Networking", 169 | "component": "NetworkingComponent" 170 | }, 171 | { 172 | "urn": "urn:vmware:vcd:component-scope:networking-module3", 173 | "type": "navigation:networking", 174 | "name": "Networking with Module (New type)", 175 | "description": "Display custom information in the Networking", 176 | "component": "SubnavComponent", 177 | "module": "SubNavModule", 178 | "route": "networking3" 179 | }, 180 | { 181 | "urn": "urn:vmware:vcd:component-scope:network-details3", 182 | "type": "navigation:network-details", 183 | "name": "Network Details (New type)", 184 | "description": "Display custom information in the Network Details", 185 | "component": "NetworkingComponent" 186 | }, 187 | { 188 | "urn": "urn:vmware:vcd:component-scope:network-details-module3", 189 | "type": "navigation:network-details", 190 | "name": "Network Details with Module (New type)", 191 | "description": "Display custom information in the Network Details", 192 | "component": "SubnavComponent", 193 | "module": "SubNavModule", 194 | "route": "networking-details3" 195 | }, 196 | { 197 | "urn": "vmware:vcloud:ip-bindings:create-a", 198 | "type": "create-ip-bindings", 199 | "name": "Create IP Bindings Extension Point A", 200 | "description": "Example of Create IP Bindings Extensibility", 201 | "component": "IpBindingsCreateWizardExtensionPointComponent", 202 | "render": { 203 | "after": ".dns-servers" 204 | } 205 | }, 206 | { 207 | "urn": "vmware:vcloud:ip-bindings:create-b", 208 | "type": "create-ip-bindings", 209 | "name": "Create IP Bindings Extension Point B", 210 | "description": "Example of Create IP Bindings Extensibility", 211 | "component": "IpBindingsCreateWizardExtensionPointComponent", 212 | "render": { 213 | "after": ".dns-servers" 214 | } 215 | } 216 | ], 217 | "locales": { 218 | "en": { 219 | "nav.label.registered.extension": "Registered Nav Extension", 220 | "nav.description.registered.extension": "Description for the Registered Nav Extension", 221 | "nav.label.vm.backup.action": "VM Backup Action 3.0", 222 | "nav.description.vm.backup.action": "Example of adding an action to VMs 3.0", 223 | "nav.label.primary.navigation": "Primary Navigation Extension Point 3.0", 224 | "nav.description.primary.navigation": "Example of adding Primary Navigation Plugin 3.0", 225 | "nav.label.primary.simple.navigation": "Simple Navigation Plugin Module 3.0", 226 | "nav.description.primary.simple.navigation": "Example of adding Simple Navigation Plugin 3.0" 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "ES2020", 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "lib": ["ES2020", "dom"], 8 | "sourceMap": true, 9 | "declaration": false, 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "removeComments": false, 13 | "noImplicitAny": true, 14 | "resolveJsonModule": true, 15 | "allowSyntheticDefaultImports": true, 16 | "skipLibCheck": true, 17 | "typeRoots": [ 18 | "./node_modules/@types" 19 | ], 20 | "baseUrl": "./" 21 | }, 22 | "exclude": [ 23 | "node_modules", 24 | "dist" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts", 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /add-on/multi-instance-skeleton/ui/vcd-sdk-polyfill.js: -------------------------------------------------------------------------------- 1 | class EntityActionExtensionComponent {} 2 | class WizardExtensionComponent {} 3 | class WizardExtensionWithValidationComponent {} 4 | class WizardExtensionWithContextComponent {} 5 | 6 | window["System"].registry.set("@vcd/common", window["System"].newModule({ 7 | EntityActionExtensionComponent, 8 | WizardExtensionComponent, 9 | WizardExtensionWithValidationComponent, 10 | WizardExtensionWithContextComponent, 11 | API_ROOT_URL: "API_ROOT_URL", 12 | FLEX_APP_URL: "FLEX_APP_URL", 13 | SESSION_SCOPE: "SESSION_SCOPE", 14 | SESSION_ORGANIZATION: "SESSION_ORGANIZATION", 15 | EXTENSION_ASSET_URL: "EXTENSION_ASSET_URL", 16 | EXTENSION_ROUTE: "EXTENSION_ROUTE", 17 | SDK_MODE: "SDK_MODE", 18 | SESSION_ORG_ID: "SESSION_ORG_ID", 19 | })); -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/defined-entities-lifecycle.md: -------------------------------------------------------------------------------- 1 | # Runtime Defined Entities 2 | 3 | A Runtime Defined Entity (RDE) is a package that contains a JSON payload. 4 | Each RDE is an instance of an RDE Type that specifies the format of the JSON payload using a JSON Schema. 5 | 6 | Examples of how an RDE can be created, retrieved, and modified can be found in the 7 | [Defined Entity Operations](defined-entity-operations.md) section. 8 | 9 | ## Defined Entity Lifecycle 10 | 11 | Defined Entities pass through three phases during their lifecycle: 12 | 13 | - `Creation` 14 | - `Utilization` 15 | - `Deletion` 16 | 17 | In simple cases, both the Creation and the Deletion phases can be immediate. In other cases, they can be long-running processes. 18 | 19 | ### Defined Entity States 20 | 21 | Each Defined Entity has a state that indicates what phase it is in. 22 | 23 | The state of a Defined Entity is contained in the `entityState` property. 24 | The possible values are the following: 25 | 26 | - `PRE_CREATED` 27 | - `RESOLVED` 28 | - `RESOLUTION_ERROR` 29 | - `IN_DELETION` 30 | 31 | ![Defined Entity Lifecycle](../../images/rde_lifecycle.png) 32 | 33 | ### Creation Phase 34 | 35 | The Creation Phase starts when [the Defined Entity is created](defined-entity-operations.md#entity-creation). During that phase the external resources that would be represented by the Defined Entity are typically allocated and initialized. 36 | 37 | The state of the entity in that phase is `PRE_CREATED`. 38 | 39 | During the Creation Phase the entity contents do not have to be valid with respect to the JSON schema of the entity type. That allows the information about the represented resource to be collected and gradually added to the entity contents as external resources are allocated, for example. 40 | 41 | The initialization process and the content update can be done either explicity via the API 42 | or automatically via a [PostCreate behavior hook](rde-hooks.md#post-create-behavior-hook). 43 | 44 | Note: An entity cannot be deleted while it is in the Creation Phase. It has to be resolved first, either successfully or unsuccessfully. 45 | 46 | ### Utilization Phase 47 | 48 | Once the needed Defined Entity information has been fully collected and the entity contents have been updated to satisfy the requirements of the type's JSON Schema, the entity can then be [resolved](defined-entity-operations.md#entity-resolution). 49 | 50 | Note: If the initally provided entity contents already contain all of the necessary information, it is possible to skip the Creation Phase and [immediately attempt to resolve the entity upon creation](defined-entity-operations.md#immediate-entity-resolution-after-creation). 51 | 52 | Once the resolution process validates the entity contents against the JSON schema of the entity type, 53 | the entity state is changed to `RESOLVED`. If the validation fails, then the entity will be placed in a `RESOLUTION_ERROR` state. 54 | 55 | A resolved entity is considered complete and ready for use. It becomes possible to perform operations on it such as behavior invocations. 56 | 57 | If a resolved entity is updated, its new contents are re-validated immediately and the entity state is changed to `RESOLVED` or `RESOLUTION_ERROR` depending on validation result. 58 | 59 | ### Deletion Phase 60 | 61 | A resolved entity [can be deleted](defined-entity-operations.md#entity-deletion) immediately if no cleanup is needed. 62 | 63 | If there are related resources that need to be released, however, then [a multi-stage deletion process](rde-hooks.md#multi-stage-rde-deletion) is initiated and the entity is placed in the `IN_DELETION` state. The entity can be fully deleted once the cleanup process completes. 64 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/defined-entities-overview.md: -------------------------------------------------------------------------------- 1 | # Runtime Defined Entities Framework 2 | 3 | ## Introduction 4 | 5 | The VMware Cloud Director extensibility framework can be used in many different 6 | ways, for example: 7 | 8 | - Clients can add custom functionality to VMware Cloud Director that addresses 9 | their specific use cases 10 | 11 | - ISVs can create extensions that tightly integrate their software with 12 | VMware Cloud Director 13 | 14 | - Third Parties can create and distribute extensions offering new 15 | value-added functionality 16 | 17 | - Service Providers could deliver new differentiating capabilities to 18 | their customers 19 | 20 | Runtime Defined Entities can greatly simplify the development of 21 | extensions by providing a built-in state management mechanism and the 22 | definition of custom operation execution functionality within Cloud 23 | Director. 24 | 25 | VMware Cloud Director supports several pre-defined entity types – VMs, vApps, 26 | Networks, etc. The Runtime Defined Entities (RDE) functionality allows 27 | clients to define their own custom entity types with custom 28 | functionality. 29 | 30 | ## Typical RDE Uses 31 | 32 | Several typical ways RDEs can be used by extensions are the following: 33 | 34 | - Represent an external resource, for example a Container Cluster, and keep references to its resources in a strongly typed JSON document. (c.f. [Defined Entity Type schema](defined-entity-types.md)) 35 | 36 | - Persist the state of an Extension without the need of an external database. (c.f. [Defined Entities](defined-entities-lifecycle.md)) 37 | 38 | - Use the RDE Access Control mechanisms to manage the users’ access to 39 | resources. (c.f. [RDE Access Control](rde-access-control.md)) 40 | 41 | - Use RDE instances as a Desired State interface to an external system. 42 | (c.f. [Field-level Access Control](rde-access-control.md#field-level-rde-access-contol-and-encryption)) 43 | 44 | RDEs also provide a powerful [versioning mechanism](rde-versions.md), thus simplifying 45 | the management of the extensions’ lifecycle, especially when used in the 46 | context of [Solution Add-Ons](../../extension-sdk/extension-sdk.md). 47 | 48 | ## Runtime Defined Entities Concepts 49 | 50 | A Runtime Defined Entity (RDE) is a package that contains a JSON payload. 51 | 52 | Each RDE is an instance of an [RDE Type](defined-entity-types.md) that specifies 53 | the format of the JSON payload using a JSON Schema. 54 | 55 | An RDE Type may implement a number of [RDE Interfaces](defined-interfaces.md) that categorize it 56 | conceptually, for example – “Container Cluster”. An RDE Interface may 57 | also define [RDE Behaviors](behaviors-general-concepts.md) that can be executed on RDEs that implement it. 58 | 59 | ![Example Defined Entity Interface, Type, and instances](../../images/rde_concepts.png) 60 | 61 | [RDE Behaviors](behaviors-general-concepts.md) are custom executable operations that can be performed on a 62 | Runtime Defined Entity. Behaviors can be defined by clients via several 63 | different mechanisms. RDE Types can be configured to automatically 64 | execute specific Behaviors on certain events during the RDE lifecycle. 65 | 66 | RDE Types, their RDE instances, as well as RDE Interfaces are [versioned](rde-versions.md). 67 | They ensure that the schema of an RDE does not change once it is created. They also offer a path to transfer to other versions. 68 | 69 | ### Strict Tenancy 70 | 71 | Runtime Defined Entities follow a strict tenancy model. 72 | 73 | An RDE created in a specific tenant cannot be moved to another tenant. 74 | Access to the RDE cannot be shared with users outside the tenant. 75 | 76 | ## Links to the RDE Components Documentation 77 | 78 | - [RDE Interfaces](defined-interfaces.md) 79 | - [RDE Types](defined-entity-types.md) 80 | - [RDE Lifecycle](defined-entities-lifecycle.md) 81 | - [RDE Operations](defined-entity-operations.md) 82 | - [RDE Queries](rde-queries.md) 83 | - [RDE Behaviors](behaviors-general-concepts.md) 84 | - [RDE Access Control](rde-access-control.md) 85 | - [RDE Hooks](rde-hooks.md) 86 | - [RDE Versioning](rde-versions.md) 87 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/defined-entity-types.md: -------------------------------------------------------------------------------- 1 | # Defined Entity Types 2 | 3 | A Defined Entity Type describes the content structure of the defined entities that are of that type 4 | using a JSON schema. 5 | 6 | In addition, the interfaces the type implements define the behaviors that can be executed on its entities. 7 | 8 | ## Definition 9 | 10 | A new Defined Entity Type can be created via [an API call that specifies its definition](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entityTypes/post/) 11 | 12 | Here is an example Defined Entity Type definition: 13 | 14 | ```json 15 | { 16 | "name": "Basic Conainer Cluster", 17 | "vendor": "clusterVendorA", 18 | "nss": "basicContainerCluster", 19 | "version": "1.0.0", 20 | "interfaces": ["urn:vcloud:interface:clusterVendorA:containerCluster:1.0.0"], 21 | "schema": { 22 | "cluster" : { 23 | "type" : "object", 24 | "properties" : { 25 | "name" : { "type" : "string" }, 26 | "nodes" : {"type" : "array", "items" : { "type" : "object" }} 27 | }, 28 | "required": [ "name" ] 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | The properties `nss`, `vendor`, and `version` uniquely identify the type. Once the type is created, these properties cannot be modified. 35 | 36 | The key Defined Entity Type properties are the following: 37 | 38 | ### Name 39 | 40 | The human-readable name of the entity type. It may contain spaces and special symbols. 41 | 42 | ### Vendor 43 | 44 | The ID of the vendor providing the entity type. It must be alpha-numeric. 45 | 46 | ### NSS 47 | 48 | The ID of the entity type. It must be alpha-numeric. 49 | 50 | ### Version 51 | 52 | The version of the entity type. It must follow the [Semantic Versioning](https://semver.org/) format. 53 | See the [RDE Versioning](rde-versions.md) section for more details. 54 | 55 | Once an instance of a defined entity type has been created, the entity type cannot be modified anymore. This preserves the consistency of the entities in time. 56 | If changes are needed, [a new version](rde-versions.md) of the type must be created. 57 | 58 | ### Schema 59 | 60 | The structure of the type entities is defined via a [JSON schema](https://json-schema.org/) that specifies the fields and attributes to be included in a given entity. We support draft-04, draft-06 and draft-07 versions of the JSON Schema. 61 | 62 | The contents of a newly created entity do not have to match the type schema [immediately upon the entity creation](defined-entities-lifecycle.md#creation-phase). However, the contents must match the schema for the entity to be resolved. 63 | 64 | #### Custom additions to the JSON Schema 65 | 66 | Fields defined in the JSON schema of a Defined Entity Type can be annotated with `x-vcloud-restricted` to restrict the access to the fields or to mark them as encrypted. For example: 67 | 68 | ```json 69 | ... 70 | "clusterState" : { 71 | "type" : "string", 72 | "x-vcloud-restricted" : ["protected", "secure"] 73 | } 74 | ... 75 | ``` 76 | 77 | More information about the possible field annotations and their values can be found in the [Field-level RDE Access Contol and Encryption](rde-access-control.md#field-level-rde-access-contol-and-encryption) section. 78 | 79 | ### Interfaces 80 | 81 | The list of IDs of the [interfaces](defined-interfaces.md) that the type implements. 82 | 83 | ### Hooks 84 | 85 | The behaviors that must be bound to the [type lifecycle event hooks](rde-hooks.md). 86 | 87 | ## Example API calls 88 | 89 | ### Create a Defined Entity Type 90 | 91 | A new Defined Entity Type can be created by authorized clients via [an API call](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entityTypes/post/). For example: 92 | 93 | ```text 94 | POST /cloudapi/1.0.0/entityTypes 95 | ``` 96 | 97 | ```json 98 | { 99 | "name": "Basic Conainer Cluster", 100 | "vendor": "clusterVendorA", 101 | "nss": "basicContainerCluster", 102 | "version": "1.0.0", 103 | "schema": { 104 | "cluster" : { 105 | "type" : "object", 106 | "properties" : { 107 | "name" : { "type" : "string" }, 108 | "nodes" : {"type" : "array", "items" : { "type" : "object" }} 109 | }, 110 | "required": [ "name" ] 111 | } 112 | } 113 | } 114 | ``` 115 | 116 | Response: 117 | 118 | ```json 119 | { 120 | "name": "Basic Conainer Cluster", 121 | "id": "urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0", 122 | "vendor": "clusterVendorA", 123 | "nss": "basicContainerCluster", 124 | "version": "1.0.0", 125 | "schema": { 126 | "cluster" : { 127 | "type" : "object", 128 | "properties" : { 129 | "name" : { "type" : "string" }, 130 | "nodes" : {"type" : "array", "items" : { "type" : "object" }} 131 | }, 132 | "required": [ "name" ] 133 | } 134 | }, 135 | "interfaces": [], 136 | "hooks": null, 137 | "inheritedVersion": null, 138 | "readonly": false 139 | } 140 | ``` 141 | 142 | ### Update a Defined Entity Type 143 | 144 | A Defined Entity Type definition can be updated by authorized clients via [an API call](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entityTypes/id/put/). For example: 145 | 146 | The `vendor`, `nss`, and `schema` cannot be changed when updating the type. 147 | 148 | A Defined Entity Type can only be updated if no entities exist that are instances of the type. 149 | 150 | ```text 151 | PUT /cloudapi/1.0.0/entityTypes/ 152 | ``` 153 | 154 | ```json 155 | { 156 | "name": "Simple Conainer Cluster", 157 | "vendor": "clusterVendorA", 158 | "nss": "basicContainerCluster", 159 | "version": "1.0.0", 160 | "schema": { 161 | "cluster" : { 162 | "type" : "object", 163 | "properties" : { 164 | "name" : { "type" : "string" }, 165 | "nodes" : {"type" : "array", "items" : { "type" : "object" }} 166 | }, 167 | "required": [ "name" ] 168 | } 169 | } 170 | } 171 | ``` 172 | 173 | Response: 174 | 175 | ```json 176 | { 177 | "name": "Simple Conainer Cluster", 178 | "id": "urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0", 179 | "vendor": "clusterVendorA", 180 | "nss": "basicContainerCluster", 181 | "version": "1.0.0", 182 | "schema": { 183 | "cluster" : { 184 | "type" : "object", 185 | "properties" : { 186 | "name" : { "type" : "string" }, 187 | "nodes" : {"type" : "array", "items" : { "type" : "object" }} 188 | }, 189 | "required": [ "name" ] 190 | } 191 | }, 192 | "interfaces": [], 193 | "hooks": null, 194 | "inheritedVersion": null, 195 | "readonly": false 196 | } 197 | ``` 198 | 199 | ### Delete a Defined Entity Type 200 | 201 | A Defined Entity Type definition can be deleted by authorized clients via [an API call](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entityTypes/id/delete/): 202 | 203 | ```text 204 | DELETE /cloudapi/1.0.0/entityTypes/ 205 | ``` 206 | 207 | A Defined Entity Type can only be deleted if no entities exist that are instances of the type. 208 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/defined-interfaces.md: -------------------------------------------------------------------------------- 1 | # Defined Interfaces 2 | 3 | Interfaces represent categories of Defined Entity definitions. 4 | 5 | For example, a `ContainerCluster` interface could represent the concept of a Cluster of Containers. 6 | Multiple [Defined Entity Types](defined-entity-types.md) may implement that interface, each representing a different type of Container Cluster. 7 | 8 | Interfaces can also specify custom defined actions called [behaviors](behaviors-general-concepts.md) that can be used to perform operations on Defined Entities of the corresponding categories. 9 | 10 | For example, a `ContainerCluster` interface could define an `addNode` behavior. Defined Entity Types implementing the interface can either use the default `addNode` behavior definition in the interface or they can override it to make it specific to their implmentation. 11 | 12 | ## Definition 13 | 14 | An example simple interface definition: 15 | 16 | ```json 17 | { 18 | "name": "Container Cluster", 19 | "vendor": "clusterVendorA", 20 | "nss": "containerCluster", 21 | "version": "1.0.0" 22 | } 23 | ``` 24 | 25 | The properties `nss`, `vendor`, and `version` uniquely identify the interface. Once the interface is created, these properties cannot be modified. 26 | 27 | ## Example API calls 28 | 29 | ### Create an interface 30 | 31 | A new interface can be created by authorized clients via [an API call](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/interfaces/post/). For example: 32 | 33 | ```text 34 | POST /cloudapi/1.0.0/interfaces 35 | ``` 36 | 37 | ```json 38 | { 39 | "name": "Container Cluster", 40 | "vendor": "clusterVendorA", 41 | "nss": "containerCluster", 42 | "version": "1.0.0" 43 | } 44 | ``` 45 | 46 | Response: 47 | 48 | ```json 49 | { 50 | "name": "Container Cluster", 51 | "id": "urn:vcloud:interface:clusterVendorA:containerCluster:1.0.0", 52 | "vendor": "clusterVendorA", 53 | "nss": "containerCluster", 54 | "version": "1.0.0", 55 | "readonly": false 56 | } 57 | ``` 58 | 59 | The `id` field in the response represents how the interface will be referenced in other API requests. 60 | 61 | The `readonly` flag is added automatically to the interface representation and indicates whether the definition is custom or built-in. It cannot be set via the API. 62 | 63 | ### Update an interface 64 | 65 | An interface can be updated by authorized clients via [an API call](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/interfaces/id/put/). For example: 66 | 67 | ```text 68 | PUT /cloudapi/1.0.0/interfaces/ 69 | ``` 70 | 71 | ```json 72 | { 73 | "name": "Cluster of Containers", 74 | "vendor": "clusterVendorA", 75 | "nss": "containerCluster", 76 | "version": "1.0.0" 77 | } 78 | ``` 79 | 80 | Response: 81 | 82 | ```json 83 | { 84 | "name": "Cluster of Containers", 85 | "id": "urn:vcloud:interface:clusterVendorA:containerCluster:1.0.0", 86 | "vendor": "clusterVendorA", 87 | "nss": "containerCluster", 88 | "version": "1.0.0", 89 | "readonly": false 90 | } 91 | ``` 92 | 93 | An interface cannot be updated if it is in use. An interface is in use if there is at least one RDE instance created in any of the RDE Types which implement the interface. 94 | 95 | ### Add a behavior to an interface 96 | 97 | Intefaces can define a set of [RDE Behaviors](behaviors-general-concepts.md). 98 | Behaviors are typically specified as part of the interface definition. 99 | 100 | Behaviors can also be added to the interface [individually](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/interfaces/id/behaviors/post/) or [as a set](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/interfaces/id/behaviors/put/). 101 | 102 | ```text 103 | POST /cloudapi/1.0.0/interfaces//behaviors 104 | ``` 105 | 106 | ```json 107 | { 108 | "name": "addNode", 109 | "execution" : { 110 | "type": ..., 111 | ... 112 | } 113 | } 114 | ``` 115 | 116 | Response: 117 | 118 | ```json 119 | { 120 | "name": "addNode", 121 | "id": "urn:vcloud:behavior-interface:addNode:clusterVendorA:containerCluster:1.0.0", 122 | "ref": "urn:vcloud:behavior-interface:addNode:clusterVendorA:containerCluster:1.0.0", 123 | "execution": { 124 | "type": ..., 125 | ... 126 | } 127 | } 128 | ``` 129 | 130 | The `id` and `ref` fields in the response represent how the specific behavior can be referenced in other API requests. The difference between them is explained in the [behavior id](behaviors-general-concepts.md#behavior-id-vs-ref) section. 131 | 132 | The behaviors of an interface cannot be updated if the interface is in use. An interface is in use if there is at least one RDE instance created in any of the RDE Types that implement the interface. 133 | 134 | ### Delete an interface 135 | 136 | An interface can be updated by authorized clients via [an API call](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/interfaces/id/delete/). 137 | 138 | ```text 139 | DELETE /cloudapi/1.0.0/interfaces/ 140 | ``` 141 | 142 | An interface cannot be deleted if it is in use. An interface is in use if there is at least one RDE instance created in any of the RDE Types which implement the interface. 143 | 144 | ## Access to Defined Interface Operations 145 | 146 | | Operation | Required Right | 147 | | ----------------------------------- | --------------------------------------- | 148 | | View and query interface | All users have the necessary rights. | 149 | | Create interface | __Create new custom entity definition__ | 150 | | Edit interface | __Edit custom entity definition__ | 151 | | Delete interface | __Delete custom entity definition__ | 152 | | View and query interface behaviors | __View custom entity definitions__ | 153 | | Create a new behavior in interface | __Create new custom entity definition__ | 154 | | Update behavior in interface | __Edit custom entity definition__ | 155 | | Delete behavior in interface | __Edit custom entity definition__ | 156 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/no-op-behaviors.md: -------------------------------------------------------------------------------- 1 | # No-op Behaviors 2 | 3 | No-op behaviors, as the name suggests, are behaviors which do not perform any operation as part of their execution. They always succeed or always fail (depending on how they are configured). This type of behaviors can be used for testing purposes. 4 | 5 | ## Behavior Definition 6 | 7 | The no-op behavior execution type is `noop`. By default no-op behavior execution always succeeds and returns a `null` result. However, a no-op behavior can be configured to return a custom result. Also no-op behaviors can be configured to always fail with custom error. 8 | 9 | ### Special execution properties 10 | 11 | There are some special properties which can be set in the behavior definition `execution_properties` section. 12 | 13 | - `returnValue` - any - this property is used to set a custom result value for the behavior. 14 | - `returnError` - Object - this property is used to set a custom error for the behavior: 15 | 16 | ```json 17 | "returnError": { 18 | "majorErrorCode": 501, 19 | "minorErrorCode": "NOT_IMPLEMENTED", 20 | "message": "Not Implemented" 21 | } 22 | ``` 23 | 24 | Default no-op behavior definition: 25 | 26 | ```json 27 | { 28 | "name": "testNoOp", 29 | "execution": { 30 | "type": "noop" 31 | } 32 | } 33 | ``` 34 | 35 | No-op behavior definition returning custom result: 36 | 37 | ```json 38 | { 39 | "name": "testNoOpWithReturnArgument", 40 | "execution": { 41 | "type": "noop", 42 | "execution_properties": { 43 | "returnValue": "Successful Behavior Execution" 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | No-op behavior definition for behavior failing with custom error: 50 | 51 | ```json 52 | { 53 | 54 | "name": "testNoOpError", 55 | "execution": { 56 | "type": "noop", 57 | "execution_properties": { 58 | "returnError": { 59 | "majorErrorCode": 501, 60 | "minorErrorCode": "NOT_IMPLEMENTED", 61 | "message": "Not Implemented" 62 | } 63 | } 64 | } 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/rde-queries.md: -------------------------------------------------------------------------------- 1 | # RDE Queries 2 | 3 | Runtime Defined Entities can be queried using several REST API requests. 4 | 5 | The queries support the standart VMware Cloud Director REST API mechanisms for paging, sorting, and filtering using the `page`, `pageSize`, `sortAsc`/`sortDesc`. and `filter` query parameters. 6 | 7 | ## Query Entities by Type 8 | 9 | ### Query Entities by Type ID 10 | 11 | A user can perform [a query to get the accessible entities of a particular type](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entities/types/typeId/get/). 12 | For example, for the type `urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0` the query would be: 13 | 14 | ```text 15 | GET https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/types/urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0 16 | ``` 17 | 18 | Response: 19 | 20 | ```json 21 | { 22 | "resultTotal": 37, 23 | "pageCount": 2, 24 | "page": 1, 25 | "pageSize": 25, 26 | "values": [ 27 | { 28 | "id": "urn:vcloud:entity:clusterVendorA:basicContainerCluster:b80dd0a4-d69f-407a-8577-99b6010e5847", 29 | "entityType": "urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0", 30 | "name": "exhibitionEntity", 31 | "entity": { 32 | "cluster": { 33 | "name": "exhibitionCluster", 34 | "nodes": [ 35 | {"name": "node-1", "ip": "10.244.0.1"} 36 | ] 37 | } 38 | }, 39 | "entityState": "PRE_CREATED", 40 | "owner": ..., 41 | "org": ... 42 | }, 43 | ... 44 | ] 45 | } 46 | ``` 47 | 48 | ### Query Entities by Type ID Segments 49 | 50 | When it is necessary to query entities not just by a single type version, but by 51 | several versions of that type, it is possible to [query the entities by type ID segments](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entities/types/vendor/nss/version/get/). 52 | 53 | This query allows the specification only of the version prefix, rather than the exact version. 54 | 55 | For example, if it is necessary to get not only the entities of the `1.0.0` version of the 56 | `urn:vcloud:type:clusterVendorA:basicContainerCluster` type, but all of `1.x` version, 57 | the following query can be used: 58 | 59 | ```text 60 | GET https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/types/clusterVendorA/basicContainerCluster/1 61 | ``` 62 | 63 | ## Query Entities by Interface 64 | 65 | It is sometimes necessary to query the entities that are of types that implement 66 | a specific interface. 67 | 68 | ### Query Entities by Interface ID 69 | 70 | A user can perform [a query to get all accessible entities of types that implement a particular interface](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entities/interfaces/interfaceId/get/). 71 | For example, for the interface `urn:vcloud:interface:vmware:k8s:1.0.0` the query would be: 72 | 73 | ```text 74 | GET https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/interfaces/urn:vcloud:interface:vmware:k8s:1.0.0 75 | ``` 76 | 77 | ### Query Entities by Interface ID Segments 78 | 79 | When it is necessary to query entities not just by a single interface version, but by 80 | several versions of that interface, it is possible to [query the entities by interface ID segments](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entities/interfaces/vendor/nss/version/get/). 81 | 82 | This query allows the specification only of the version prefix, rather than the exact version. 83 | 84 | For example, if it is necessary to get not only the entities of `1.0.0` version of the 85 | `urn:vcloud:interface:vmware:k8s` interface, but all of `1.0.x` versions, 86 | the following query can be used: 87 | 88 | ```text 89 | GET https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/interfaces/vmware/k8s/1.0 90 | ``` 91 | 92 | ## Tenancy 93 | 94 | Tenant users can only query entities within their own tenant. 95 | 96 | Provider users query only the entities in the 'System' organization by default. 97 | They can, however, perform queries in the context of a tenant using 98 | the `X-VMWARE-VCLOUD-TENANT-CONTEXT` request header to specify the tenant organization ID. 99 | 100 | For example: 101 | 102 | ```text 103 | X-VMWARE-VCLOUD-TENANT-CONTEXT: urn:vcloud:org:ea806c38-09e1-482e-8d6a-582a368fbeb3 104 | ``` 105 | 106 | ## Filtering 107 | 108 | The RDE queries use the standard VMware Cloud Director REST API filtering mechanism via the `filter` query parameter. It is possible to filter by the entity properties, 109 | as well as the entity content's properties. For example: 110 | 111 | Filtering by entity name: 112 | 113 | ```text 114 | GET 115 | https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entityTypes/urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0?filter=(name==exhibitionEntity) 116 | ``` 117 | 118 | Filtering by entity content: 119 | 120 | ```text 121 | GET 122 | https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entityTypes/urn:vcloud:type:clusterVendorA:basicContainerCluster:1.0.0?filter=(entity.cluster.name==exhibitionCluster) 123 | ``` 124 | 125 | The examples above are based on having an entity with the following entity contents: 126 | 127 | ```json 128 | { 129 | "type" : "object", 130 | "properties" : { 131 | "cluster" : {"$ref" : "#/definitions/Cluster" }, 132 | "clusterState" : {"$ref" : "#/definitions/ClusterState" }, 133 | "topProtectedAndSecureStatus" : { 134 | "type" : "string", 135 | "x-vcloud-restricted" : ["protected", "secure"] 136 | }, 137 | "privateStatus" : { 138 | "type" : "string", 139 | "x-vcloud-restricted" : "private" 140 | }, 141 | "protectedStatus" : { 142 | "type" : "object", 143 | "x-vcloud-restricted" : "protected", 144 | "properties": { 145 | "phase" : { 146 | "type" : "string" 147 | }, 148 | "privateAndSecure" : { 149 | "type" : "object", 150 | "x-vcloud-restricted" : ["private", "secure"], 151 | "properties": { 152 | "token" : { 153 | "type" : "string" 154 | } 155 | } 156 | }, 157 | "secureArray" : { 158 | "type": "array", 159 | "items": { 160 | "type": "object", 161 | "required": [ 162 | "values" 163 | ], 164 | "properties": { 165 | "values": { 166 | "type": "array", 167 | "items": { 168 | "anyOf": [ 169 | { 170 | "type": "string" 171 | }, 172 | { 173 | "type": "object" 174 | } 175 | ] 176 | }, 177 | "x-vcloud-restricted": [ 178 | "private", 179 | "secure" 180 | ] 181 | } 182 | } 183 | } 184 | } 185 | } 186 | }, 187 | "privateState" : { 188 | "$ref" : "#/definitions/ClusterState", 189 | "x-vcloud-restricted" : "private" 190 | } 191 | }, 192 | "definitions" : { 193 | "Cluster" : { 194 | "type" : "object", 195 | "properties" : { 196 | "name" : { "type" : "string" }, 197 | "nodes" : {"type" : "array", "items" : { "type" : "object" }} 198 | } 199 | }, 200 | "ClusterState" : { 201 | "type" : "object", 202 | "properties" : { 203 | "host" : { "type" : "string" }, 204 | "status" : { 205 | "type" : "string" 206 | } 207 | } 208 | } 209 | } 210 | } 211 | ``` 212 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/defined-entities/rde-versions.md: -------------------------------------------------------------------------------- 1 | # RDE Versioning 2 | 3 | ## Goal 4 | 5 | Runtime Defined Entity Interfaces and Types are versioned. There can be 6 | several RDE Interfaces and Types with the same Vendor and NSS, but with 7 | different Version IDs. For example: 8 | 9 | - urn:vcloud:type:vmware:tkgcluster:1.0.0 10 | - urn:vcloud:type:vmware:tkgcluster:1.1.0 11 | 12 | Each version has its own definition. For example, each RDE Type version 13 | can specify a different JSON Schema. 14 | 15 | The version mechanism preserves the consistency of the existing Runtime 16 | Defined Entity instances as the RDE definitions evolve. 17 | 18 | If RDE instances that are based on given Type and Interface 19 | versions exist, then those versions become immutable. 20 | 21 | If an extension is upgraded and needs to use enhanced functionality in 22 | its RDE instances, it can create a new version of its RDE Type with an 23 | updated JSON Schema. The extension can then create new entities based on 24 | the new RDE Type version. 25 | 26 | The extension can also access and update RDE instances based on other RDE Type versions while keeping their data consistent. See the section [Handling different RDE instance versions](#handling-different-rde-instance-versions) for details. 27 | 28 | ## RDE Version Format 29 | 30 | RDEs use the “Semantic Versioning” standard. Versions must be of the form 31 | MAJOR.MINOR.PATCH, where each section is numeric, for example: 1.0.0, 32 | 1.1.0, 2.1.1. 33 | 34 | The standard Semantic Versioning precedence is used where it is needed 35 | (e.g. see the section [RDE Type Versions and RDE Access Control](#rde-type-versions-and-rde-access-control)). For example, 1.0.0 36 | \< 2.0.0 \< 2.1.0 \< 2.1.1 . 37 | 38 | Note: RDEs do not support additional versioning labels for pre-releases, 39 | e.g. 1.0.0-alpha. 40 | 41 | ## RDE IDs and Classification IDs 42 | 43 | The version ID is part of the RDE Interface and Type IDs, for example: 44 | 45 | - urn:vcloud:interface:vmware:k8s:1.0.0 46 | - urn:vcloud:type:vmware:tkgcluster:1.0.0 47 | 48 | RDE Instances do not have a version in their IDs, as the IDs must stay 49 | the same, even if they are upgraded to a different version of the type. 50 | An example instance ID: 51 | 52 | ```text 53 | urn:vcloud:entity:vmware:tkgcluster:56bacbaa-8ddc-4bef-b7a5-e8f6758aff25 54 | ``` 55 | 56 | The version of the instance can be seen in the type ID in its 57 | representation: 58 | 59 | ```text 60 | GET 61 | https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/urn:vcloud:entity:vmware:tkgcluster:56bacbaa-8ddc-4bef-b7a5-e8f6758aff25 62 | ``` 63 | 64 | ```json 65 | { 66 | "id": "urn:vcloud:entity:vmware:tkgcluster:56bacbaa-8ddc-4bef-b7a5-e8f6758aff25", 67 | "entityType": "urn:vcloud:type:vmware:tkgcluster:1.0.0", 68 | … 69 | } 70 | ``` 71 | 72 | While performing RDE queries, it is possible to use “Classification” RDE 73 | Type or Interface IDs that have an incomplete version. For example, the 74 | following query: 75 | 76 | ```text 77 | GET 78 | https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/types/vmware/tkgcluster/1 79 | ``` 80 | 81 | will return all entities of type 82 | urn:vcloud:type:vmware:tkgcluster:**1.x.x** – any type version with 83 | major version of 1. 84 | 85 | ## Handling different RDE instance versions 86 | 87 | If an extension is upgraded and needs to use enhanced functionality in 88 | its RDE instances, it has to create a new version of the RDE Type with 89 | the updated JSON Schema. 90 | 91 | RDE instances of different versions of the same RDE Type can exist at 92 | the same time. An extension can choose how to manage its access to these 93 | different RDE instance versions. 94 | 95 | It can either upgrade the existing RDE instances to a new version of the 96 | RDE Type, or it can request the entity and specify that the 97 | contents must be converted to a specific version before they are 98 | returned to the client. 99 | 100 | ### Upgrading an RDE instance to another type version 101 | 102 | RDE instances can be upgraded to another type version either 103 | individually or globally. 104 | 105 | #### Individual RDE instance upgrade 106 | 107 | To change the type version of an instance, it can be updated with the 108 | "entityType" property set to the ID of the desired type version. 109 | 110 | ```text 111 | PUT 112 | https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/urn:vcloud:entity:vmware:tkgcluster:56bacbaa-8ddc-4bef-b7a5-e8f6758aff25 113 | ``` 114 | 115 | ```json 116 | { 117 | "entityType": "urn:vcloud:type:vmware:tkgcluster:1.1.0", 118 | … 119 | } 120 | ``` 121 | 122 | A user must have Full Control access to the entity to be able to perform 123 | the operation. 124 | The new type version can be either higher or lower, hence it is possible 125 | to downgrade the entity as well. 126 | 127 | It is desirable to ensure that the updated entity contents match the 128 | schema of the new type version, otherwise the entity resolution may 129 | fail. 130 | 131 | In case the schema of the new type version has required fields and those 132 | fields are missing in the entity contents, then the fields will be 133 | automatically filled in with their default values if such default values 134 | are defined in the new schema. 135 | 136 | #### Mass RDE upgrade 137 | 138 | All instances of particular RDE Type versions can be upgraded or 139 | downgraded to another version using the [RDE Version Migration API](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/cloudapi/1.0.0/entityTypes/typeId/migrateEntities/post/). 140 | 141 | The mass RDE upgrade is a long-running process that updates the selected 142 | RDE instances using the same rules as an individual RDE 143 | instance upgrade. 144 | 145 | #### RDE Conversion to a specific type version upon request 146 | 147 | It is possible to get or query RDE instances converted to a specific RDE 148 | Type version using the “acceptType” query parameter, for example: 149 | 150 | ```text 151 | GET 152 | https://{{vcd_host}}:{{vcd_port}}/cloudapi/1.0.0/entities/urn:vcloud:entity:vmware:tkgcluster:56bacbaa-8ddc-4bef-b7a5-e8f6758aff25?acceptType=urn:vcloud:type:vmware:tkgcluster:1.1.0 153 | ``` 154 | 155 | This operation will not modify the entity. It will only attempt to 156 | convert its representation in the response. 157 | 158 | The functionality allows several extension versions to operate 159 | concurrently, each specifying the version they require. For performance 160 | reasons, however, it is preferable that the extensions use the upgrade 161 | functionality instead and also use backward and forward compatible 162 | schema so that they could operate on entities of any version without the 163 | need of an explicit conversion. 164 | 165 | ## RDE Type Versions and RDE Access Control 166 | 167 | The user access to RDE Instances is controlled by a combination of RDE 168 | Type Rights that are generated for each RDE Type, as well as ACLs. 169 | 170 | The RDE Type Rights can be assigned to the user’s role and represent the 171 | capabilities of the user (e.g. “Editor”). The RDE Type Rights are 172 | version-agnostic – the same RDE Type Rights are used for all versions of 173 | a particular RDE Type. This avoids the need to update the user roles 174 | when new versions of an RDE Type become available. 175 | 176 | The ACLs of RDE Instances specify the access a user has to the 177 | corresponding instance. The RDE Instance ACLs are also version-agnostic 178 | – they are not affected if the RDE Type version of the instance changes. 179 | 180 | In short, RDE Type versions do not affect the typical RDE Access 181 | Control. 182 | 183 | ### Special Case: RDE Type versions with explicitly specified limited access 184 | 185 | Providers may sometimes want to publish new experimental RDE Type 186 | versions only to some organizations or users. This can be achieved by 187 | adjusting the ACLs of a specific RDE Type version via the [Type Access 188 | Controls API](https://developer.broadcom.com/xapis/vmware-cloud-director-openapi/latest/type-access-controls/). 189 | 190 | The RDE Type ACLs control which Users or Organizations can “see” the 191 | Type version and perform operations with its instances based on the 192 | users’ personal RDE Type Rights and instance ACLs. 193 | 194 | By default when the Rights Bundle of an RDE Type is published to an 195 | Organization, the whole Organization is automatically granted ACLs to 196 | all defined versions of the published RDE Type. 197 | 198 | In addition, when a new RDE Type version is defined, it automatically 199 | inherits the ACLs of the previous version of the RDE Type. In that way 200 | there is no need to explicitly assign ACLs to new RDE Type versions. 201 | 202 | Providers, however can modify the RDE Type ACLs using the aforementioned 203 | API endpoint to allow only some Users or Organizations to “see” specific 204 | versions. 205 | 206 | ### Special Case: Defining an older RDE Type version 207 | 208 | If an older RDE Type version is defined, it will not automatically 209 | inherit the ACLs of the existing RDE Type versions and thus may be 210 | invisible to the tenants. To resolve this, the provider must either 211 | explicitly update the RDE Type version ACLs using the API, or simply 212 | unpublish and then republish the RDE Type to the relevant Organizations. 213 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/extensibility-platform.md: -------------------------------------------------------------------------------- 1 | # VMware Cloud Director Extensibility Platform Overview 2 | 3 | VMware Cloud Director Extensibility Platform provides a set of capabilities that enable developers and Cloud Service Providers (CSPs) to build and offer additional cloud services in their portfolio. As stated in the [previous section](../extension-sdk/extension-sdk.md), Solution Add-ons provide the necessary capabilities for simple lifecycle management and they are built on top of the multiple capabilities of the Extensibility Platform. For more details on each capability, refer to the sections below and review the technical information in the detailed guides. 4 | 5 | ## UI Extensibility 6 | 7 | Through the UI Extensibility framework, developers can create custom plugins that integrate seamlessly into the VMware Cloud Director user interface. The UI plugins serve the role of the frontend for VMware Cloud Director Extensions, allowing Cloud Providers and Tenants to manage and consume value added services. UI Plugins are developed using the Angular and Clarity frameworks, but in some cases, other technology stacks might be required. This is why UI Plugins also support iFrames. 8 | 9 | More details for all UI Extensibility capabilities and tooling can be found [here](ui-plugins.md). 10 | 11 | ## Message broker 12 | 13 | The backbone of many of the extensibility framework technologies is a [message bus](message-broker.md). Extension backends communicate with-, or monitor various system processes and events. Currently, there are two message busses - one for the AMQP protocol, which is backed by a provider's own RabbitMQ server, and one for the MQTT protocol which is embedded in VMware Cloud Director. AMQP is being slowly phased out in favour of MQTT and will eventually be completely removed. 14 | 15 | ## Notifications and Events 16 | 17 | Notifications and Events are mechanisms that provide real-time information about activities, changes, and status updates within the VCD environment. These features allow VMware Cloud Director Extensions to monitor and respond to events that occur in the cloud infrastructure and enable various monitoring, alerting, automation and external system integration usecases. 18 | 19 | VMware Cloud Director Notifications and Events are consumed using the MQTT protocol, and more details will be provided in a detailed guide soon. 20 | 21 | ## API Extensibility 22 | 23 | VMware Cloud Director provides an [API Extensibility](api-extensibility.md) feature that allows defining custom API endpoints that integrate seamlessly into VMware Cloud Director REST API layer. Extensions can leverage this capability to enable new services that can be consumed by either UI Plugins or API users and scripts. The additional APIs require a backend to process the request information and respond in the proper fashion. Currently there are two flavours for this: 24 | 25 | - MQTT - API extension services can integrate with VMware Cloud Director backend via websocket connection, using MQTT protocol. Such services use 2 distinct MQTT topics for receiving incoming http requests and providing the response. These responses are then propagated back to the API caller in their original http form. 26 | - API Transparent Proxy - This approach allows VMware Cloud Director to act as a transparent proxy to any REST API that the VCD appliance has network connectivity to but the API caller does not. This approach, combined with the iFrame support of UI Plugins, enables quick and low-effort integrations with other Cloud Services and Systems. 27 | 28 | ## Runtime Defined Entities and Behaviors 29 | 30 | [Runtime Defined Entities](defined-entities/defined-entities-overview.md) (RDE) allow Extensions to create custom objects through the VMware Cloud Director API and persist them into the VMware Cloud Director's database. The RDEs enable use cases like managing the desired state of external resources and storing the state of an extension. In addition to extending the database, the RDE framework intoduces different types of behaviors such as Webhook, MQTT, vRO, AWS Lamba and Built-in FaaS that can be used to interact with the data stored in the Runtime Defined Entities. 31 | 32 | RDEs additionally provide advanced RBAC and Access Control for each type of object and their instances. These capabilities, combined with behaviors, are a great alternative to a traditional appliance backend that Extensions usually implement. More details for Runtime Defined Entities' management and all Behavior types will be provided in a detailed guide soon. 33 | 34 | ## Blocking Tasks(Callouts) 35 | 36 | Most built-in long-running operations in VMware Cloud Director can be configured to block and wait to be resumed/cancelled/aborted via an external entity - either manually by a user, or by an application. 37 | 38 | ## Object Extensibility 39 | 40 | VMware Cloud Director provides an [Object Extensibility](object-extensibility.md) feature that allows Extensions to participate in, influence, or override the logic that VMware Cloud Director applies 41 | to core workflows like vApp/VM instantiation and placement. While [Blocking Tasks](https://docs.vmware.com/en/VMware-Cloud-Director/10.5/VMware-Cloud-Director-Service-Provider-Admin-Guide/GUID-B61D23C2-CCCF-4B33-8692-642C80A24193.html) 42 | allow Providers to control the progress of certain tasks in the system, Object Extensions can directly influence the 43 | outcome of certain core platform workflows. Extensions have to leverage MQTT behaviors to interact and plug into the 44 | core workflows currently exposed by VMware Cloud Director. 45 | 46 | More details will be provided in a detailed guide soon. 47 | 48 | ## Object Metadata 49 | 50 | Many core entity types support a notion of a soft schema extension to their main properties. Object metadata gives cloud operators and tenants a flexible way to associate user-defined properties (name=value pairs) with objects. 51 | 52 | There are two separate pools of metadata, which have different management and capabilities: 53 | 54 | 1. Metadata of objects managed by the legacy VMware Cloud Director api 55 | 2. [Metadata](object-metadata-2.md) of objects managed by the VMware Cloud Director `cloudapi` 56 | 57 | Objects which can be managed by both kinds of apis(legacy and cloudapi) will have the two separate pools. Metadata of the legacy api is considered deprecated and will receive only bug-fixes. Metadata of the cloudapi provides new features, with the primary goal being - improved _performance_, including a more powerful _search-by-metadata_ mechanism. 58 | -------------------------------------------------------------------------------- /documentation/extensibility-platform/message-broker.md: -------------------------------------------------------------------------------- 1 | VMware Cloud Director uses a traditional message broker to support many of the extensibility platform capabilities. Starting from version 10.2, a message broker comes pre-bundled with the product. The protocol used for communication is [mqtt3.1.1](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html). 2 | > This page is still under development 3 | # Establishing a connection 4 | * web socket at `{vcd.host}/messaging/mqtt` 5 | 6 | ## Characteristics of the broker capabilities 7 | * embedded brokers in each cell of a cell group are clustered 8 | * brokers "forward" messages between themselves if necessary; for ex. when one client is connected on cell A and publishes to a topic, and there is another client connected on Cell B which is subscribed to the same topic, it will receive the message 9 | * `QoS=0,1,2` are supported, and 10 | * `QoS=1,2` - message loss may occur in case the broker stops 11 | * `QoS=0` - message loss may occur even in case of network hiccups 12 | * `clean start/session=true` 13 | * `clean start=false` is not supported, because the broker does not keep any persisted state, meaning on crash or restart any durable session will be forgotten 14 | * default message expiration is 5 minutes(unless the broker holding the message is stopped before then) 15 | * cannot be used as a generic broker 16 | * messages flow from VMware Cloud Director outward(notifications), or 17 | * request/response on strictly defined topics(extension services) 18 | * authentication 19 | * extension services -> user=extension service triplet, pass=extension token(get from RESTapi `/cloudapi/1.0.0/tokens`, `type=EXTENSION`) 20 | * notifications -> user=user@organisation, pass=session jwt(perform a regular login at RESTapi to obtain jwt) 21 | 22 | ## Message Schema 23 | * meta information describing how the payload is encoded(`payload` is a json/xml encoded string, etc); the `type` sometimes dictates the payload type(`BEHAVIOR_INVOCATION`=>`InvocationArguments`), but other times it is additionally specified by `headers.contentType` 24 | * the reason for this structure is that payloads with varying schemas may float over the same topic; this means that your application may need to cope with messages which it is not interested in(and ignore them, rather than fail) 25 | ```json 26 | { 27 | "type": "EVENT|EXTERNAL_EVENT|TASK|EXTENSION_TASK|API_REQUEST|API_RESPONSE|BEHAVIOR_INVOCATION|BEHAVIOR_RESPONSE", 28 | "headers": {}, 29 | "payload": "" 30 | } 31 | ``` 32 | > **Note:** 33 | > 34 | > This pattern is used throughout the extensibility platform and various features may themselves encode their payloads in terms of meta info+encoded payload in the main `payload`; for ex. `BEHAVIOR_INVOCATION` wraps `HalfDuplexEnvelope` in its `payload`(`InvocationArguments.arguments`) as a json encoded string and the `HalfDuplexEnvelope` itself also encodes an [object extension request/response](object-extensibility.md#implementing-an-object-extension-backend) in its `payload`(where `payloadType` contains the name of the encoded payload schema) 35 | 36 | ## Configuring a client 37 | * handling connection hiccups(subscriptions must be re-established "manually") 38 | * handling password expiration(token, jwt, etc) 39 | * working around known bugs of popular clients(paho) 40 | 41 | ## Best practices 42 | * mission critical applications integrating _notifications_ should have a mechanism to periodically check for missed events and should not solely rely on `QoS`(or `clean start`) 43 | * extension developers must find balance between the frequency of the check and the `QoS` level that is used; the higher the `QoS` the less frequent the check may need to be; for most situations `QoS=1` should be good enough, unless the check is very expensive to make; `QoS=2` may be used as last resort, as that is very taxing on the broker and may lower the overall performance of VMware Cloud Director itself 44 | * `QoS` should be viewed as levels of guarantee that a message transfer between broker and client will succeed, but it does not directly guarantee a message transfer between a `publisher` and a `subscriber` 45 | * applications may need to implement additional logic to cope with duplicated messages for `QoS=1` (at least once) -------------------------------------------------------------------------------- /documentation/extension-sdk/extension-sdk.md: -------------------------------------------------------------------------------- 1 | # VMware Cloud Director Extension SDK 2 | VMware Cloud Director Extension SDK provides utilities empowering the Solution Add-On Development Lifecycle. It contains tools for writing, building, testing and packaging VMware Cloud Director UI plugins, Runtime Defined Entities, users, rights, right-bundles, roles, backend vApps, network services and policies, and their lifecycle operations into self-contained all-in-one bundle distributed under the name VMware Cloud Director Solution Add-On. 3 | 4 | ![Solution Add-On Overview](../images/overview.png) 5 | 6 | ## Why You Need Solution Add-On 7 | Could Director exposes different extensibility mechanisms where each of those has its own lifecycle and management APIs, but in the real world they play together to implement more complex use cases and be used as part of a bigger solution often delivered with number of documents for manual steps. This makes building, distributing and deploying such solution a challenge. Solutions add-ons have been introduced to tackle this problem. 8 | 9 | ## Solution Add-On 10 | Starting with VMware Cloud Director 10.4.1, you can use VMware Cloud Director Solution Add-Ons UI to extend your VMware Cloud Director offering with value-added functionalities. Through the UI, you can manage the resources and life cycle of solutions that are custom-built to extend the functionality of VMware Cloud Director. 11 | 12 | A solution add-on is the representation of a solution that is custom-built for VMware Cloud Director in the VMware Cloud Director extensibility ecosystem. A solution add-on can encapsulate UI and API VMware Cloud Director extensions together with their backend services and lifecycle management. Solution аdd-оns are distributed as .iso files with an embedded installer for 64-bit Linux, Windows and Mac operating systems for local installation or can be directly managed via VMware Cloud Director UI. 13 | 14 | Once a solution add-on is uploaded in VMware Cloud Director the provider can enable it by creating an instance of it. Depending on it vendor the add-on can support single or multiple instances. The multi-instance add-ons tend to create copies of themselves to address multi-tenancy or other type of isolation or distribute themselves across the datacenter resources to achieve better performance or gain certain level of network access. 15 | 16 | ## Solution Landing Zone 17 | Most of the solutions add-ons go beyond the standard extensibility in VMware Cloud Director and bring their own backend that requires specific infrastructure resources. Automating the installation and upgrade flows of such add-on might be very challenging if there is no standard interface between the provider resources and the vendor expectations. Starting with VMware Cloud Director 10.4.1 vendors can utilize the Solution Add-On Landing Zone and implement their add-on lifecycle management run books throughout automation. 18 | 19 | The Solution Add-On Landing Zone is a part of the provider management plane that represents a pool of compute, storage and networking resources dedicated to hosting, managing, and running solution add-ons on behalf of the cloud provider. It is implemented as VMware Cloud Director Runtime Defined Entity referencing existing VMware Cloud Director resources. It further defines a standard interface for vendors that could be consumed via VMware Cloud Director Extension SDK and flexible resource allocation scheme for providers to choose from linking resources already in use or dedicate new infrastructure pool, or make a combination of both. 20 | 21 | ## Key Roles in the Solution Add-On Ecosystem 22 | ### Vendor 23 | Vendors are the creators of solution add-ons who use the Solution Add-On SDK to create services that complement VMware Cloud Director, such as Container Service Extension, third-party software vendors, Kubernetes service, and others. 24 | 25 | ### Provider 26 | Providers are the operators of solution add-ons in the VMware Cloud Director on-premises or VMware Cloud Director service environment. 27 | 28 | ### Tenant 29 | Tenants are the consumers of the business outcomes brought about by a solution add-on, for example, self-service provisioning of Kubernetes clusters, Kubernetes operators, databases, UI extensions with back-office properties, and more. 30 | 31 | # What is Next? 32 | * [Setting up the Development Environment](setup.md) 33 | * [Building a Simple Solution Add-On](playground.md) 34 | * [Understanding the Solution Add-On Lifecycle](lifecycle.md) 35 | * [Understanding the Solution Add-On Behavior](behavior.md) 36 | * [Exploring Solution Add-On Elements](elements.md) 37 | * [Troubleshooting Solution Add-On Operations](troubleshoot.md) 38 | * [Read the Service Provider Admin Guide for Solution Add-Ons](https://docs.vmware.com/en/VMware-Cloud-Director/10.4/VMware-Cloud-Director-Service-Provider-Admin-Portal-Guide/GUID-4F12C8F7-7CD3-44E8-9711-A5F43F8DCEB5.html) -------------------------------------------------------------------------------- /documentation/extension-sdk/setup.md: -------------------------------------------------------------------------------- 1 | 2 | # Setting up the Development Environment 3 | The VMware Cloud Director Extension SDK is distributed as binary under the name `vcd-ext-shell`. This shell introduces set of commands to manage the various stages of a solution add-on development and its future upgrades. 4 | 5 | The latest version of `vcd-ext-shell` for Linux, Windows and Mac operating systems is available on our website [[Download]](https://packages.vmware.com/vcd-ext/). 6 | 7 | ## Prerequisites 8 | - Docker version 18.09 or higher. [[Setup]](https://docs.docker.com/get-docker/) 9 | - Docker buildx plugin. [[Setup]](https://docs.docker.com/build/install-buildx/) 10 | - Internet access required to access Docker images. 11 | - Your favorite IDE. 12 | 13 | ## Install 14 | The SDK can be installed by appending its path to the operating system `PATH` variable. 15 | ```shell 16 | echo 'export PATH=$PATH:' >> ~/.bash_profile 17 | ``` 18 | 19 | ## Setup 20 | Executing the `vcd-ext-shell` for the first time will print its basic use message. 21 | 22 | ```shell 23 | $ vcd-ext-shell 24 | ``` 25 | 26 | ```shell 27 | Welcome to VMware Cloud Director Solution Add-On CLI Version '-'. 28 | 29 | CEIP is 'on'. If needed it can be turned off via 'setup ceip off' command. 30 | 31 | Use or for help, navigation and autocompletion, try now! 32 | 33 | It looks like '' does not contain a valid solution add-on. 34 | You can use 'solution open' command to open an existing solution add-on project or 'solution new' command to create a new project. 35 | ``` 36 | 37 | The release details can be reviewed via `about` command. 38 | ```shell 39 | * no solution |> about 40 | release-notes Print release notes 41 | license Print license 42 | version Print version 43 | ``` 44 | 45 | The `vcd-ext-shell` supports automatic upgrade via `upgrade` command. The `vcd-ext-shell` will check for upgrades on a regular basis, once a new version is available it is going to prompt the user and suggest the upgrade. This the best way to keep up to date with the latest improvements and fixes. 46 | ```shell 47 | * no solution |> upgrade 48 | vcd-ext-shell Upgrade to latest version 49 | ``` 50 | 51 | Also, the first execution of the `vcd-ext-shell` will create initial configuration under `~/.vcd-ext-shell` with the following contents. 52 | ```shell 53 | privateKeyPemPath: "" 54 | certificatePemPath: "" 55 | encryptionKey: "" 56 | bootstrapRepoUrl: "" 57 | latestVersion: 58 | cloudDirectors: 59 | activeHost: "" 60 | hosts: [] 61 | extensionLibraries: 62 | activeRepository: "" 63 | repositories: [] 64 | ceip: true 65 | ``` 66 | 67 | It is recommended to modify the contents of `~/.vcd-ext-shell` via the `setup` command. 68 | 69 | ```shell 70 | * no solution |> setup 71 | 72 | vendor-certificate Set vendor or generate self-signed certificate and private key (used to create ISO signatures) 73 | encryption-key A key used to encrypt runtime deployment configuration in development mode 74 | extension-library Set extension library URI (filesystem path or URL) 75 | cloud-directors Manage VMware Cloud Director connections 76 | ceip Set participation in VMware's Customer Experience Improvement Program ("CEIP") 77 | print Print current configuration 78 | ``` 79 | 80 | ### Vendor 81 | Solution add-on vendor represents the add-on developer identity. The `vcd-ext-shell` is going to use the vendor certificate to sign all files part of the resulted ISO. This way the provider receives a guarantee for the origin of the add-on. 82 | 83 | The vendor can set up their identity by providing the paths to their private key and certificate PEM encoded files. Alternatively a developer can utilize the built-in self-signed pair generation functionality. 84 | 85 | **NOTE**: Self-signed certificates should be used only for pure development activities when the add-on is ready to be released it is recommended to use publicly trusted certificate. 86 | 87 | Use existing or generate new private key and certificate. 88 | ```shell 89 | *no solution* |>setup vendor-certificate 90 | set Set path to vendor certificate and private key 91 | generate Generate new self-signed certificate 92 | ``` 93 | 94 | **NOTE**: Prior an add-on installation the provider has to trust the vendor certificate otherwise the installation request will be denied. 95 | 96 | ### Encryption 97 | A solution add-on might use credentials or other type of secrets provided as user input. This data will be encrypted with a key defined explicitly by the provider or implicitly generated by VMware Cloud Director. 98 | 99 | In development mode (`vcd-ext-shell` ) all add-on operations are executed explicitly and require manual configuration of an encryption-key. 100 | 101 | ```shell 102 | * no solution |> setup encryption-key --key 103 | ``` 104 | 105 | ### Environment 106 | A developer or a continuous integration system can use multiple VMware Cloud Director instances to implement and test the various aspects of an add-on. 107 | 108 | Add a new VMware Cloud Director environment. 109 | ```shell 110 | * no solution |> setup cloud-director add 111 | --host Public URL or IP (required) 112 | --port Port (default: 443) 113 | --username Username (required) 114 | --password Password (exclusive with --password-file) 115 | --password-file Password file (exclusive with --password) 116 | ``` 117 | 118 | List the available VMware Cloud Director environments 119 | ```shell 120 | * no solution |> setup cloud-directors print 121 | VMware Cloud Director Hosts: 122 | Active Host: cloud-a.director.local 123 | Hosts: 124 | - cloud-a.director.local 125 | - cloud-b.director.local 126 | ``` 127 | 128 | Change the default VMware Cloud Director environment. 129 | ```shell 130 | * no solution |> setup cloud-directors activate --host cloud-b.director.local 131 | VMware Cloud Director Hosts: 132 | Active Host: cloud-b.director.local 133 | Hosts: 134 | - cloud-a.director.local 135 | - cloud-b.director.local 136 | ``` 137 | 138 | ### CEIP 139 | The `vcd-ext-shell` command line interface participates in VMware's Customer Experience Improvement Program (CEIP). 140 | The CEIP provides VMware with information that enables VMware to improve its products and services, to fix problems, and to advise you on how best to use our products. 141 | 142 | Details regarding the data collected through CEIP and the purposes for which it is used by VMware are set forth at the Trust & Assurance Center at http://www.vmware.com/trustvmware/ceip.html. 143 | 144 | CEIP can be enabled or disabled via `vcd-ext-shell`. 145 | ```shell 146 | * no solution |> setup ceip 147 | on Turn on CEIP 148 | off Turn off CEIP 149 | print Display current setting 150 | ``` 151 | 152 | ### Extension Library 153 | The `vcd-ext-shell` can bootstrap a new project or add an element to an existing project using a prescribed library. 154 | In order to do so one or more repositories can be configured 155 | Default preconfigured public repository is https://github.com/vmware/cloud-director-extension-standard-library 156 | 157 | ```shell 158 | * no solution |> setup extension-library 159 | add Configure new extension library 160 | remove Remove existing extension library from configuration 161 | activate Set extension library as active target 162 | default Set default extension library as active target 163 | print Print available extension libraries 164 | ``` 165 | 166 | ### Environment variables 167 | For ease of CI/CD management `vcd-ext-shell` can replace specific place-holders inside manifest file. 168 | Placeholder format is as follows `{{ env 'VAR_NAME' 'OPTIONAL_DEFAULT' }}`. 169 | Replacement values will be taken from (in order of priority): OS environment variables, configured properties, default values 170 | Setting the configuration variables can be done with command `setup environment set --name VAR1 --value Value1`. 171 | When `vcd-ext-shell` is in a context of a project `set` command will present place-holders form manifest flags. 172 | ```shell 173 | *no solution* |>setup environment 174 | set Set 175 | clean Clean configuration properties 176 | print Print variables 177 | ``` 178 | 179 | # What is Next? 180 | * [Building a Simple Solution Add-On](playground.md) 181 | * [Understanding the Solution Add-On Lifecycle](lifecycle.md) 182 | * [Understanding the Solution Add-On Behavior](behavior.md) 183 | * [Exploring Solution Add-On Elements](elements.md) -------------------------------------------------------------------------------- /documentation/images/api_extensibility_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/api_extensibility_1.png -------------------------------------------------------------------------------- /documentation/images/api_extensibility_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/api_extensibility_2.png -------------------------------------------------------------------------------- /documentation/images/api_extensibility_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/api_extensibility_3.png -------------------------------------------------------------------------------- /documentation/images/clone.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/clone.jpeg -------------------------------------------------------------------------------- /documentation/images/compose-recompose.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/compose-recompose.jpeg -------------------------------------------------------------------------------- /documentation/images/extensibility-overview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/extensibility-overview.gif -------------------------------------------------------------------------------- /documentation/images/instantiateVappTemplate.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/instantiateVappTemplate.jpeg -------------------------------------------------------------------------------- /documentation/images/object-extensions.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/object-extensions.jpg -------------------------------------------------------------------------------- /documentation/images/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/overview.png -------------------------------------------------------------------------------- /documentation/images/rde-multi-stage-delete-async-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-multi-stage-delete-async-1.png -------------------------------------------------------------------------------- /documentation/images/rde-multi-stage-delete-async-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-multi-stage-delete-async-2.png -------------------------------------------------------------------------------- /documentation/images/rde-multi-stage-delete-sync-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-multi-stage-delete-sync-1.png -------------------------------------------------------------------------------- /documentation/images/rde-multi-stage-delete-sync-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-multi-stage-delete-sync-2.png -------------------------------------------------------------------------------- /documentation/images/rde-post-create-hook-entity-states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-post-create-hook-entity-states.png -------------------------------------------------------------------------------- /documentation/images/rde-sync-deletion-entity-states-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-sync-deletion-entity-states-1.png -------------------------------------------------------------------------------- /documentation/images/rde-sync-deletion-entity-states-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde-sync-deletion-entity-states-2.png -------------------------------------------------------------------------------- /documentation/images/rde_concepts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde_concepts.png -------------------------------------------------------------------------------- /documentation/images/rde_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/rde_lifecycle.png -------------------------------------------------------------------------------- /documentation/images/reconfigureVm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/reconfigureVm.jpeg -------------------------------------------------------------------------------- /documentation/images/relocate.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/relocate.jpeg -------------------------------------------------------------------------------- /documentation/images/sample-ext-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-action.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-create-entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-create-entity.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-create-ip-binding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-create-ip-binding.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-main-navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-main-navigation.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-network-side-navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-network-side-navigation.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-networking-navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-networking-navigation.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-side-navigation-entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-side-navigation-entity.png -------------------------------------------------------------------------------- /documentation/images/sample-ext-top-level-navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/sample-ext-top-level-navigation.png -------------------------------------------------------------------------------- /documentation/images/under-the-hood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/under-the-hood.png -------------------------------------------------------------------------------- /documentation/images/webHook-behavior-slack-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/images/webHook-behavior-slack-example.png -------------------------------------------------------------------------------- /documentation/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## Overview 4 | Welcome to the VMware Cloud Director Extensibility Development Guide, your comprehensive resource for developing and extending capabilities within VMware Cloud Director. This guide is structured to provide developers and Cloud Service Providers (CSPs) with the necessary information to enhance, customize, and expand the functionality of the platform. 5 | 6 | ![VMware Cloud Director Extensibility Overview](images/extensibility-overview.gif) 7 | 8 | ## Purpose 9 | Developers and CSPs to gain insights into the capabilities offered by the VMware Cloud Director Extensibility Platform and the VMware Cloud Director Extension SDK. 10 | 11 | ## Audience 12 | The intended audience for this guide includes developers, system administrators, and Cloud Service Providers looking to extend and tailor the VMware Cloud Director environment to meet specific business requirements. 13 | 14 | ## Structure of the Guide 15 | The VMware Cloud Director Extensibility Development Guide is organized into two main sections: 16 | 17 | ### VMware Cloud Director Extension SDK 18 | Extensions are bundled within what we refer to as Solution Add-Ons, which are installed in VMware Cloud Director and follow a carefully defined and controlled lifecycle. These add-ons are made up of different parts that use the features of the Extensibility Platform. In this section, you will discover how to develop, construct, and deploy them. 19 | 20 | [Continue to VMware Cloud Director Extension SDK](extension-sdk/extension-sdk.md) 21 | 22 | ### VMware Cloud Director Extensibility Platform 23 | The Extensibility Platform introduces the capabilities that enable developers and CSPs to build and offer additional cloud services. It also highlights the relationship between Solution Add-ons and the broader capabilities of the Extensibility Platform. In this section, you will access detailed technical guides that offer in-depth information on each capability. 24 | 25 | [Continue to VMware Cloud Director Extensibility Platform](extensibility-platform/extensibility-platform.md) -------------------------------------------------------------------------------- /documentation/schemas/obj-ext-channel-msg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/schemas/obj-ext-channel-msg.zip -------------------------------------------------------------------------------- /documentation/schemas/obj-ext-channel-msg/envelopes.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | description: Contains the schemas of the messages that are to be exchanged between VMware Cloud Director and channel extensions 4 | title: Object Extensibility Messages 5 | version: "1.0" 6 | paths: 7 | definitions: 8 | MessageEnvelope: 9 | type: object 10 | description: Self describing message containing metadata describing the invocation and payload. 11 | properties: 12 | clientRequestId: 13 | type: string 14 | description: Contains the value of the X-VMWARE-VCLOUD-CLIENT-REQUEST-ID header set on the original workflow invocation request. If provided, allows to correlate the workflow request to the message(s) sent. 15 | namespace: 16 | type: string 17 | description: Object extension namespace. This is extracted out of the channel urn. 18 | envelopeType: 19 | type: string 20 | payloadType: 21 | type: string 22 | description: An alias of the type of the payload. Possible values are the names of the top level types in the object extensibility schema xsd. 23 | phase: 24 | type: string 25 | description: Extension point of the invocation 26 | payload: 27 | type: string 28 | description: JSON encoded payload. This must be (de)serialized according to the payloadType on request/response. 29 | discriminator: envelopeType 30 | required: 31 | - payloadType 32 | - phase 33 | - envelopeType 34 | - payload 35 | HalfDuplexEnvelope: 36 | description: Sent in a normal request/response exchange 37 | allOf: 38 | - $ref: '#/definitions/MessageEnvelope' 39 | - type: object 40 | properties: 41 | correlationId: 42 | type: string 43 | description: Message id 44 | expectedResponsePayloadType: 45 | type: string 46 | description: The expected type of the returned payload 47 | ErrorMessageEnvelope: 48 | description: Sent only by VMware Cloud Director when the last exchange failed. The payload is that of the original message which failed. 49 | allOf: 50 | - $ref: '#/definitions/MessageEnvelope' 51 | - type: object 52 | properties: 53 | correlationIdRef: 54 | type: string 55 | description: The id of the message which failed. 56 | required: 57 | - correlationIdRef -------------------------------------------------------------------------------- /documentation/schemas/obj-ext-phase-payloads.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware/cloud-director-extension-standard-library/22126afc76cb4fac0b203474d4ec0c9b093ab9c4/documentation/schemas/obj-ext-phase-payloads.zip -------------------------------------------------------------------------------- /documentation/schemas/obj-ext-phase-payloads/master.xjb: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /documentation/schemas/obj-ext-phase-payloads/master.xsd: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /documentation/toc.md: -------------------------------------------------------------------------------- 1 | # VMware Cloud Director Extension SDK 2 | 3 | * [Introduction](introduction.md) 4 | * [Extension SDK](extension-sdk/extension-sdk.md) 5 | * [Setting up the Development Environment](extension-sdk/setup.md) 6 | * [Building a Simple Solution Add-On](extension-sdk/playground.md) 7 | * [Understanding the Solution Add-On Lifecycle](extension-sdk/lifecycle.md) 8 | * [Understanding the Solution Add-On Behavior](extension-sdk/behavior.md) 9 | * [Exploring Solution Add-On Elements](extension-sdk/elements.md) 10 | * [Troubleshooting Solution Add-On Operations](extension-sdk/troubleshoot.md) 11 | * [Extensibility Platform](extensibility-platform/extensibility-platform.md) 12 | * [UI Plugins Development](extensibility-platform/ui-plugins.md) 13 | * [Message Broker](extensibility-platform/message-broker.md) 14 | * [API Extensibility](extensibility-platform/api-extensibility.md) 15 | * [Object Extensibility](extensibility-platform/object-extensibility.md) 16 | * [Object Metadata 2.0](extensibility-platform/object-metadata-2.md) 17 | * [Runtime Defined Entities](extensibility-platform/defined-entities/defined-entities-overview.md) 18 | * [RDE Interfaces](extensibility-platform/defined-entities/defined-interfaces.md) 19 | * [RDE Types](extensibility-platform/defined-entities/defined-entity-types.md) 20 | * [RDE Lifecycle](extensibility-platform/defined-entities/defined-entities-lifecycle.md) 21 | * [RDE Operations](extensibility-platform/defined-entities/defined-entity-operations.md) 22 | * [RDE Queries](extensibility-platform/defined-entities/rde-queries.md) 23 | * [RDE Access Control](extensibility-platform/defined-entities/rde-access-control.md) 24 | * [RDE Behaviors](extensibility-platform/defined-entities/behaviors-general-concepts.md) 25 | * [Webhook behaviors](extensibility-platform/defined-entities/webhook-behaviors.md) 26 | * [MQTT behaviors](extensibility-platform/defined-entities/mqtt-behaviors.md) 27 | * [VRO behaviors](extensibility-platform/defined-entities/vro-behaviors.md) 28 | * [AWS Lambda behaviors](extensibility-platform/defined-entities/aws-lambda-behaviors.md) 29 | * [No-op behaviors](extensibility-platform/defined-entities/no-op-behaviors.md) 30 | * [RDE Hooks](extensibility-platform/defined-entities/rde-hooks.md) 31 | * [RDE Versioning](extensibility-platform/defined-entities/rde-versions.md) 32 | --------------------------------------------------------------------------------