SDDC Import/Export for VMware Cloud on AWS – Part V – Adding a New Feature – Writing the Export Code

In Part IV, I figured out the API calls for Service Access. In this post, I will implement the Python code required to export.

Remember this screenshot from the previous post – the Linked VPC API calls. Note that we do not have an API call to create the VPC link – VPC linking is not done here.

For most other export/import operations, we issue a GET API call to retrieve the JSON, then a PUT request to save that exported JSON in a new SDDC. In this case, the VPC linking is typically done when an SDDC is created. With this feature, we are not trying to restore the link between the SDDC and the original VPC. We are only trying to restore the connected services.

To implement the feature, I decided we need to export the data from linked-vpcs as well as connected-services, but we are not going to attempt to restore the VPC link. All we are going to do is restore the state of the connected service.

First, I need to add a flag to config.ini – an export flag in the export section and an import flag in the import section.

# Export service access?
service_access_export = True
# Import service access?
service_access_import = True

Next, the Python code to load the config flags and config filename. I considered whether I wanted the filename configurable, but I decided to hard-code the name. The reason for this is the way the export needs to run – I need to find all Service Access configurations, then export a config file for each one. I want to be in control of the filenames.

The loadConfigFilename function will check the ini file for a matching key, and if it’s not found it will resort to hard-coded values

   def loadConfigFilename(self,config,section,key):
        """Loads a JSON filename from the config file, with hard-coded defaults if they are missing."""
            filename  = config.get(section, key)
            return filename
            if (key == 'cgw_export_filename'):
                return 'cgw.json'
            elif (key == 'cgw_import_filename'):
                return 'cgw.json'
# truncated

I add these lines of code to the ConfigLoader function in

self.service_access_export  = self.loadConfigFlag(config,"exportConfig","service_access_export")
self.service_access_import  = self.loadConfigFlag(config,"importConfig","service_access_import")
self.service_access_filename = self.loadConfigFilename(config,"importConfig","service_access_filename")

I add the hard-coded config filename to loadConfigFilename()

            elif (key == 'service_access_filename'):
                return 'service_access.json'

Now I write the function to perform the export.

Line 5 generates the required header for my API call.
Line 6 gives me my URL. The script builds the base URL for API calls dynamically, saving it in self.proxy_url, so all we need to add on is the path to the specific API call.

Lines 7-14 are a try/except block invoking the linked-vpcs API call. Line 8 makes the call. Line 9 checks for an HTTP status code – this is no different than using your browser to load a webpage – a successful HTTP request returns HTTP/200 status code. If it’s not 200, we save any error text and return False to indicate an error condition. If we end up in the except block we do the same thing, save the error message and return False.

In line 16, we are turning the text of the HTTP response into a JSON object.

Line 17, we parse the JSON out of the surrounding ‘results’ object.

Line 18 – 21 – you can only have a single VPC connected to an SDDC, this code makes sure we only have one returned in the JSON.

Line 23-26 we save the JSON to the filesystem.

We now have our connected VPC, but we need to iterate through the connected-services to get all of the service access configuration.

Line 29 we build out the connected-services API URL, using the connected VPC ID that we retrieved from the prior call.

Lines 30-38 are the same try/except block as the previous try/except block.

Lines 40-46 do the same thing as 23-26, saving JSON to the filesystem. The difference here is that we first iterate through all of the connected services in line 43. The API call returns all of the connected services, we have to loop through them all and dump them one by one.

In line 44, we build the filename. self.service_access_filename is ‘service_access.json’. If the service that we’re saving is the ‘s3’ service, we will end up with a filename of ‘s3-service_access.json’

In line 47, we return True to indicate success

def exportServiceAccess(self):
        """Exports SDDC Service Access config to a JSON file"""

        # First, retrieve the linked VPC ID
        myHeader = {'csp-auth-token': self.access_token}
        myURL = (self.proxy_url + '/cloud-service/api/v1/infra/linked-vpcs')
            response = requests.get(myURL,headers=myHeader)
            if response.status_code != 200:
                self.lastJSONResponse  = f'API Call Status {response.status_code}, text:{response.text}'
                return False
            self.lastJSONResponse = f'API Call Status {response.status_code}, text:{response.text}'
            return False

        json_response = response.json()       
        linked_vpcs = json_response["results"]
        num_vpcs = len(linked_vpcs)
        if num_vpcs != 1:
            print('Unexpected linked VPC count: ',num_vpcs)
            return False
            linked_vpc = linked_vpcs[0]
            fname = self.export_path / self.service_access_filename
            with open(fname, 'w+') as outfile:

            # Use the linked VPC ID to discover connected services
            myURL = (self.proxy_url + '/cloud-service/api/v1/infra/linked-vpcs/' + linked_vpc['linked_vpc_id'] + '/connected-services')
                response = requests.get(myURL,headers=myHeader)
                if response.status_code != 200:
                    self.lastJSONResponse = f'API Call Status {response.status_code}, text:{response.text}'
                    return False
                self.lastJSONResponse = f'API Call Status {response.status_code}, text:{response.text}'
                return False
            json_response = response.json()
            connected_services = json_response['results']
            for svc in connected_services:
                fname = self.export_path / (svc['name'] + '-' + self.service_access_filename)
                with open(fname, 'w+') as outfile:
        return True

Finally, I need to add code in to call the exportServiceAccess function I just wrote.

       if ioObj.service_access_export is True:
            print("Beginning Service Access export...")
            retval = ioObj.exportServiceAccess()
            if retval is True:
                print("Service access exported.")
                print("Service access export error: {}.".format(ioObj.lastJSONResponse))
            print("Service access export skipped.")

That’s it for the export function. In the next post, we will work on the import function.


Leave a Reply

Your email address will not be published. Required fields are marked *