This commit is contained in:
Omar Zevallos 2018-12-31 14:37:43 -05:00
Родитель 16c08b400f
Коммит 50abd47cab
4 изменённых файлов: 187 добавлений и 190 удалений

Просмотреть файл

@ -24,7 +24,7 @@ steps:
AZURE_CLIENT_ID: $(SPAPPID)
AZURE_CLIENT_SECRET: $(SPPW)
- script: |
python3 test/avere_template_deploy.py --skip-az-ops
python3 test/test_avere_template_deploy.py --skip-az-ops
displayName: 'Test Avere vFXT template-based deployment'
env:
AVERE_ADMIN_PW: $(controllerpassword)

Просмотреть файл

@ -1,228 +1,110 @@
#!/usr/bin/python3
"""
Test template-based Avere vFXT deployment.
Class used for testing template-based deployment of the Avere vFXT product.
Assumptions:
1. The caller/script is able to write to the current working directory.
2. Azure secrets are stored in the following environment variables:
* AVERE_ADMIN_PW
* AVERE_CONTROLLER_PW
* AZURE_CLIENT_ID
* AZURE_CLIENT_SECRET
* AZURE_TENANT_ID
* AZURE_SUBSCRIPTION_ID
Objects require the following environment variables at instantiation:
* AVERE_ADMIN_PW
* AVERE_CONTROLLER_PW
* AZURE_CLIENT_ID
* AZURE_CLIENT_SECRET
* AZURE_TENANT_ID
* AZURE_SUBSCRIPTION_ID
"""
import argparse
import json
import os
import random
import sys
import time
from pprint import pformat
from random import choice
from string import ascii_lowercase, digits
from urllib.request import urlretrieve
import requests
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.resource.resources.models import DeploymentMode
# GLOBAL VARIABLES ############################################################
DEFAULT_LOCATION = 'eastus2'
DEPLOY_PARAMS = {}
RESOURCE_GROUP_CREATED = False
SCRIPT_ARGS = None
TEMPLATE_URL = 'https://raw.githubusercontent.com/Azure/Avere/master/src/vfxt/azuredeploy-auto.json'
TEMPLATE_LOCAL_FILE = os.path.basename(TEMPLATE_URL)
class AvereTemplateDeploy:
def __init__(self, deploy_params={}, resource_group=None,
location='eastus2', debug=True):
"""Initialize, authenticate to Azure, generate deploy params."""
self._template_url = 'https://raw.githubusercontent.com/Azure/Avere/master/src/vfxt/azuredeploy-auto.json'
self.debug = debug
# FUNCTIONS ###################################################################
self.deploy_params = deploy_params
self.resource_group = self.deploy_params.pop('resourceGroup',
resource_group)
self.location = self.deploy_params.pop('location', location)
def load_credentials():
"""Loads Azure credentials from environment variables."""
print('> Load Azure credentials.')
return ResourceManagementClient(
credentials=ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID']
),
subscription_id=os.environ['AZURE_SUBSCRIPTION_ID']
)
self._debug('> Loading Azure credentials')
self.rm_client = ResourceManagementClient(
credentials=ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID']
),
subscription_id=os.environ['AZURE_SUBSCRIPTION_ID']
)
def load_params():
"""
Loads the parameters needed in this script (e.g., resource group name).
If the user specified a parameters file, load those values into
DEPLOY_PARAMS. Otherwise, generate the parameter values and store those
values for re-use. The generated parameter values are stored in the current
working directory as <resource-group-name>.params.json.
"""
global DEPLOY_PARAMS
if SCRIPT_ARGS.param_file: # Open user-specified params file.
with open(SCRIPT_ARGS.param_file) as config_file:
DEPLOY_PARAMS = json.load(config_file)
else: # Generate and store params.
random_id = 'av' + \
''.join(random.choice(ascii_lowercase + digits) for _ in range(6))
rg_name = 'aapipe-' + random_id + '-rg'
DEPLOY_PARAMS = {
'resource-group': rg_name,
'parameters': {
'virtualNetworkResourceGroup': rg_name,
if not self.deploy_params:
random_id = 'av' + \
''.join(choice(ascii_lowercase + digits) for _ in range(6))
self.resource_group = 'aapipe-' + random_id + '-rg'
self.deploy_params = {
'virtualNetworkResourceGroup': self.resource_group,
'virtualNetworkName': random_id + '-vnet',
'virtualNetworkSubnetName': random_id + '-subnet',
'avereBackedStorageAccountName': random_id + 'sa',
'controllerName': random_id + '-con',
'controllerAuthenticationType': 'password'
}
}
with open(DEPLOY_PARAMS['resource-group'] + '.params.json', 'w') as pf:
json.dump(DEPLOY_PARAMS, pf)
self._debug('> Generated deploy parameters: \n{}'.format(
json.dumps(self.deploy_params, indent=4)))
# Set location/region. Precedence: command-line > params file > default
if not SCRIPT_ARGS.location:
SCRIPT_ARGS.location = DEPLOY_PARAMS.pop('location', DEFAULT_LOCATION)
_debug('SCRIPT_ARGS.location = {}'.format(SCRIPT_ARGS.location))
_debug('DEPLOY_PARAMS (before secrets): \n{}'.format(
json.dumps(DEPLOY_PARAMS, indent=4)))
# Add secrets to the parameters for template deployment.
secrets = {
'adminPassword': os.environ['AVERE_ADMIN_PW'],
'controllerPassword': os.environ['AVERE_CONTROLLER_PW'],
'servicePrincipalAppId': os.environ['AZURE_CLIENT_ID'],
'servicePrincipalPassword': os.environ['AZURE_CLIENT_SECRET'],
'servicePrincipalTenant': os.environ['AZURE_TENANT_ID']
}
DEPLOY_PARAMS['parameters'] = {**DEPLOY_PARAMS['parameters'], **secrets}
def create_resource_group(rm_client):
"""Creates an Azure resource group."""
global RESOURCE_GROUP_CREATED
print('> Creating resource group: ' + DEPLOY_PARAMS['resource-group'])
if not SCRIPT_ARGS.skip_az_ops:
rg = rm_client.resource_groups.create_or_update(
DEPLOY_PARAMS['resource-group'],
{'location': SCRIPT_ARGS.location}
def create_resource_group(self):
"""Creates the Azure resource group for this deployment."""
self._debug('> Creating resource group: ' + self.resource_group)
return self.rm_client.resource_groups.create_or_update(
self.resource_group,
{'location': self.location}
)
_debug('Resource Group = {}'.format(rg))
RESOURCE_GROUP_CREATED = True
def deploy_template(rm_client):
"""Deploys the Avere vFXT template."""
print('> Deploying template')
def delete_resource_group(self):
"""Deletes the Azure resource group for this deployment."""
self._debug('> Deleting resource group: ' + self.resource_group)
return self.rm_client.resource_groups.delete(self.resource_group)
# Prepare parameters.
parameters = {k: {'value': v} for k, v in DEPLOY_PARAMS['parameters'].items()}
def deploy(self):
"""Deploys the Avere vFXT template."""
self._debug('> Deploying template')
if not SCRIPT_ARGS.skip_az_ops:
op = rm_client.deployments.create_or_update(
resource_group_name=DEPLOY_PARAMS['resource-group'],
deploy_secrets = {
'adminPassword': os.environ['AVERE_ADMIN_PW'],
'controllerPassword': os.environ['AVERE_CONTROLLER_PW'],
'servicePrincipalAppId': os.environ['AZURE_CLIENT_ID'],
'servicePrincipalPassword': os.environ['AZURE_CLIENT_SECRET'],
'servicePrincipalTenant': os.environ['AZURE_TENANT_ID']
}
params = {**self.deploy_params, **deploy_secrets}
return self.rm_client.deployments.create_or_update(
resource_group_name=self.resource_group,
deployment_name='azuredeploy-auto',
properties={
'mode': DeploymentMode.incremental,
'template': _load_template(),
'parameters': parameters
'parameters': {k: {'value': v} for k, v in params.items()},
'template': requests.get(self._template_url).json()
}
)
_wait_for_op(op)
def delete_resource_group(rm_client):
"""Deletes the resource group."""
print('> Deleting resource group: ' + DEPLOY_PARAMS['resource-group'])
if not SCRIPT_ARGS.skip_az_ops:
op = rm_client.resource_groups.delete(DEPLOY_PARAMS['resource-group'])
_wait_for_op(op)
def _debug(self, s):
"""Prints the passed string, with a DEBUG header, if debug is on."""
if self.debug:
print('[DEBUG]: {}'.format(s))
def cleanup(rm_client):
"""
Performs multiple cleanup activities.
1. Deletes the downloaded template file.
2. Deletes the resource group.
"""
print('> Cleaning up')
if os.path.isfile(TEMPLATE_LOCAL_FILE):
os.remove(TEMPLATE_LOCAL_FILE)
if not SCRIPT_ARGS.skip_rg_cleanup and RESOURCE_GROUP_CREATED:
delete_resource_group(rm_client)
# HELPER FUNCTIONS ############################################################
def _load_template():
"""Downloads the Avere vFXT deployment template."""
_debug('Downloading template: ' + TEMPLATE_URL)
urlretrieve(TEMPLATE_URL, filename=TEMPLATE_LOCAL_FILE)
with open(TEMPLATE_LOCAL_FILE, 'r') as template_file_fd:
template = json.load(template_file_fd)
return template
def _wait_for_op(op, timeout_sec=60):
"""
Wait for a long-running operation (op) for timeout_sec seconds.
op is an AzureOperationPoller object.
"""
time_start = time.time()
while not op.done():
op.wait(timeout=timeout_sec)
print('>> operation status: {0} ({1} sec)'.format(
op.status(), int(time.time() - time_start)))
result = op.result()
if result:
print('>> operation result: {}'.format(result))
def _debug(s):
"""Prints the passed string, with a DEBUG header, if debug is on."""
if SCRIPT_ARGS.debug:
print('[DEBUG]: {}'.format(s))
# MAIN ########################################################################
def main():
"""Main script driver."""
rm_client = load_credentials()
load_params()
retcode = 0 # PASS
try:
create_resource_group(rm_client)
deploy_template(rm_client)
except Exception as ex:
print('\n' + ('><' * 40))
print('> TEST FAILED')
print('> EXCEPTION TEXT: {}'.format(ex))
print(('><' * 40) + '\n')
retcode = 1 # FAIL
raise
except:
retcode = 2 # FAIL
raise
finally:
cleanup(rm_client)
print('> SCRIPT COMPLETE. Resource Group: {} (region: {})'.format(
DEPLOY_PARAMS['resource-group'], SCRIPT_ARGS.location))
print('> RESULT: ' + ('FAIL' if retcode else 'PASS'))
sys.exit(retcode)
def __str__(self):
return pformat(vars(self), indent=4)
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser(
description='Test template-based Avere vFXT deployment.')
arg_parser.add_argument('-p', '--param-file', default=None,
help='Full path to JSON params file. ' +
'Default: None (generate new params)')
arg_parser.add_argument('-l', '--location', default=None,
help='Azure location (region short name) to use for deployment. ' +
'Default: ' + DEFAULT_LOCATION)
arg_parser.add_argument('-xc', '--skip-rg-cleanup', action='store_true',
help='Do NOT delete the resource group during cleanup.')
arg_parser.add_argument('-xo', '--skip-az-ops', action='store_true',
help='Do NOT actually run any of the Azure operations.')
arg_parser.add_argument('-d', '--debug', action='store_true',
help='Turn on script debugging.')
SCRIPT_ARGS = arg_parser.parse_args()
main()
pass

Просмотреть файл

@ -1 +1,2 @@
azure.mgmt.resource
requests

Просмотреть файл

@ -0,0 +1,114 @@
#!/usr/bin/python3
"""
Driver for testing template-based deployment of the Avere vFXT product.
"""
import argparse
import json
import sys
import time
from avere_template_deploy import AvereTemplateDeploy
# HELPER FUNCTIONS ############################################################
def wait_for_op(op, timeout_sec=60):
"""
Wait for a long-running operation (op) for timeout_sec seconds.
op is an AzureOperationPoller object.
"""
time_start = time.time()
while not op.done():
op.wait(timeout=timeout_sec)
print('>> operation status: {0} ({1} sec)'.format(
op.status(), int(time.time() - time_start)))
result = op.result()
if result:
print('>> operation result: {}'.format(result))
# MAIN ########################################################################
def main(script_args):
def debug(s):
"""Prints the passed string, with a DEBUG header, if debug is on."""
if script_args.debug:
print('[DEBUG]: {}'.format(s))
"""Main script driver."""
retcode = 0 # PASS
try:
deploy_params = {}
if script_args.param_file: # Open user-specified params file.
with open(script_args.param_file) as pfile:
deploy_params = json.load(pfile)
atd = AvereTemplateDeploy(
debug=script_args.debug,
location=script_args.location,
deploy_params=deploy_params
)
debug('atd = \n{}'.format(atd))
if not script_args.param_file:
dparams = {
**atd.deploy_params,
'resourceGroup': atd.resource_group
}
with open(atd.resource_group + '.params.json', 'w') as pfile:
json.dump(dparams, pfile)
print('> Creating resource group: ' + atd.resource_group)
if not script_args.skip_az_ops:
rg = atd.create_resource_group()
debug('Resource Group = {}'.format(rg))
print('> Deploying template')
if not script_args.skip_az_ops:
wait_for_op(atd.deploy())
except Exception as ex:
print('\n' + ('><' * 40))
print('> TEST FAILED')
print('> EXCEPTION TEXT: {}'.format(ex))
print(('><' * 40) + '\n')
retcode = 1 # FAIL
raise
except:
retcode = 2 # FAIL
raise
finally:
if not script_args.skip_rg_cleanup:
print('> Deleting resource group: ' + atd.resource_group)
if not script_args.skip_az_ops:
wait_for_op(atd.delete_resource_group())
print('> SCRIPT COMPLETE. Resource Group: {} (region: {})'.format(
atd.resource_group, atd.location))
print('> RESULT: ' + ('FAIL' if retcode else 'PASS'))
sys.exit(retcode)
if __name__ == '__main__':
default_location = 'eastus2'
arg_parser = argparse.ArgumentParser(
description='Test template-based Avere vFXT deployment.')
arg_parser.add_argument('-p', '--param-file', default=None,
help='Full path to JSON params file. ' +
'Default: None (generate new params)')
arg_parser.add_argument('-l', '--location', default=default_location,
help='Azure location (region short name) to use for deployment. ' +
'Default: ' + default_location)
arg_parser.add_argument('-xc', '--skip-rg-cleanup', action='store_true',
help='Do NOT delete the resource group during cleanup.')
arg_parser.add_argument('-xo', '--skip-az-ops', action='store_true',
help='Do NOT actually run any of the Azure operations.')
arg_parser.add_argument('-d', '--debug', action='store_true',
help='Turn on script debugging.')
script_args = arg_parser.parse_args()
main(script_args)