├── .envrc.sample ├── .gitignore ├── Dockerfile ├── README.md ├── requirements.txt ├── sample-config-yaml ├── harbor.yaml ├── namespace.yaml ├── wcp-cluster-avi.yaml ├── wcp-cluster-nsxt.yaml ├── wcp-cluster-vds.yaml └── wcpcontentlib.yaml └── wcpctl.py /.envrc.sample: -------------------------------------------------------------------------------- 1 | export WCP_USERNAME=administrator@vsphere.local 2 | export WCP_PASSWORD=Change.Me.2020 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .envrc 2 | .idea 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | WORKDIR /app 4 | COPY requirements.txt /app/requirements.txt 5 | COPY wcpctl.py /app/wcpctl.py 6 | RUN pip install -r requirements.txt 7 | 8 | ENTRYPOINT [ "./wcpctl.py" ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WCPCTL 2 | ## A `kubectl` style command line tool to interact with vSphere 7 with Tanzu Supervisor Cluster. 3 | ## Now with support for vSphere 7.0 U1. 4 | ### This can be used to spin up NSX-T or vSphere NW based Supervisor Cluster. 5 | 6 | --- 7 | 8 | *vSphere 7 with Kubernetes* brings some amazing features that enable you to run K8s nativly on your compute clusters. While the product is amazing, currently most of the interaction to the platform is limited to interaction with the vCenter UI. While the user interactions are minimal, the thought process for this project was to perform those interactions thru a CLI tool. If its a CLI tool, why not make it `kubectl` style, with simple, user friendly `yaml` files to provide the configurations and JSON style outputs?? 9 | 10 | Some guidance inspired from the awesome work of *vthinkbeyondvm.com* 11 | 12 | --- 13 | 14 | * Language - Python (tested on v3.7, MacOS, Linux and Windows Server 2016) 15 | * Required modules - requests, json, time, yaml, uuid, argparse, getpass 16 | * Most are generally available. May require install of yaml using the following - `pip3 install -U PyYAML` . 17 | * Windows additionally may require requests. This can be installed by `pip3 install requests`. 18 | 19 | --- 20 | 21 | ### Currently the following actions are supported - 22 | * Creation of the Supervisor Cluster 23 | * Creation of Namespaces 24 | * Creation of Harbor registry 25 | * Creation of Subscribed Content Library 26 | * Reconfiguration of Namespaces (Resource config is a WIP) 27 | * Deletion of Namespaces 28 | * Deletion of Harbor registry 29 | * Deletion of Supervisor cluster 30 | * Deletion of Subscribed Content Library 31 | * Describe Namespace(s) (JSON output) 32 | * Describe WCP Registry (JSON output) 33 | * Describe WCP Cluster (JSON output) 34 | * Describe Subscribed Content Library 35 | 36 | --- 37 | 38 | ### Setup 39 | * Make sure Python3 is installed and working. 40 | * Make sure the modules listed above are installed. May require pip3 to install. 41 | * Make sure you have admin access to the vCenter server. 42 | * Make sure the workstation where the `wcpctl` cli will run on has access to the VCenter server. 43 | * Clone this repo. 44 | * Execute `chmod +x wcpctl.py` if its not already set to execute. 45 | * Modify the YAML config files provided in the `sample-config-yaml` folder. 46 | * If possible, move the `wcpctl.py` file a folder in $PATH. E.g. `sudo cp wcpctl.py /usr/local/bin` 47 | * Execute the `wcpctl` code. 48 | 49 | ``` 50 | $ wcpctl.py -h 51 | ============================================================================= 52 | 53 | usage: wcpctl.py [-h] [--version] {create,apply,delete,describe} ... 54 | 55 | wcpctl controls for managing Supervisor Clusters in vSphere 7 with K8s. Uses 56 | YAML configuration files to setup and manage the Supervisor Cluster. Find 57 | additional information at: https://github.io/papivot/wcpctl 58 | 59 | positional arguments: 60 | {create,apply,delete,describe} 61 | Commands 62 | create Create WCP object(s) 63 | apply Apply configuration changes to WCP object(s) 64 | delete Delete WCP object(s) 65 | describe Describe a WCP object(s) 66 | 67 | optional arguments: 68 | -h, --help show this help message and exit 69 | --version show program's version number and exit 70 | 71 | ``` 72 | ### Sample Examples 73 | 74 | #### To enable/create a Supervisor Cluster 75 | * modify the `wcp-cluster.yaml` sample provided in the `sample-config-yaml` folder. Make sure that the following are enabled/configured (as per official docs) - 76 | - Relevent Content Library 77 | - NSX configuration 78 | 79 | ``` 80 | wcpctl.py create some-wcp-cluster-config.yaml -u administrator@vsphere.local 81 | ``` 82 | 83 | #### To disable the Supervisor Cluster, 84 | ``` 85 | wcpctl.py delete some-wcp-cluster-config.yaml 86 | ``` 87 | 88 | 89 | #### To modify Namespace(s) 90 | ``` 91 | wcpctl.py apply some-namespaceconfig.yaml 92 | ``` 93 | 94 | #### To create a Registry 95 | ``` 96 | wcpctl.py create some-regconfig.yaml 97 | ``` 98 | 99 | #### To describe Registry 100 | ``` 101 | wcpctl.py describe some-nsconfigfile.yaml 102 | ``` 103 | 104 | #### To create a WCP content library 105 | ``` 106 | wcpctl.py create some-contentlibconfigfile.yaml 107 | ``` 108 | 109 | 110 | ### Feedback 111 | 112 | Would love feedback from users. 113 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argparse 2 | requests 3 | pysocks 4 | PyYAML 5 | jsonpatch==1.16 6 | jsonpointer==1.10 7 | jsonschema>=3.0.1 -------------------------------------------------------------------------------- /sample-config-yaml/harbor.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: wcpRegistry 3 | metadata: 4 | vcenter: pacific-vcsa.haas-270.pez.pivotal.io # Modify this 5 | datacenter: Pacific-Datacenter # Modify this 6 | cluster: Workload-Cluster # Modify this 7 | spec: 8 | garbage_collection: 9 | minute: 0 10 | hour: 2 11 | type: WEEKLY 12 | day_of_week: SUNDAY 13 | storage: 14 | - policy: pacific-gold-storage-policy # Modify this 15 | limit: 204800 # Modify this 16 | -------------------------------------------------------------------------------- /sample-config-yaml/namespace.yaml: -------------------------------------------------------------------------------- 1 | kind: wcpNamespace 2 | metadata: 3 | vcenter: pacific-vcsa.haas-270.pez.pivotal.io # Modify this 4 | datacenter: Pacific-Datacenter # Modify this 5 | cluster: Workload-Cluster # Modify this 6 | spec: 7 | namespace: demonamespace1 # Modify this 8 | Description: 'Demo NS 1' 9 | access_list: 10 | - role: EDIT 11 | subject_type: USER 12 | subject: Administrator 13 | domain: vsphere.local 14 | # To add additional users, create the users in VCenter and uncomment the section below 15 | # - role: VIEW 16 | # subject_type: USER 17 | # subject: user1 18 | # domain: vsphere.local 19 | storage_specs: 20 | - limit: 200 21 | policy: pacific-gold-storage-policy # Modify this 22 | resource_spec: 23 | # Resources not implemented yet 24 | # com.vmware.vcenter.namespaces.resource_quota_options_v1_update: 25 | # cpu_limit: 200 26 | # memory_limit: 200 27 | # storage_request_limit: 200 28 | --- 29 | kind: wcpNamespace 30 | metadata: 31 | vcenter: pacific-vcsa.haas-270.pez.pivotal.io # Modify this 32 | datacenter: Pacific-Datacenter # Modify this 33 | cluster: Workload-Cluster # Modify this 34 | spec: 35 | namespace: demonamespace2 # Modify this 36 | Description: 'Demo NS 2' 37 | access_list: 38 | - role: EDIT 39 | subject_type: USER 40 | subject: Administrator 41 | domain: vsphere.local 42 | # To add additional users, create the users in VCenter and uncomment the section below 43 | # - role: VIEW 44 | # subject_type: USER 45 | # subject: user1 46 | # domain: vsphere.local 47 | storage_specs: 48 | - limit: 200 49 | policy: pacific-gold-storage-policy # Modify this 50 | resource_spec: 51 | # Resources ot implemented yet 52 | # com.vmware.vcenter.namespaces.resource_quota_options_v1_update: 53 | # cpu_limit: 200 54 | # memory_limit: 200 55 | # storage_request_limit: 200 56 | -------------------------------------------------------------------------------- /sample-config-yaml/wcp-cluster-avi.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: wcpCluster 3 | metadata: 4 | vcenter: 192.168.100.50 5 | datacenter: Pacific-Datacenter 6 | cluster: Supervisor-Cluster 7 | spec: 8 | service_cidr: 9 | address: 10.96.0.0 10 | prefix: 24 11 | worker_DNS: 12 | - 192.168.100.1 # Change this 13 | master_DNS: 14 | - 192.168.100.1 # Change this 15 | master_DNS_search_domains: 16 | - env1.lab.local # Change this 17 | master_NTP_servers: 18 | - 192.168.100.2 # Change this 19 | Master_DNS_names: 20 | - wcp-avi.env1.lab.local # Change this 21 | workload_ntp_servers: # New addition 22 | - 192.168.100.2 23 | master_management_network: 24 | address_range: 25 | starting_address: 192.168.100.60 # Change this 26 | subnet_mask: 255.255.254.0 # Change this 27 | gateway: 192.168.100.1 # Change this 28 | address_count: 5 29 | mode: STATICRANGE 30 | network: DVPG-Management-network # Change this 31 | ephemeral_storage_policy: tanzu # Change this 32 | image_storage: 33 | storage_policy: tanzu # Change this 34 | master_storage_policy: tanzu # Change this 35 | network_provider: VSPHERE_NETWORK 36 | size_hint: TINY 37 | login_banner: 38 | default_kubernetes_service_content_library: Kubernetes # Change this 39 | # Networking details follow - 40 | load_balancer_config_spec: 41 | address_ranges: [] 42 | avi_config_create_spec: 43 | certificate_authority_chain: | 44 | -----BEGIN CERTIFICATE----- 45 | MIIF7TCCA9WgAwIBAgIUDfbkpeT/F3JxoSWUG6n6LF98rDgwDQYJKoZIhvcNAQEN 46 | BQAwaTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdBc2hidXJu 47 | MQswCQYDVQQKDAJTRTERMA8GA1UECwwIUGVyc29uYWwxGzAZBgNVBAMMEmF2aS5l 48 | bnYxLmxhYi5sb2NhbDAeFw0yMTA1MzAyMTU0MDRaFw0zMTA1MjgyMTU0MDRaMGkx 49 | CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHQXNoYnVybjELMAkG 50 | A1UECgwCU0UxETAPBgNVBAsMCFBlcnNvbmFsMRswGQYDVQQDDBJhdmkuZW52MS5s 51 | YWIubG9jYWwwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGgszSUZGo 52 | ckKLcZ/d8XsKGtWT1L+0KjuOyZN7OK5vOnnXqA9j04QxfOPYeNtW8aCVbNWdelHG 53 | 1N8jVgZVEWIhIxvmfYlyoZdISWpBPElziL4s6OwoshICEeAY5CbJfQwHvZh2YDJP 54 | .... 55 | nktfWlPxPDIVNttNoMH4/EH8Hw4g28aJLu1JXpjf9ALqM8BjIj0w0ZG+WqycbbdZ 56 | CpKx6/CjuWN3k9zxH+Td9k//7W0J9M1jgXQo3NLQk/vPqTyv+/RetwXUygQGmh5p 57 | 1L1kVaqrGafwypDhlR3aTDDtnksxxZhXzjqLkrbYBwSJw65QbXSmx1uCE+7bfIl4 58 | BblspbE5b+MXb4JHZE/YC2/xSz/JTZ8CVrUHSdIqid36Wt3DqrXMFOTvfFBLiHAl 59 | 8e1qpielbfceTo/mYiVVkwjd/7wzW/vgGWinST6XMqgUHXfsTE0ZGVlsq2qe9WBZ 60 | iEwPZ/hFN9YyExsa5wT7PDacj6vj1PULEX83R2NpkkUApn+0HcQkWLCBzdQDDIy/ 61 | 7oAmF4rYjuLxyyZfLWhP6++4E9y+e8LKoCEX7eufBvymL/6IsJmG0A1ssBrCLMxs 62 | AzLIWkmRs3mth54cQGwB1gCxe/C6Ci7KpT8IqaysnJkmn7XTgLKJaTiXghlfdTtP 63 | MfSlv7u1laao2pgQOl6Mjf93qMEulrrBsz65mJO+CRRS 64 | -----END CERTIFICATE----- 65 | server: 66 | host: 192.168.100.58 # Change this 67 | port: 443 68 | username: admin 69 | password: VMware123 70 | id: avi 71 | provider: AVI 72 | workload_networks_spec: 73 | supervisor_primary_workload_network: 74 | network: network-1 75 | network_provider: VSPHERE_NETWORK 76 | vsphere_network: 77 | address_ranges: 78 | - address: 192.168.102.10 # Change this 79 | count: 90 # Change this 80 | gateway: 192.168.102.1 # Change this 81 | subnet_mask: 255.255.254.0 # Change this 82 | portgroup: Workload0-VDS-PG 83 | -------------------------------------------------------------------------------- /sample-config-yaml/wcp-cluster-nsxt.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: wcpCluster 3 | metadata: 4 | vcenter: pacific-vcsa.haas-270.pez.pivotal.io 5 | datacenter: Pacific-Datacenter 6 | cluster: Workload-Cluster 7 | spec: 8 | service_cidr: 9 | address: 10.96.0.0 10 | prefix: 24 11 | worker_DNS: 12 | - 10.192.2.10 13 | master_DNS: 14 | - 10.192.2.10 15 | master_DNS_search_domains: 16 | - haas-xxx.pez.pivotal.io 17 | master_NTP_servers: 18 | - 10.192.2.5 19 | Master_DNS_names: 20 | - wcp.haas-xxx.pez.pivotal.io 21 | master_management_network: 22 | address_range: 23 | gateway: 10.193.222.1 24 | starting_address: 10.193.222.45 25 | subnet_mask: 255.255.255.0 26 | address_count: 5 27 | mode: STATICRANGE 28 | network: "DVPG-Management Network" 29 | ephemeral_storage_policy: pacific-gold-storage-policy 30 | image_storage: 31 | storage_policy: pacific-gold-storage-policy 32 | master_storage_policy: pacific-gold-storage-policy 33 | network_provider: NSXT_CONTAINER_PLUGIN 34 | size_hint: TINY 35 | login_banner: 36 | default_kubernetes_service_content_library: Kubernetes 37 | # Networking details - 38 | ncp_cluster_network_spec: 39 | nsx_edge_cluster: # LEAVE BLANK 40 | cluster_distributed_switch: # LEAVE BLANK 41 | egress_cidrs: 42 | - address: 110.193.222.128 43 | prefix: 26 44 | ingress_cidrs: 45 | - address: 10.193.222.64 46 | prefix: 26 47 | pod_cidrs: 48 | - address: 10.244.0.0 49 | prefix: 21 50 | -------------------------------------------------------------------------------- /sample-config-yaml/wcp-cluster-vds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: wcpCluster 3 | metadata: 4 | vcenter: 192.168.4.50 5 | datacenter: Pacific-Datacenter 6 | cluster: Workload-Cluster 7 | spec: 8 | service_cidr: 9 | address: 10.96.0.0 10 | prefix: 24 11 | worker_DNS: 12 | - 192.168.4.1 # Change this 13 | master_DNS: 14 | - 192.168.4.1 # Change this 15 | master_DNS_search_domains: 16 | - env4.lab.local # Change this 17 | master_NTP_servers: 18 | - 10.192.2.5 # Change this 19 | Master_DNS_names: 20 | - wcp.env4.lab.local # Change this 21 | workload_ntp_servers: # New addition 22 | - 10.192.2.5 23 | master_management_network: 24 | address_range: 25 | starting_address: 192.168.4.70 # Change this 26 | subnet_mask: 255.255.255.0 # Change this 27 | gateway: 192.168.4.1 # Change this 28 | address_count: 5 29 | mode: STATICRANGE 30 | network: "DVPG-Management Network" # Change this 31 | ephemeral_storage_policy: pacific-gold-storage-policy # Change this 32 | image_storage: 33 | storage_policy: pacific-gold-storage-policy # Change this 34 | master_storage_policy: pacific-gold-storage-policy # Change this 35 | network_provider: VSPHERE_NETWORK 36 | size_hint: TINY 37 | login_banner: 38 | default_kubernetes_service_content_library: Kubernetes # Change this 39 | # Networking details follow - 40 | load_balancer_config_spec: 41 | address_ranges: 42 | - address: 192.168.141.1 # Change this 43 | count: 99 # Change this 44 | ha_proxy_config_create_spec: 45 | certificate_authority_chain: | 46 | -----BEGIN CERTIFICATE----- 47 | MIIDnzCCAoegAwIBAgIJAOirI4Oyj91dMA0GCSqGSIb3DQEBBQUAMG0xCzAJBgNV 48 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlQYWxvIEFsdG8x 49 | DzANBgNVBAoMBlZNd2FyZTENMAsGA1UECwwEQ0FQVjEVMBMGA1UEAwwMMTkyLjE2 50 | MA0GA1UECgwGVk13YXJlMQ0wCwYDVQQLDARDQVBWMRUwEwYDVQQDDAwxOTIuMTY4 51 | ... 52 | yRkE88bQsSMnLYb1K66ksS0xnWJgM7gX3u8LHLPX91HKFFyseApsTU37PEMwXMs2 53 | U1wXUHxm/d7EGuRaAbu+huWd3prquEkAoOzXbcvM7QH//2tg/S28InL2lstIrOC7 54 | 9ua9o0kvfa4fMyZtKQsfYe5556msWA+uqyAnZG786HMrf2Vb6udzFqvYgDZZpxNK 55 | OTRjLe1Lv7s4R7R1+hvU3G0OjO+P+1Idxr2QSXiE9WjuCJGzMOOjshkc+5i9tpu5 56 | 8IWX74o2WgoxABV/jPtxU+WcIg== 57 | -----END CERTIFICATE----- 58 | servers: 59 | - host: 192.168.4.57 # Change this 60 | port: 5556 61 | username: admin 62 | password: VMware1VMware1 63 | id: haproxy-1 64 | provider: HA_PROXY 65 | workload_networks_spec: 66 | supervisor_primary_workload_network: 67 | network: network-1 68 | network_provider: VSPHERE_NETWORK 69 | vsphere_network: 70 | address_ranges: 71 | - address: 192.168.140.10 # Change this 72 | count: 90 # Change this 73 | gateway: 192.168.140.1 # Change this 74 | subnet_mask: 255.255.254.0 # Change this 75 | portgroup: Workload0 -------------------------------------------------------------------------------- /sample-config-yaml/wcpcontentlib.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: wcpContentLibrary 3 | metadata: 4 | vcenter: vcsa-01.haas-270.pez.pivotal.io # Modify this 5 | datacenter: Datacenter # Modify this 6 | cluster: Cluster # Modify this 7 | spec: 8 | name: Kubernetes 9 | description: "Default Content Library for WCP" 10 | storage_backings: 11 | - datastore_id: LUN01 # Modify this 12 | type: "DATASTORE" 13 | type: SUBSCRIBED 14 | subscription_info: 15 | authentication_method: NONE 16 | ssl_thumbprint: 56:53:d1:6f:ce:b9:c1:c9:25:d5:7f:41:11:df:7b:00:2c:97:bf:95 17 | automatic_sync_enabled: True 18 | subscription_url: "https://wp-content.vmware.com/v2/latest/lib.json" 19 | on_demand: False 20 | -------------------------------------------------------------------------------- /wcpctl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # “Copyright 2020 VMware, Inc.” 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | # and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 9 | # subject to the following conditions: 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 12 | # LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 13 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 14 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | import requests 18 | import json 19 | import time 20 | import yaml 21 | import sys 22 | import uuid 23 | import argparse 24 | import getpass 25 | import os 26 | 27 | parser = argparse.ArgumentParser(description='wcpctl controls for managing Supervisor Clusters in vSphere 7 with K8s. Uses YAML configuration files to setup and manage the Supervisor Cluster. Find additional information at: https://github.io/papivot/wcpctl') 28 | parser.add_argument('--version', action='version',version='%(prog)s v0.3') 29 | subparsers = parser.add_subparsers(help='Commands',dest='verb') 30 | 31 | # A create command 32 | create_parser = subparsers.add_parser('create', help='Create WCP object(s)') 33 | create_parser.add_argument('filename', action='store', help='YAML file with WCP object configuration. See examples for help') 34 | create_parser.add_argument('-u', action="store", dest="userid", help='VCenter userid. If not provided, will default to administrator@vsphere.local') 35 | 36 | # A apply command 37 | apply_parser = subparsers.add_parser('apply', help='Apply configuration changes to WCP object(s)') 38 | apply_parser.add_argument('filename', action='store', help='YAML file with WCP object configuration. See examples for help') 39 | apply_parser.add_argument('-u', action="store", dest="userid", help='VCenter userid. If not provided, will default to administrator@vsphere.local') 40 | 41 | # A delete command 42 | delete_parser = subparsers.add_parser('delete', help='Delete WCP object(s)') 43 | delete_parser.add_argument('filename', action='store', help='YAML file with WCP object configuration. See examples for help') 44 | delete_parser.add_argument('-u', action="store", dest="userid", help='VCenter userid. If not provided, will default to administrator@vsphere.local') 45 | 46 | # A delete command 47 | describe_parser = subparsers.add_parser('describe', help='Describe a WCP object(s)') 48 | describe_parser.add_argument('filename', action='store', help='YAML file with WCP metadata info. See examples for help') 49 | describe_parser.add_argument('-u', action="store", dest="userid", help='VCenter userid. If not provided, will default to administrator@vsphere.local') 50 | 51 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 52 | 53 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 54 | headers = {'content-type': 'application/json'} 55 | 56 | cmd = parser.parse_args() 57 | verb = cmd.verb 58 | filename = cmd.filename 59 | if cmd.userid: 60 | userid = cmd.userid 61 | else: 62 | if not os.environ.get('WCP_USERNAME'): 63 | userid = "administrator@vsphere.local" 64 | else: 65 | userid = os.environ.get('WCP_USERNAME') 66 | 67 | if not os.environ.get('WCP_PASSWORD'): 68 | password = getpass.getpass(prompt='Password: ') 69 | else: 70 | password = os.environ.get('WCP_PASSWORD') 71 | 72 | if not os.environ.get('SKIP_COMPAT_CHECK'): 73 | skip_compat = False 74 | else: 75 | skip_compat = True 76 | 77 | def generate_random_uuid(): 78 | return str(uuid.uuid4()) 79 | 80 | 81 | def get_storage_id(storage_name,dc): 82 | json_response = s.get('https://'+vcip+'/rest/vcenter/datastore?filter.datacenters='+dc+'&filter.names='+storage_name) 83 | if json_response.ok: 84 | results = json.loads(json_response.text)["value"] 85 | for result in results: 86 | if result["name"] == storage_name: 87 | return result["datastore"] 88 | else: 89 | return 0 90 | return 0 91 | 92 | 93 | def get_storage_policy(sp_name): 94 | json_response = s.get('https://' + vcip + '/rest/vcenter/storage/policies') 95 | if json_response.ok: 96 | results = json.loads(json_response.text)["value"] 97 | for result in results: 98 | if result["name"] == sp_name: 99 | return result["policy"] 100 | else: 101 | return 0 102 | else: 103 | return 0 104 | 105 | 106 | def get_content_library(cl_name): 107 | json_response = s.get('https://' + vcip + '/rest/com/vmware/content/library') 108 | if json_response.ok: 109 | results = json.loads(json_response.text)["value"] 110 | for result in results: 111 | json_response = s.get('https://' + vcip + '/rest/com/vmware/content/library/id:' + result) 112 | if json_response.ok: 113 | cl_library = json.loads(json_response.text)["value"] 114 | if cl_library["name"] == cl_name: 115 | return cl_library["id"] 116 | else: 117 | return 0 118 | return 0 119 | 120 | 121 | def get_nsx_switch(cluster): 122 | json_response = s.get('https://'+vcip+'/api/vcenter/namespace-management/distributed-switch-compatibility?cluster='+cluster+'&compatible=true') 123 | if json_response.ok: 124 | results = json.loads(json_response.text) 125 | # Making assumption that there is only 1 distributed switch. 126 | nsx_sw_id = results[0]['distributed_switch'] 127 | return nsx_sw_id 128 | else: 129 | return 0 130 | 131 | 132 | def get_nsx_edge_cluster(cluster,dvs): 133 | json_response = s.get('https://'+vcip+'/api/vcenter/namespace-management/edge-cluster-compatibility?cluster='+cluster+'&compatible=true&distributed_switch='+dvs) 134 | if json_response.ok: 135 | results = json.loads(json_response.text) 136 | edge_id = results[0]['edge_cluster'] 137 | return edge_id 138 | else: 139 | return 0 140 | 141 | 142 | def get_mgmt_network(mgmt_nw_name, dc): 143 | json_response = s.get('https://' + vcip + '/rest/vcenter/network?filter.datacenters=' + dc) 144 | if json_response.ok: 145 | results = json.loads(json_response.text)["value"] 146 | for result in results: 147 | if result["name"] == mgmt_nw_name: 148 | return result["network"] 149 | else: 150 | return 0 151 | return 0 152 | 153 | 154 | def check_wcp_cluster_compatibility(cluster,net_p,skip_compat): 155 | if skip_compat: 156 | return 1 157 | else: 158 | json_response = s.get('https://' + vcip + '/api/vcenter/namespace-management/cluster-compatibility?network_provider='+ net_p) 159 | if json_response.ok: 160 | results = json.loads(json_response.text) 161 | res = next((sub for sub in results if sub['cluster'] == cluster), None) 162 | if res['compatible'] == True: 163 | return 1 164 | else: 165 | return 0 166 | else: 167 | return 0 168 | 169 | 170 | def check_wcp_cluster_status(cluster): 171 | json_response = s.get('https://' + vcip + '/api/vcenter/namespace-management/clusters/' + cluster) 172 | if json_response.ok: 173 | result = json.loads(json_response.text) 174 | if result["config_status"] == "RUNNING": 175 | if result["kubernetes_status"] == "READY": 176 | return result["api_server_cluster_endpoint"] 177 | else: 178 | return 0 179 | else: 180 | return 0 181 | 182 | 183 | def check_wcp_harbor_status(cluster): 184 | json_response = s.get('https://' + vcip + '/rest/vcenter/content/registries/harbor') 185 | if json_response.ok: 186 | results = json.loads(json_response.text)["value"] 187 | for result in results: 188 | if result["cluster"] == cluster: 189 | return result["registry"] 190 | else: 191 | return 0 192 | else: 193 | return 0 194 | 195 | 196 | def check_wcp_harbor_ui_url_status(cluster): 197 | json_response = s.get('https://' + vcip + '/rest/vcenter/content/registries/harbor') 198 | if json_response.ok: 199 | results = json.loads(json_response.text)["value"] 200 | for result in results: 201 | if result["cluster"] == cluster: 202 | return result["ui_access_url"] 203 | else: 204 | return 0 205 | else: 206 | return 0 207 | 208 | 209 | def check_wcp_ns_status(ns_name): 210 | json_response = s.get('https://' + vcip + '/api/vcenter/namespaces/instances/' + ns_name) 211 | if json_response.ok: 212 | result = json.loads(json_response.text) 213 | if result["config_status"] == "RUNNING": 214 | return 1 215 | else: 216 | return 0 217 | else: 218 | return 0 219 | 220 | 221 | with open(filename, ) as f: 222 | yamldocs = yaml.load_all(f, Loader=yaml.FullLoader) 223 | for yamldoc in yamldocs: 224 | objtype = yamldoc["kind"] 225 | vcip = yamldoc["metadata"]["vcenter"] 226 | datacenter = yamldoc["metadata"]["datacenter"] 227 | cluster = yamldoc["metadata"]["cluster"] 228 | spec = yamldoc["spec"] 229 | s = "Global" 230 | s = requests.Session() 231 | s.verify = False 232 | 233 | # Connect to VCenter and start a session 234 | vcsession = s.post('https://' + vcip + '/rest/com/vmware/cis/session', auth=(userid, password)) 235 | if not vcsession.ok: 236 | print("Session creation is failed, please check vcenter connection") 237 | sys.exit() 238 | token = json.loads(vcsession.text)["value"] 239 | token_header = {'vmware-api-session-id': token} 240 | 241 | # Based on the datacenter get all datacenters 242 | datacenter_object = s.get('https://' + vcip + '/rest/vcenter/datacenter?filter.names=' + datacenter) 243 | if len(json.loads(datacenter_object.text)["value"]) == 0: 244 | print("No datacenter found, please enter valid datacenter name") 245 | sys.exit() 246 | datacenter_id = json.loads(datacenter_object.text)["value"][0].get("datacenter") 247 | 248 | # Based on the cluster name get the cluster_id 249 | cluster_object = s.get( 250 | 'https://' + vcip + '/rest/vcenter/cluster?filter.names=' + cluster + '&filter.datacenters=' + datacenter_id) 251 | if len(json.loads(cluster_object.text)["value"]) == 0: 252 | print("No cluster found, please enter valid cluster name") 253 | sys.exit() 254 | cluster_id = json.loads(cluster_object.text)["value"][0].get("cluster") 255 | 256 | if verb == "create": 257 | 258 | # create wcpCluster 259 | if objtype == "wcpCluster": 260 | del yamldoc["kind"] 261 | del yamldoc["metadata"] 262 | if check_wcp_cluster_compatibility(cluster_id, yamldoc["spec"]["network_provider"],skip_compat): 263 | if not check_wcp_cluster_status(cluster_id): 264 | 265 | temp1 = get_storage_policy(yamldoc["spec"].get("ephemeral_storage_policy")) 266 | if not temp1: 267 | print("wcpCluster/" + cluster + " check value for ephemeral_storage_policy") 268 | sys.exit() 269 | temp2 = get_storage_policy(yamldoc["spec"].get("master_storage_policy")) 270 | if not temp2: 271 | print("wcpCluster/" + cluster + " check value for master_storage_policy") 272 | sys.exit() 273 | temp3 = get_storage_policy(yamldoc["spec"]["image_storage"].get("storage_policy")) 274 | if not temp3: 275 | print("wcpCluster/" + cluster + " check value for storage_policy") 276 | sys.exit() 277 | temp4 = get_content_library(yamldoc["spec"].get("default_kubernetes_service_content_library")) 278 | if not temp4: 279 | print ("wcpCluster/"+cluster+" check value for default_kubernetes_service_content_library") 280 | sys.exit() 281 | temp5 = get_mgmt_network(yamldoc["spec"]["master_management_network"].get("network"), datacenter_id) 282 | if not temp5: 283 | print("wcpCluster/" + cluster + " check value for master_management_network - network") 284 | sys.exit() 285 | 286 | #Process for NSX config 287 | if yamldoc["spec"]["network_provider"] == "NSXT_CONTAINER_PLUGIN": 288 | temp6 = get_nsx_switch(cluster_id) 289 | if not temp6: 290 | print("wcpCluster/" + cluster + " no compatiable NSX switch for cluster") 291 | sys.exit() 292 | temp7 = get_nsx_edge_cluster(cluster_id, temp6) 293 | if not temp7: 294 | print("wcpCluster/" + cluster + " no compatiable NSX edge cluster") 295 | sys.exit() 296 | yamldoc["spec"]["ncp_cluster_network_spec"].update({"cluster_distributed_switch": temp6}) 297 | yamldoc["spec"]["ncp_cluster_network_spec"].update({"nsx_edge_cluster": temp7}) 298 | 299 | # Process BYO LB 300 | else: 301 | temp6 = get_mgmt_network(yamldoc["spec"]["workload_networks_spec"]["supervisor_primary_workload_network"]["vsphere_network"].get("portgroup"), datacenter_id) 302 | if not temp6: 303 | print("wcpCluster/" + cluster + " check value for master_management_network - network") 304 | sys.exit() 305 | yamldoc["spec"]["workload_networks_spec"]["supervisor_primary_workload_network"]["vsphere_network"].update({"portgroup": temp6}) 306 | 307 | # Update ...if any of the above is 0 then quit 308 | yamldoc["spec"].update({"ephemeral_storage_policy": temp1}) 309 | yamldoc["spec"].update({"master_storage_policy": temp2}) 310 | yamldoc["spec"]["image_storage"].update({"storage_policy": temp3}) 311 | yamldoc["spec"].update({"default_kubernetes_service_content_library": temp4}) 312 | yamldoc["spec"]["master_management_network"].update({"network": temp5}) 313 | 314 | json_payload = json.loads(json.dumps(yamldoc["spec"])) 315 | json_response = s.post('https://'+vcip+'/api/vcenter/namespace-management/clusters/'+cluster_id+'?action=enable', headers=headers,json=json_payload) 316 | if json_response.ok: 317 | print("wcpCluster/" + cluster + " creation started") 318 | else: 319 | print("wcpCluster/" + cluster + " creation failed") 320 | print(json_response.text) 321 | else: 322 | print ("wcpCluster/"+cluster+" already operational at https://"+check_wcp_cluster_status(cluster_id)) 323 | else: 324 | print("wcpCluster/" + cluster + " not compatiable") 325 | 326 | # create wcpRegistry 327 | if objtype == "wcpRegistry": 328 | if not check_wcp_harbor_status(cluster_id): 329 | del yamldoc["kind"] 330 | del yamldoc["metadata"] 331 | client_token = generate_random_uuid() 332 | yamldoc.update({"client_token": client_token}) 333 | yamldoc["spec"].update({"cluster": cluster_id}) 334 | reg_creation_started = 0 335 | i = 0 336 | # Update variables and proceed 337 | for sp in yamldoc["spec"]["storage"]: 338 | temp1 = get_storage_policy(sp["policy"]) 339 | if temp1: 340 | yamldoc["spec"]["storage"][i].update({"policy": temp1}) 341 | else: 342 | print("wcpNamespace/" + spec["namespace"] + " invalid storage policy specified") 343 | sys.exit() 344 | i = i + 1 345 | json_payload = json.loads(json.dumps(yamldoc)) 346 | headers.update({'vmware-api-session-id': token}) 347 | json_response = s.post('https://'+vcip+'/rest/vcenter/content/registries/harbor',headers=headers,json=json_payload) 348 | if json_response.ok: 349 | print("wcpRegistry/Harbor creation started") 350 | reg_creation_started = 1 351 | else: 352 | print("wcpRegistry/Harbor failed") 353 | print(json_response.text) 354 | 355 | if reg_creation_started == 1: 356 | while not check_wcp_harbor_ui_url_status(cluster_id): 357 | print("Sleeping for 20 sec...") 358 | time.sleep(20) 359 | print("wcpRegistry/Harbor access URL " + check_wcp_harbor_ui_url_status(cluster_id)) 360 | else: 361 | print("wcpRegistry/Harbor already running") 362 | 363 | # create wcpNamespace 364 | if objtype == "wcpNamespace": 365 | if not check_wcp_ns_status(spec["namespace"]): 366 | spec.update({"cluster": cluster_id}) 367 | i = 0 368 | # Update variables and proceed 369 | for sp in spec["storage_specs"]: 370 | temp1 = get_storage_policy(sp["policy"]) 371 | if temp1: 372 | spec["storage_specs"][i].update({"policy": temp1}) 373 | else: 374 | print("wcpNamespace/" + spec["namespace"] + " invalid storage policy specified") 375 | sys.exit() 376 | i = i + 1 377 | json_payload = json.loads(json.dumps(spec)) 378 | json_response = s.post('https://'+vcip+'/api/vcenter/namespaces/instances',headers=headers,json=json_payload) 379 | if json_response.ok: 380 | print("wcpNamespace/" + spec["namespace"] + " created") 381 | else: 382 | print("wcpNamespace/" + spec["namespace"] + " creation failed") 383 | print(json_response.text) 384 | else: 385 | print("wcpNamespace/" + spec["namespace"] + " already exists. No changes made") 386 | 387 | # create wcpContentLibrary 388 | if objtype == "wcpContentLibrary": 389 | if not get_content_library(yamldoc["spec"].get("name")): 390 | del yamldoc["kind"] 391 | del yamldoc["metadata"] 392 | client_token = generate_random_uuid() 393 | yamldoc.update({"client_token": client_token}) 394 | i = 0 395 | for sb in yamldoc["spec"]["storage_backings"]: 396 | temp1 = get_storage_id(sb["datastore_id"], datacenter_id) 397 | if temp1: 398 | yamldoc["spec"]["storage_backings"][i].update({"datastore_id": temp1}) 399 | else: 400 | print("wcpContentLibrary invalid storage name specified") 401 | sys.exit() 402 | i = i + 1 403 | yamldoc["create_spec"] = yamldoc.pop("spec") 404 | json_payload = json.loads(json.dumps(yamldoc)) 405 | headers.update({'vmware-api-session-id': token}) 406 | json_response = s.post('https://'+vcip+'/rest/com/vmware/content/subscribed-library',headers=headers,json=json_payload) 407 | if json_response.ok: 408 | print("wcpContentLibrary/Subscribed_library created") 409 | else: 410 | print("wcpContentLibrary/Subscribed_library created") 411 | print(json_response.text) 412 | else: 413 | print("wcpContentLibrary/Subscribed_library already running") 414 | 415 | elif verb == "delete": 416 | 417 | # delete wcpCluster 418 | if objtype == "wcpCluster": 419 | if check_wcp_cluster_status(cluster_id): 420 | # Check if you want to delete ??? 421 | json_response = s.post('https://'+vcip+'/api/vcenter/namespace-management/clusters/'+cluster_id+'?action=disable') 422 | if json_response.ok: 423 | print("wcpCluster/" + cluster + " delete initiated") 424 | else: 425 | print("wcpCluster/" + cluster + " delete failed") 426 | print(json_response.text) 427 | else: 428 | print("wcpCluster/" + cluster + " not operational") 429 | 430 | # delete wcpRegistry 431 | if objtype == "wcpRegistry": 432 | reg_destruction_started = 0 433 | harbor_id = check_wcp_harbor_status(cluster_id) 434 | if harbor_id: 435 | json_response = s.delete('https://'+vcip+'/rest/vcenter/content/registries/harbor/'+harbor_id, headers=token_header) 436 | if (json_response.ok): 437 | print("wcpRegistry/Harbor deletion started") 438 | reg_destruction_started = 1 439 | else: 440 | print("wcpRegistry/Harbor deletion failed") 441 | print(json_response.text) 442 | 443 | if reg_destruction_started == 1: 444 | while check_wcp_harbor_status(cluster_id): 445 | print("Sleeping for 20 sec...") 446 | time.sleep(20) 447 | else: 448 | print("wcpRegistry/Harbor not found") 449 | 450 | # delete wcpNamespace 451 | if objtype == "wcpNamespace": 452 | json_response = s.delete('https://' + vcip + '/api/vcenter/namespaces/instances/' + spec["namespace"]) 453 | if (json_response.ok): 454 | print("wcpNamespace/" + spec["namespace"] + " deleted") 455 | else: 456 | print("wcpNamespace/" + spec["namespace"] + " deletion failed") 457 | print(json_response.text) 458 | 459 | # delete wcpContentLibrary 460 | if objtype == "wcpContentLibrary": 461 | contentlibrary_id = get_content_library(yamldoc["spec"].get("name")) 462 | if contentlibrary_id: 463 | json_response = s.delete('https://'+vcip+'/rest/com/vmware/content/subscribed-library/id:'+contentlibrary_id) 464 | if (json_response.ok): 465 | print("wcpContentLibrary/Subscribed_library successfully deleted") 466 | else: 467 | print("wcpContentLibrary/Subscribed_library deletion failed") 468 | result = json.loads(json_response.text) 469 | print(json.dumps(result, indent=2, sort_keys=True)) 470 | else: 471 | print("wcpContentLibrary/Subscribed library not found") 472 | 473 | elif verb == "apply": 474 | 475 | # apply wcpCluster 476 | if objtype == "wcpCluster": 477 | del yamldoc["kind"] 478 | del yamldoc["metadata"] 479 | if check_wcp_cluster_compatibility(cluster_id, yamldoc["spec"]["network_provider"],skip_compat): 480 | if not check_wcp_cluster_status(cluster_id): 481 | 482 | temp1 = get_storage_policy(yamldoc["spec"].get("ephemeral_storage_policy")) 483 | temp2 = get_storage_policy(yamldoc["spec"].get("master_storage_policy")) 484 | temp3 = get_storage_policy(yamldoc["spec"]["image_storage"].get("storage_policy")) 485 | temp4 = get_content_library(yamldoc["spec"].get("default_kubernetes_service_content_library")) 486 | temp5 = get_mgmt_network(yamldoc["spec"]["master_management_network"].get("network"), datacenter_id) 487 | if not temp5: 488 | print("wcpCluster/" + cluster + " check value for master_management_network - network") 489 | sys.exit() 490 | 491 | #Process for NSX config 492 | if yamldoc["spec"]["network_provider"] == "NSXT_CONTAINER_PLUGIN": 493 | temp6 = get_nsx_switch(cluster_id) 494 | if not temp6: 495 | print("wcpCluster/" + cluster + " no compatiable NSX switch for cluster") 496 | sys.exit() 497 | temp7 = get_nsx_edge_cluster(cluster_id, temp6) 498 | if not temp7: 499 | print("wcpCluster/" + cluster + " no compatiable NSX edge cluster") 500 | sys.exit() 501 | yamldoc["spec"]["ncp_cluster_network_spec"].update({"cluster_distributed_switch": temp6}) 502 | yamldoc["spec"]["ncp_cluster_network_spec"].update({"nsx_edge_cluster": temp7}) 503 | 504 | # Process BYO LB 505 | else: 506 | temp6 = get_mgmt_network(yamldoc["spec"]["workload_networks_spec"]["supervisor_primary_workload_network"]["vsphere_network"].get("portgroup"), datacenter_id) 507 | if not temp6: 508 | print("wcpCluster/" + cluster + " check value for master_management_network - network") 509 | sys.exit() 510 | yamldoc["spec"]["workload_networks_spec"]["supervisor_primary_workload_network"]["vsphere_network"].update({"portgroup": temp6}) 511 | 512 | # Update any of the above is 0 then quit 513 | 514 | yamldoc["spec"].update({"ephemeral_storage_policy": temp1}) 515 | yamldoc["spec"].update({"master_storage_policy": temp2}) 516 | yamldoc["spec"]["image_storage"].update({"storage_policy": temp3}) 517 | yamldoc["spec"].update({"default_kubernetes_service_content_library": temp4}) 518 | yamldoc["spec"]["master_management_network"].update({"network": temp5}) 519 | 520 | json_payload = json.loads(json.dumps(yamldoc["spec"])) 521 | json_response = s.post('https://'+vcip+'/api/vcenter/namespace-management/clusters/'+cluster_id+'?action=enable', headers=headers,json=json_payload) 522 | if json_response.ok: 523 | print("wcpCluster/" + cluster + " creation started") 524 | else: 525 | print("wcpCluster/" + cluster + " creation failed") 526 | print(json_response.text) 527 | else: 528 | print ("wcpCluster/"+cluster+" already operational at https://"+check_wcp_cluster_status(cluster_id)) 529 | else: 530 | print("wcpCluster/" + cluster + " not compatiable") 531 | 532 | # TO DO : stub to patch server 533 | # json_response = s.patch('https://'+vcip+'/api/vcenter/namespace-management/clusters/'+cluster_id, headers=headers,json=json_payload) 534 | # if json_response.ok: 535 | # print ("wcpCluster/"+cluster+" updated") 536 | # else: 537 | # print ("wcpCluster/"+cluster+" update failed") 538 | # print (json_response.text) 539 | 540 | # To rotate password 541 | # json_response = s.post('https://'+vcip+'/api/vcenter/namespace-management/clusters/'+cluster_id+'?action=rotate_password') 542 | # if json_response.ok: 543 | # print ("wcpCluster/"+cluster+" password rotated") 544 | # else: 545 | # print ("wcpCluster/"+cluster+" password rotation failed") 546 | # print (json_response.text) 547 | 548 | # apply wcpRegistry 549 | if objtype == "wcpRegistry": 550 | # same as create as no modify option 551 | if not check_wcp_harbor_status(cluster_id): 552 | del yamldoc["kind"] 553 | del yamldoc["metadata"] 554 | client_token = generate_random_uuid() 555 | yamldoc.update({"client_token": client_token}) 556 | yamldoc["spec"].update({"cluster": cluster_id}) 557 | i = 0 558 | # Update variables and proceed 559 | for sp in yamldoc["spec"]["storage"]: 560 | temp1 = get_storage_policy(sp["policy"]) 561 | if temp1: 562 | yamldoc["spec"]["storage"][i].update({"policy": temp1}) 563 | else: 564 | print("wcpNamespace/" + spec["namespace"] + " invalid storage policy specified") 565 | sys.exit() 566 | i = i + 1 567 | json_payload = json.loads(json.dumps(yamldoc)) 568 | headers.update({'vmware-api-session-id': token}) 569 | json_response = s.post('https://'+vcip+'/rest/vcenter/content/registries/harbor',headers=headers,json=json_payload) 570 | if json_response.ok: 571 | print("wcpRegistry/Harbor created") 572 | reg_creation_started = 1 573 | else: 574 | print("wcpRegistry/Harbor failed") 575 | print(json_response.text) 576 | 577 | if reg_creation_started == 1: 578 | while not check_wcp_harbor_ui_url_status(cluster_id): 579 | print("Sleeping for 20 sec...") 580 | time.sleep(20) 581 | print("wcpRegistry/Harbor access URL " + check_wcp_harbor_ui_url_status(cluster_id)) 582 | else: 583 | print("wcpRegistry/Harbor already running") 584 | 585 | # apply wcpNamespace 586 | if objtype == "wcpNamespace": 587 | if check_wcp_ns_status(spec["namespace"]): 588 | spec.update({"cluster": cluster_id}) 589 | i = 0 590 | # Update variables and proceed 591 | for sp in spec["storage_specs"]: 592 | temp1 = get_storage_policy(sp["policy"]) 593 | if temp1: 594 | spec["storage_specs"][i].update({"policy": temp1}) 595 | else: 596 | print("wcpNamespace/" + spec["namespace"] + " invalid storage policy specified") 597 | sys.exit() 598 | i = i + 1 599 | json_payload = json.loads(json.dumps(spec)) 600 | json_response = s.put('https://'+vcip+'/api/vcenter/namespaces/instances/'+spec["namespace"],headers=headers,json=json_payload) 601 | if json_response.ok: 602 | print("wcpNamespace/" + spec["namespace"] + " updated") 603 | else: 604 | print("wcpNamespace/" + spec["namespace"] + " update failed") 605 | print(json_response.text) 606 | else: 607 | spec.update({"cluster": cluster_id}) 608 | i = 0 609 | # Update variables and proceed 610 | for sp in spec["storage_specs"]: 611 | temp1 = get_storage_policy(sp["policy"]) 612 | if temp1: 613 | spec["storage_specs"][i].update({"policy": temp1}) 614 | else: 615 | print("wcpNamespace/" + spec["namespace"] + " invalid storage policy specified") 616 | sys.exit() 617 | i = i + 1 618 | json_payload = json.loads(json.dumps(spec)) 619 | json_response = s.post('https://'+vcip+'/api/vcenter/namespaces/instances',headers=headers,json=json_payload) 620 | if json_response.ok: 621 | print("wcpNamespace/" + spec["namespace"] + " created") 622 | else: 623 | print("wcpNamespace/" + spec["namespace"] + " creation failed") 624 | print(json_response.text) 625 | 626 | # apply wcpContentLibrary 627 | if objtype == "wcpContentLibrary": 628 | if not get_content_library(yamldoc["spec"].get("name")): 629 | del yamldoc["kind"] 630 | del yamldoc["metadata"] 631 | client_token = generate_random_uuid() 632 | yamldoc.update({"client_token": client_token}) 633 | i = 0 634 | for sb in yamldoc["spec"]["storage_backings"]: 635 | temp1 = get_storage_id(sb["datastore_id"], datacenter_id) 636 | if temp1: 637 | yamldoc["spec"]["storage_backings"][i].update({"datastore_id": temp1}) 638 | else: 639 | print("wcpContentLibrary invalid storage name specified") 640 | sys.exit() 641 | i = i + 1 642 | yamldoc["create_spec"] = yamldoc.pop("spec") 643 | json_payload = json.loads(json.dumps(yamldoc)) 644 | headers.update({'vmware-api-session-id': token}) 645 | json_response = s.post('https://'+vcip+'/rest/com/vmware/content/subscribed-library',headers=headers,json=json_payload) 646 | if json_response.ok: 647 | print("wcpContentLibrary/Subscribed_library created") 648 | else: 649 | print("wcpContentLibrary/Subscribed_library created") 650 | print(json_response.text) 651 | else: 652 | print("wcpContentLibrary/Subscribed_library already running") 653 | 654 | elif verb == 'describe': 655 | 656 | # describe wcpCluster 657 | if objtype == "wcpCluster": 658 | if check_wcp_cluster_status(cluster_id): 659 | json_response = s.get('https://'+vcip+'/api/vcenter/namespace-management/clusters/'+cluster_id) 660 | if json_response.ok: 661 | result = json.loads(json_response.text) 662 | print(json.dumps(result, indent=2, sort_keys=True)) 663 | else: 664 | print("wcpCluster/" + cluster + " error describing") 665 | else: 666 | print("wcpCluster/" + cluster + " not ready") 667 | 668 | # describe wcpRegistry 669 | if objtype == "wcpRegistry": 670 | harbor_id = check_wcp_harbor_status(cluster_id) 671 | if harbor_id: 672 | json_response = s.get('https://' + vcip + '/rest/vcenter/content/registries/harbor/' + harbor_id) 673 | if (json_response.ok): 674 | result = json.loads(json_response.text) 675 | print(json.dumps(result, indent=2, sort_keys=True)) 676 | else: 677 | print("wcpRegistry/Harbor error describing") 678 | else: 679 | print("wcpRegistry/Harbor not found") 680 | 681 | # describe wcpNamespace 682 | if objtype == "wcpNamespace": 683 | json_response = s.get('https://' + vcip + '/api/vcenter/namespaces/instances') 684 | if (json_response.ok): 685 | results = json.loads(json_response.text) 686 | for result in results: 687 | if result["cluster"] == cluster_id: 688 | json_response = s.get('https://'+vcip+'/api/vcenter/namespaces/instances/'+result["namespace"]) 689 | if (json_response.ok): 690 | nsresult = json.loads(json_response.text) 691 | print(json.dumps(nsresult, indent=2, sort_keys=True)) 692 | else: 693 | print("wcpNamespace" + result["namespace"] + " error describing") 694 | else: 695 | print("wcpNamespace error describing") 696 | 697 | # describe wcpContentLibrary 698 | if objtype == "wcpContentLibrary": 699 | contentlibrary_id = get_content_library(yamldoc["spec"].get("name")) 700 | if contentlibrary_id: 701 | json_response = s.get('https://'+vcip+'/rest/com/vmware/content/subscribed-library/id:'+contentlibrary_id) 702 | if (json_response.ok): 703 | result = json.loads(json_response.text) 704 | print(json.dumps(result, indent=2, sort_keys=True)) 705 | else: 706 | print("wcpContentLibrary/Subscribed library error describing") 707 | else: 708 | print("wcpContentLibrary/Subscribed library not found") 709 | 710 | # Clean up and exit... 711 | session_delete = s.delete('https://' + vcip + '/rest/com/vmware/cis/session', auth=(userid, password)) 712 | --------------------------------------------------------------------------------