diff --git a/.gitignore b/.gitignore index 65cd7465..437c5a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -407,3 +407,5 @@ arm-ttk/ # delete_roles.sh files util/.role_assignment_cleanup* +bicep/hub/build/ +bicep/hub/params/*.json diff --git a/bicep/anf.bicep b/bicep/anf.bicep index 8c41f37b..84495682 100644 --- a/bicep/anf.bicep +++ b/bicep/anf.bicep @@ -4,12 +4,12 @@ import {tags_t, availabilityZone_t} from './types.bicep' param name string param location string param tags tags_t -param availabilityZone availabilityZone_t[] +param availabilityZone availabilityZone_t[] = [] param resourcePostfix string = uniqueString(resourceGroup().id) param subnetId string param serviceLevel string param sizeTiB int -param defaultMountOptions string +param defaultMountOptions string = 'rw,hard,rsize=262144,wsize=262144,vers=3,tcp,_netdev,nconnect=8' param infrastructureOnly bool = false var capacity = sizeTiB * 1024 * 1024 * 1024 * 1024 diff --git a/bicep/ccw.bicep b/bicep/ccw.bicep index 10732d39..de0bcad2 100644 --- a/bicep/ccw.bicep +++ b/bicep/ccw.bicep @@ -7,7 +7,10 @@ param insidersBuild bool param branch string param projectVersion string -param pyxisProjectVersion string +param monitoringProjectVersion string +param monitoringIngestionEndpoint string +param monitoringIdentityClientId string +param hubMI string param adminUsername string @secure() @@ -25,7 +28,9 @@ param clusterInitSpecs types.cluster_init_param_t param slurmSettings types.slurmSettings_t param schedulerNode types.scheduler_t param loginNodes types.login_t -param htc types.htc_t +param d64d types.htc_t +param d16d types.htc_t +param m64 types.htc_t param hpc types.hpc_t param gpu types.hpc_t param tags types.resource_tags_t @@ -103,7 +108,7 @@ output vnet types.networkOutput_t = union( var deploy_bastion = network.?bastion ?? false module ccwBastion './bastion.bicep' = if (deploy_bastion) { name: 'ccwBastion' - scope: create_new_vnet ? az.resourceGroup() : az.resourceGroup(split(network.?existing_vnet_id, '/')[4]) + scope: az.resourceGroup() params: { location: location tags: getTags('Microsoft.Network/bastionHosts', tags) @@ -202,7 +207,7 @@ module mySQLccw './mysql.bicep' = if (create_database) { params: { location: location tags: getTags('Microsoft.DBforMySQL/flexibleServers', tags) - Name: db_name + // Name: db_name adminUser: adminUsername adminPassword: databaseAdminPassword subnetId: subnets.database.id @@ -315,25 +320,24 @@ output filerInfoFinal types.filerInfo_t = { output cyclecloudPrincipalId string = infrastructureOnly ? '' : ccwVM.outputs.principalId output managedIdentityId string = infrastructureOnly ? '' : ccwManagedIdentity.outputs.managedIdentityId - -// Automatically inject the ccw and pyxis cluster init specs +// Automatically inject the ccw and monitoring cluster init specs var ccwClusterInitSpec = { type: 'gitHubReleaseURL' gitHubReleaseURL: uri('https://github.com/Azure/cyclecloud-slurm-workspace/releases/tag/', projectVersion) spec: 'default' - target: ['login', 'scheduler', 'htc', 'hpc', 'gpu', 'dynamic'] + target: ['login', 'scheduler', 'd64d', 'd16d', 'm64', 'hpc', 'gpu', 'dynamic'] } -var pyxisClusterInitSpec = { +var monitoringClusterInitSpec = { type: 'gitHubReleaseURL' - gitHubReleaseURL: uri('https://github.com/Azure/cyclecloud-pyxis/releases/tag/', pyxisProjectVersion) + gitHubReleaseURL: uri('https://github.com/Azure/cyclecloud-monitoring/releases/tag/', monitoringProjectVersion) spec: 'default' - target: ['login', 'scheduler', 'htc', 'hpc', 'gpu', 'dynamic'] + target: ['login', 'scheduler', 'd64d', 'd16d', 'm64', 'hpc', 'gpu', 'dynamic'] } -// Projects <= 2025.02.06 have the pyxis logic embedded in the ccw cluster init spec -var requiredClusterInitSpecs = [ccwClusterInitSpec, pyxisClusterInitSpec] +// Use of azslurm 4.0 does not require pyxis +var requiredClusterInitSpecs = [ccwClusterInitSpec, monitoringClusterInitSpec] output clusterInitSpecs types.cluster_init_param_t = union(requiredClusterInitSpecs, clusterInitSpecs) @@ -344,12 +348,24 @@ output schedulerNode types.scheduler_t = schedulerNode output loginNodes types.login_t = loginNodes output partitions types.partitions_t = { - htc: union({ - sku: htc.sku - maxNodes: htc.maxNodes - osImage: htc.osImage - useSpot: htc.?useSpot ?? false - }, contains(htc,'availabilityZone') ? { availabilityZone: htc.?availabilityZone } : {}) + d64d: union({ + sku: d64d.sku + maxNodes: d64d.maxNodes + osImage: d64d.osImage + useSpot: d64d.?useSpot ?? false + }, contains(d64d,'availabilityZone') ? { availabilityZone: d64d.?availabilityZone } : {}) + d16d: union({ + sku: d16d.sku + maxNodes: d16d.maxNodes + osImage: d16d.osImage + useSpot: d16d.?useSpot ?? false + }, contains(d16d,'availabilityZone') ? { availabilityZone: d16d.?availabilityZone } : {}) + m64: union({ + sku: m64.sku + maxNodes: m64.maxNodes + osImage: m64.osImage + useSpot: m64.?useSpot ?? false + }, contains(m64,'availabilityZone') ? { availabilityZone: m64.?availabilityZone } : {}) hpc: hpc gpu: gpu } @@ -389,7 +405,7 @@ output manualInstall bool = manualInstall output acceptMarketplaceTerms bool = acceptMarketplaceTerms output ood object = union(ood, { - version: '1.0.1' + version: '1.1.0' nic: deployOOD ? oodNIC.outputs.NICId : '' managedIdentity: deployOOD ? createOODMI ? oodNewManagedIdentity.id : ood.?appManagedIdentityId : '' clientId: deployOOD ? registerOODApp ? oodApp.outputs.oodClientAppId : ood.?appId : '' @@ -402,9 +418,16 @@ output oodManualRegistration object = { fqdn: deployOOD ? oodNIC.outputs.privateIp : '' } +output monitoring object = { + ingestionEndpoint: monitoringIngestionEndpoint + identityClientId: monitoringIdentityClientId +} +output hubMI string = hubMI + output files object = { availability_zones_json: loadTextContent('./files-to-load/encoded/availability_zones.json.base64') create_cc_param_py: loadTextContent('./files-to-load/encoded/create_cc_param.py.base64') cyclecloud_install_py: loadTextContent('./files-to-load/encoded/cyclecloud_install.py.base64') initial_params_json: loadTextContent('./files-to-load/encoded/initial_params.json.base64') + slurm_txt: loadTextContent('./files-to-load/encoded/slurm.txt.base64') } diff --git a/bicep/exports.bicep b/bicep/exports.bicep index 7aad9a61..2dbfef59 100644 --- a/bicep/exports.bicep +++ b/bicep/exports.bicep @@ -5,4 +5,5 @@ var role_lookup = { 'Storage Account Contributor': resourceId('microsoft.authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') 'Storage Blob Data Contributor': resourceId('microsoft.authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') 'Storage Blob Data Reader': resourceId('microsoft.authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1') + 'Monitoring Metrics Publisher': resourceId('microsoft.authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') } diff --git a/bicep/files-to-load/create_cc_param.py b/bicep/files-to-load/create_cc_param.py index a20e827a..f07f8957 100644 --- a/bicep/files-to-load/create_cc_param.py +++ b/bicep/files-to-load/create_cc_param.py @@ -30,15 +30,13 @@ def set_slurm_params(params, dbPassword, outputs): params['SubnetId'] = '/'.join([outputs['vnet']['value']['rg'], outputs['vnet']['value']['name'], outputs['vnet']['value']['computeSubnetName']]) # Define Availability Zone - params['DefineNodesAvailabilityZone'] = any('availabilityZone' in zoneList for zoneList in [outputs['partitions']['value']['htc'], outputs['partitions']['value']['hpc'], outputs['partitions']['value']['gpu']]) - - #HTC - params['HTCMachineType'] = outputs['partitions']['value']['htc']['sku'] - params['MaxHTCExecuteNodeCount'] = int(outputs['partitions']['value']['htc']['maxNodes']) - params['HTCImageName'] = outputs['partitions']['value']['htc']['osImage'] - params['HTCUseLowPrio'] = outputs['partitions']['value']['htc']['useSpot'] - params['HTCAvailabilityZone'] = outputs['partitions']['value']['htc']['availabilityZone'] if params['DefineNodesAvailabilityZone'] and 'availabilityZone' in outputs['partitions']['value']['htc'] else None + params['DefineNodesAvailabilityZone'] = any('availabilityZone' in zoneList for zoneList in [outputs['partitions']['value']['hpc'], outputs['partitions']['value']['gpu']]) + for na in ['D64D', 'D16D', 'M64']: + params[f'{na}MachineType'] = outputs['partitions']['value'][na.lower()]['sku'] + params[f'Max{na}NodeCount'] = int(outputs['partitions']['value'][na.lower()]['maxNodes']) + params[f'{na}ImageName'] = outputs['partitions']['value'][na.lower()]['osImage'] + #HPC params['HPCMachineType'] = outputs['partitions']['value']['hpc']['sku'] params['MaxHPCExecuteNodeCount'] = int(outputs['partitions']['value']['hpc']['maxNodes']) @@ -96,6 +94,13 @@ def set_slurm_params(params, dbPassword, outputs): params['AdditionalNFSMountOptions'] = outputs['filerInfoFinal']['value']['additional']['mountOptions'] params['AdditionalNFSAddress'] = outputs['filerInfoFinal']['value']['additional']['ipAddress'] + # Monitoring + params['MonitoringEnabled'] = outputs['monitoring']["value"]['ingestionEndpoint'] != '' + params['MonitoringIngestionEndpoint'] = outputs['monitoring']['value']['ingestionEndpoint'] + params['MonitoringIdentityClientId'] = outputs['monitoring']['value']['identityClientId'] + + params['ManagedIdentity'] = outputs['hubMI']['value'] + def set_ood_params(params, outputs): slurm_params = get_json_dict('initial_params.json') @@ -119,6 +124,7 @@ def set_ood_params(params, outputs): params['ood_entra_tenant_id'] = outputs['ood']['value'].get('tenantId') params['ood_nic'] = outputs['ood']['value'].get('nic') + class ClusterInitSpec: def __init__(self, project: str, version: str, spec: str, targets: typing.List[str]): self.project = project @@ -197,7 +203,9 @@ def main(): "login": "LoginClusterInitSpecs", "gpu": "GPUClusterInitSpecs", "hpc": "HPCClusterInitSpecs", - "htc": "HTCClusterInitSpecs", + "d64d": "D64DClusterInitSpecs", + "d16d": "D16DClusterInitSpecs", + "m64": "M64ClusterInitSpecs", "scheduler": "SchedulerClusterInitSpecs", "dynamic": "DynamicClusterInitSpecs", "ood": "ClusterInitSpecs" diff --git a/bicep/files-to-load/encoded/create_cc_param.py.base64 b/bicep/files-to-load/encoded/create_cc_param.py.base64 index 8b89731e..d4dbafe7 100644 --- a/bicep/files-to-load/encoded/create_cc_param.py.base64 +++ b/bicep/files-to-load/encoded/create_cc_param.py.base64 @@ -1 +1 @@ -IyEvdXNyL2Jpbi9lbnYgcHl0aG9uDQoNCmltcG9ydCBhcmdwYXJzZQ0KaW1wb3J0IGhhc2hsaWINCmltcG9ydCBqc29uDQppbXBvcnQgb3MNCmltcG9ydCBzaHV0aWwNCmZyb20gc3VicHJvY2VzcyBpbXBvcnQgY2hlY2tfb3V0cHV0DQppbXBvcnQgc3lzDQppbXBvcnQgdHlwaW5nDQoNCg0KZGVmIGdldF9qc29uX2RpY3QoZmlsZV9uYW1lKToNCiAgICBhYnNfcGF0aCA9IG9zLnBhdGguYWJzcGF0aChmaWxlX25hbWUpDQogICAgd2l0aCBvcGVuKGFic19wYXRoKSBhcyBmcjoNCiAgICAgICAgcmV0dXJuIGpzb24ubG9hZChmcikNCg0KDQpkZWYgc2V0X3NsdXJtX3BhcmFtcyhwYXJhbXMsIGRiUGFzc3dvcmQsIG91dHB1dHMpOg0KICAgIHBhcmFtc1snUmVnaW9uJ10gPSBvdXRwdXRzWydsb2NhdGlvbiddWyd2YWx1ZSddDQogICAgaWYgb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWyd0eXBlJ10gPT0gJ25ldyc6DQogICAgICAgIHN1Ym5ldElEID0gb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWydjb21wdXRlU3VibmV0SWQnXQ0KICAgICAgICBzdWJuZXRfdG9rcyA9IHN1Ym5ldElELnNwbGl0KCIvIikNCiAgICAgICAgaWYgbGVuKHN1Ym5ldF90b2tzKSA+PSAxMToNCiAgICAgICAgICAgIHBhcmFtc1snU3VibmV0SWQnXSA9ICIvIi5qb2luKFtzdWJuZXRfdG9rc1s0XSwgc3VibmV0X3Rva3NbOF0sIHN1Ym5ldF90b2tzWzEwXV0pDQogICAgICAgIGVsc2U6DQogICAgICAgICAgICBwcmludChmIlVuZXhwZWN0ZWQgc3VibmV0IGlkIHtzdWJuZXRJRH0gLSBwYXNzaW5nIGFzIFN1Ym5ldElkIGRpcmVjdGx5IGluc3RlYWQgb2YgcmVzb3VyY2VfZ3JvdXAvdm5ldF9uYW1lL3N1Ym5ldF9uYW1lIiwgZmlsZT1zeXMuc3RkZXJyKQ0KICAgICAgICAgICAgcGFyYW1zWydTdWJuZXRJZCddID0gc3VibmV0SUQNCiAgICBlbHNlOg0KICAgICAgICBwYXJhbXNbJ1N1Ym5ldElkJ10gPSAnLycuam9pbihbb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWydyZyddLCBvdXRwdXRzWyd2bmV0J11bJ3ZhbHVlJ11bJ25hbWUnXSwgb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWydjb21wdXRlU3VibmV0TmFtZSddXSkNCiAgICAgICAgDQogICAgIyBEZWZpbmUgQXZhaWxhYmlsaXR5IFpvbmUNCiAgICBwYXJhbXNbJ0RlZmluZU5vZGVzQXZhaWxhYmlsaXR5Wm9uZSddID0gYW55KCdhdmFpbGFiaWxpdHlab25lJyBpbiB6b25lTGlzdCBmb3Igem9uZUxpc3QgaW4gW291dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHRjJ10sIG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHBjJ10sIG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnZ3B1J11dKQ0KICAgIA0KICAgICNIVEMNCiAgICBwYXJhbXNbJ0hUQ01hY2hpbmVUeXBlJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2h0YyddWydza3UnXQ0KICAgIHBhcmFtc1snTWF4SFRDRXhlY3V0ZU5vZGVDb3VudCddID0gaW50KG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHRjJ11bJ21heE5vZGVzJ10pDQogICAgcGFyYW1zWydIVENJbWFnZU5hbWUnXSA9IG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHRjJ11bJ29zSW1hZ2UnXQ0KICAgIHBhcmFtc1snSFRDVXNlTG93UHJpbyddID0gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydodGMnXVsndXNlU3BvdCddDQogICAgcGFyYW1zWydIVENBdmFpbGFiaWxpdHlab25lJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2h0YyddWydhdmFpbGFiaWxpdHlab25lJ10gaWYgcGFyYW1zWydEZWZpbmVOb2Rlc0F2YWlsYWJpbGl0eVpvbmUnXSBhbmQgJ2F2YWlsYWJpbGl0eVpvbmUnIGluIG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHRjJ10gZWxzZSBOb25lDQogICAgDQogICAgI0hQQw0KICAgIHBhcmFtc1snSFBDTWFjaGluZVR5cGUnXSA9IG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHBjJ11bJ3NrdSddDQogICAgcGFyYW1zWydNYXhIUENFeGVjdXRlTm9kZUNvdW50J10gPSBpbnQob3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydocGMnXVsnbWF4Tm9kZXMnXSkNCiAgICBwYXJhbXNbJ0hQQ0ltYWdlTmFtZSddID0gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydocGMnXVsnb3NJbWFnZSddDQogICAgcGFyYW1zWydIUENBdmFpbGFiaWxpdHlab25lJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2hwYyddWydhdmFpbGFiaWxpdHlab25lJ10gaWYgcGFyYW1zWydEZWZpbmVOb2Rlc0F2YWlsYWJpbGl0eVpvbmUnXSBhbmQgJ2F2YWlsYWJpbGl0eVpvbmUnIGluIG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHBjJ10gZWxzZSBOb25lDQoNCiAgICAjR1BVDQogICAgcGFyYW1zWydHUFVNYWNoaW5lVHlwZSddID0gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydncHUnXVsnc2t1J10NCiAgICBwYXJhbXNbJ01heEdQVUV4ZWN1dGVOb2RlQ291bnQnXSA9IGludChvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2dwdSddWydtYXhOb2RlcyddKQ0KICAgIHBhcmFtc1snR1BVSW1hZ2VOYW1lJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2dwdSddWydvc0ltYWdlJ10NCiAgICBwYXJhbXNbJ0dQVUF2YWlsYWJpbGl0eVpvbmUnXSA9IG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnZ3B1J11bJ2F2YWlsYWJpbGl0eVpvbmUnXSBpZiBwYXJhbXNbJ0RlZmluZU5vZGVzQXZhaWxhYmlsaXR5Wm9uZSddIGFuZCAnYXZhaWxhYmlsaXR5Wm9uZScgaW4gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydncHUnXSBlbHNlIE5vbmUNCg0KICAgICNzY2hlZHVsZXIgbm9kZQ0KICAgICNwYXJhbXNbJ3NsdXJtJ10gI2lzIHRoaXMgdGhlIHNsdXJtIHZlcnNpb24/Pz8gbm8sIHNvIHdoYXQgaXMgaXQ/DQogICAgcGFyYW1zWydTY2hlZHVsZXJNYWNoaW5lVHlwZSddID0gb3V0cHV0c1snc2NoZWR1bGVyTm9kZSddWyd2YWx1ZSddWydza3UnXQ0KICAgIHBhcmFtc1snU2NoZWR1bGVySW1hZ2VOYW1lJ10gPSBvdXRwdXRzWydzY2hlZHVsZXJOb2RlJ11bJ3ZhbHVlJ11bJ29zSW1hZ2UnXQ0KICAgIHBhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV92ZXJzaW9uJ10gPSBvdXRwdXRzWydzbHVybVNldHRpbmdzJ11bJ3ZhbHVlJ11bJ3ZlcnNpb24nXQ0KICAgICMgaWYgb3V0cHV0c1snc2x1cm1TZXR0aW5ncyddWyd2YWx1ZSddWydjYW5Vc2VTbHVybUhBJ106DQogICAgIyAgICAgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2hhX2VuYWJsZWQnXSA9IG91dHB1dHNbJ3NsdXJtU2V0dGluZ3MnXVsndmFsdWUnXVsnc2x1cm1IQSddDQogICAgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2FjY291bnRpbmdfZW5hYmxlZCddID0gYm9vbChvdXRwdXRzWydkYXRhYmFzZUluZm8nXVsndmFsdWUnXSkNCiAgICBpZiBwYXJhbXNbJ2NvbmZpZ3VyYXRpb25fc2x1cm1fYWNjb3VudGluZ19lbmFibGVkJ106DQogICAgICAgIHBhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV9hY2NvdW50aW5nX3VzZXInXSA9IG91dHB1dHNbJ2RhdGFiYXNlSW5mbyddWyd2YWx1ZSddWydkYXRhYmFzZVVzZXInXQ0KICAgIGlmIHBhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV9hY2NvdW50aW5nX2VuYWJsZWQnXToNCiAgICAgICAgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2FjY291bnRpbmdfcGFzc3dvcmQnXSA9IGRiUGFzc3dvcmQNCiAgICBpZiBwYXJhbXNbJ2NvbmZpZ3VyYXRpb25fc2x1cm1fYWNjb3VudGluZ19lbmFibGVkJ106DQogICAgICAgIHBhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV9hY2NvdW50aW5nX3VybCddID0gb3V0cHV0c1snZGF0YWJhc2VJbmZvJ11bJ3ZhbHVlJ11bJ3VybCddDQogICAgI3BhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV9hY2NvdW50aW5nX2NlcnRpZmljYXRlX3VybCddDQoNCiAgICAjbG9naW4gbm9kZShzKQ0KICAgIHBhcmFtc1snbG9naW5NYWNoaW5lVHlwZSddID0gKG91dHB1dHNbJ2xvZ2luTm9kZXMnXVsndmFsdWUnXVsnc2t1J10pLnN0cmlwKCkNCiAgICBwYXJhbXNbJ051bWJlckxvZ2luTm9kZXMnXSA9IGludChvdXRwdXRzWydsb2dpbk5vZGVzJ11bJ3ZhbHVlJ11bJ2luaXRpYWxOb2RlcyddKQ0KICAgIHBhcmFtc1snTG9naW5JbWFnZU5hbWUnXSA9IG91dHB1dHNbJ2xvZ2luTm9kZXMnXVsndmFsdWUnXVsnb3NJbWFnZSddDQogICAgcGFyYW1zWydFbmFibGVOb2RlSGVhbHRoQ2hlY2tzJ10gPSBvdXRwdXRzWydzbHVybVNldHRpbmdzJ11bJ3ZhbHVlJ11bJ2hlYWx0aENoZWNrRW5hYmxlZCddDQoNCiAgICAjRXhlY3V0ZSBub2RlIHRhZ3MNCiAgICBwYXJhbXNbJ05vZGVUYWdzJ10gPSBvdXRwdXRzWydub2RlQXJyYXlUYWdzJ11bJ3ZhbHVlJ10NCg0KICAgICNOZXR3b3JrIEF0dGFjaGVkIFN0b3JhZ2UNCiAgICBwYXJhbXNbJ1VzZUJ1aWx0aW5TaGFyZWQnXSA9IG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2hvbWUnXVsndHlwZSddID09ICduZnMtbmV3JyANCiAgICBpZiBwYXJhbXNbJ1VzZUJ1aWx0aW5TaGFyZWQnXToNCiAgICAgICAgcGFyYW1zWydGaWxlc3lzdGVtU2l6ZSddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnaG9tZSddWyduZnNDYXBhY2l0eUluR2InXQ0KICAgIGVsc2U6DQogICAgICAgIHBhcmFtc1snTkZTVHlwZSddID0gJ25mcycgaWYgb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnaG9tZSddWyd0eXBlJ10gaW4gWyduZnMtZXhpc3RpbmcnLCdhbmYtbmV3J10gZWxzZSAnbHVzdHJlJw0KICAgICAgICAjIFdlIG5vIGxvbmdlciBuZWVkIHRvIGhhbmRsZSB0aGVzZSBkaWZmZXJlbnRseSBiYXNlZCBvbiB0aGUgZnMgdHlwZSwgYXMgZWFjaA0KICAgICAgICAjIGZzIG1vZHVsZSdzIGNvbW1vbiBvdXRwdXRzIG1hcCB0byB0aGVzZS4NCiAgICAgICAgcGFyYW1zWydORlNTaGFyZWRFeHBvcnRQYXRoJ10gPSBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydob21lJ11bJ2V4cG9ydFBhdGgnXQ0KICAgICAgICBwYXJhbXNbJ05GU1NoYXJlZE1vdW50T3B0aW9ucyddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnaG9tZSddWydtb3VudE9wdGlvbnMnXQ0KICAgICAgICBwYXJhbXNbJ05GU0FkZHJlc3MnXSA9IG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2hvbWUnXVsnaXBBZGRyZXNzJ10NCg0KICAgIHBhcmFtc1snQWRkaXRpb25hbE5GUyddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnYWRkaXRpb25hbCddWyd0eXBlJ10gIT0gJ2Rpc2FibGVkJw0KICAgIGlmIHBhcmFtc1snQWRkaXRpb25hbE5GUyddOg0KICAgICAgICBwYXJhbXNbJ0FkZGl0aW9uYWxORlNUeXBlJ10gPSAnbmZzJyBpZiBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydhZGRpdGlvbmFsJ11bJ3R5cGUnXSBpbiBbJ25mcy1leGlzdGluZycsJ2FuZi1uZXcnXSBlbHNlICdsdXN0cmUnDQogICAgICAgIHBhcmFtc1snQWRkaXRpb25hbE5GU01vdW50UG9pbnQnXSA9IG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2FkZGl0aW9uYWwnXVsnbW91bnRQYXRoJ10NCiAgICAgICAgcGFyYW1zWydBZGRpdGlvbmFsTkZTRXhwb3J0UGF0aCddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnYWRkaXRpb25hbCddWydleHBvcnRQYXRoJ10NCiAgICAgICAgcGFyYW1zWydBZGRpdGlvbmFsTkZTTW91bnRPcHRpb25zJ10gPSBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydhZGRpdGlvbmFsJ11bJ21vdW50T3B0aW9ucyddDQogICAgICAgIHBhcmFtc1snQWRkaXRpb25hbE5GU0FkZHJlc3MnXSA9IG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2FkZGl0aW9uYWwnXVsnaXBBZGRyZXNzJ10NCg0KDQpkZWYgc2V0X29vZF9wYXJhbXMocGFyYW1zLCBvdXRwdXRzKToNCiAgICBzbHVybV9wYXJhbXMgPSBnZXRfanNvbl9kaWN0KCdpbml0aWFsX3BhcmFtcy5qc29uJykNCiAgICAjIFdlIHdhbnQgdG8gZXNzZW50aWFsbHkgaW5oZXJpdCBjZXJ0YWluIHNldHRpbmdzIGZyb20gdGhlIHNsdXJtIGNsdXN0ZXIuDQogICAgc2V0X3NsdXJtX3BhcmFtcyhzbHVybV9wYXJhbXMsICIiLCBvdXRwdXRzKQ0KICAgIHBhcmFtc1snTkZTQWRkcmVzcyddID0gc2x1cm1fcGFyYW1zLmdldCgnTkZTQWRkcmVzcycpIG9yICdjY3ctc2NoZWR1bGVyJw0KICAgIHBhcmFtc1snTkZTU2hhcmVkRXhwb3J0UGF0aCddID0gc2x1cm1fcGFyYW1zLmdldCgnTkZTU2hhcmVkRXhwb3J0UGF0aCcpIG9yICcvc2hhcmVkJw0KICAgIHBhcmFtc1snTkZTU2hhcmVkTW91bnRPcHRpb25zJ10gPSBzbHVybV9wYXJhbXMuZ2V0KCdORlNTaGFyZWRNb3VudE9wdGlvbnMnKQ0KICAgIHBhcmFtc1snU3VibmV0SWQnXSA9IHNsdXJtX3BhcmFtc1siU3VibmV0SWQiXQ0KICAgIHBhcmFtc1snUmVnaW9uJ10gPSBzbHVybV9wYXJhbXNbJ1JlZ2lvbiddDQogICAgcGFyYW1zWydDcmVkZW50aWFscyddID0gc2x1cm1fcGFyYW1zWydDcmVkZW50aWFscyddDQoNCiAgICBwYXJhbXNbJ01hY2hpbmVUeXBlJ10gPSBvdXRwdXRzWydvb2QnXVsndmFsdWUnXS5nZXQoJ3NrdScpDQogICAgcGFyYW1zWydNYW5hZ2VkSWRlbnRpdHknXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgnbWFuYWdlZElkZW50aXR5JykNCiAgICBwYXJhbXNbJ0Jvb3REaXNrU2l6ZSddID0gb3V0cHV0c1snb29kJ11bJ3ZhbHVlJ10uZ2V0KCdCb290RGlza1NpemUnKQ0KICAgIHBhcmFtc1snSW1hZ2VOYW1lJ10gPSBvdXRwdXRzWydvb2QnXVsndmFsdWUnXS5nZXQoJ29zSW1hZ2UnKQ0KDQogICAgcGFyYW1zWydvb2Rfc2VydmVyX25hbWUnXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgnZnFkbicsJycpDQogICAgcGFyYW1zWydvb2RfZW50cmFfdXNlcl9tYXBfbWF0Y2gnXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgndXNlckRvbWFpbicpDQogICAgcGFyYW1zWydvb2RfZW50cmFfY2xpZW50X2lkJ10gPSBvdXRwdXRzWydvb2QnXVsndmFsdWUnXS5nZXQoJ2NsaWVudElkJykNCiAgICBwYXJhbXNbJ29vZF9lbnRyYV90ZW5hbnRfaWQnXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgndGVuYW50SWQnKQ0KICAgIHBhcmFtc1snb29kX25pYyddID0gb3V0cHV0c1snb29kJ11bJ3ZhbHVlJ10uZ2V0KCduaWMnKQ0KDQpjbGFzcyBDbHVzdGVySW5pdFNwZWM6DQogICAgZGVmIF9faW5pdF9fKHNlbGYsIHByb2plY3Q6IHN0ciwgdmVyc2lvbjogc3RyLCBzcGVjOiBzdHIsIHRhcmdldHM6IHR5cGluZy5MaXN0W3N0cl0pOg0KICAgICAgICBzZWxmLnByb2plY3QgPSBwcm9qZWN0DQogICAgICAgIHNlbGYudmVyc2lvbiA9IHZlcnNpb24NCiAgICAgICAgc2VsZi5zcGVjID0gc3BlYw0KICAgICAgICBzZWxmLnRhcmdldHMgPSB0YXJnZXRzDQogICAgICAgIHNlbGYuY2x1c3Rlcl9pbml0X2tleSA9IGYie3NlbGYucHJvamVjdH06e3NlbGYuc3BlY306e3NlbGYudmVyc2lvbn0iDQoNCg0KZGVmIGRvd25sb2FkX2NsdXN0ZXJfaW5pdChvdXRwdXRzLCByb290X2ZvbGRlciwgbG9ja2VyKSAtPiB0eXBpbmcuTGlzdFtDbHVzdGVySW5pdFNwZWNdOg0KICAgIHJldCA9IFtdDQogICAgZm9yIHJlY29yZCBpbiAob3V0cHV0c1snY2x1c3RlckluaXRTcGVjcyddLmdldCgidmFsdWUiKSBvciBbXSk6DQogICAgICAgIHVybCA9IF9zdHJpcF90YWdzX2Zyb21fZ2l0aHViX3VybChyZWNvcmQpDQogICAgICAgIHVybF9oYXNoID0gaGFzaGxpYi5zaGEyNTYodXJsLmVuY29kZSgpKQ0KICAgICAgICANCiAgICAgICAgZm9sZGVyID0gb3MucGF0aC5qb2luKHJvb3RfZm9sZGVyLCB1cmxfaGFzaC5oZXhkaWdlc3QoKSkNCiAgICAgICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKGZvbGRlcik6DQogICAgICAgICAgICAjIGRvd25sb2FkIGFuZCBtb3ZlIHRvIGF2b2lkIHJlcGVhdGVkIGZhaWx1cmVzIHdpdGggcGFydGlhbCBkb3dubG9hZHMvdXBsb2Fkcw0KICAgICAgICAgICAgY2hlY2tfb3V0cHV0KFsiL3Vzci9sb2NhbC9iaW4vY3ljbGVjbG91ZCIsICJwcm9qZWN0IiwgImZldGNoIiwgdXJsLCBmb2xkZXIgKyAiLnRtcCJdKQ0KICAgICAgICAgICAgY2hlY2tfb3V0cHV0KFsiL3Vzci9sb2NhbC9iaW4vY3ljbGVjbG91ZCIsICJwcm9qZWN0IiwgInVwbG9hZCIsIGxvY2tlcl0sIGN3ZD1mb2xkZXIgKyAiLnRtcCIpDQogICAgICAgICAgICBzaHV0aWwubW92ZShmb2xkZXIgKyAiLnRtcCIsIGZvbGRlcikNCiAgICAgICAgICAgIHdpdGggb3Blbihvcy5wYXRoLmpvaW4oZm9sZGVyLCAiZG93bmxvYWQtdXJsIiksICJ3IikgYXMgZnc6DQogICAgICAgICAgICAgICAgZncud3JpdGUodXJsKQ0KICAgICAgICBwcm9qX2luZm9fcmF3ID0gY2hlY2tfb3V0cHV0KFsiL3Vzci9sb2NhbC9iaW4vY3ljbGVjbG91ZCIsICJwcm9qZWN0IiwgImluZm8iXSwgY3dkPWZvbGRlcikuZGVjb2RlKCkNCiAgICAgICAgcHJval9pbmZvID0ge30NCiAgICAgICAgZm9yIGxpbmUgaW4gcHJval9pbmZvX3Jhdy5zcGxpdGxpbmVzKCk6DQogICAgICAgICAgICBrZXksIHJlc3QgPSBsaW5lLnNwbGl0KCI6IiwgMSkNCiAgICAgICAgICAgIHByb2pfaW5mb1trZXkubG93ZXIoKV0gPSByZXN0LnN0cmlwKCkNCiAgICAgICAgcmV0LmFwcGVuZChDbHVzdGVySW5pdFNwZWMocHJval9pbmZvWyJuYW1lIl0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2pfaW5mb1sidmVyc2lvbiJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvcmQuZ2V0KCJzcGVjIikgb3IgImRlZmF1bHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvcmRbInRhcmdldCJdKSkNCiAgICByZXR1cm4gcmV0DQoNCg0KZGVmIF9zdHJpcF90YWdzX2Zyb21fZ2l0aHViX3VybChyZWNvcmQpOg0KICAgIHVybCA9IHJlY29yZFsiZ2l0SHViUmVsZWFzZVVSTCJdDQogICAgaWYgIi90YWcvIiBpbiB1cmw6DQogICAgICAgIHJldHVybiB1cmwucmVwbGFjZSgiL3RhZyIsICIiKQ0KICAgIHJldHVybiB1cmwNCg0KDQpkZWYgX3ZlcnNpb25fZnJvbV91cmwocmVjb3JkKToNCiAgICBpZiByZWNvcmQuZ2V0KCJ2ZXJzaW9uIik6DQogICAgICAgIHJldHVybiByZWNvcmRbInZlcnNpb24iXQ0KICAgIHJldHVybiByZWNvcmRbImdpdEh1YlJlbGVhc2VVUkwiXS5zcGxpdCgiLyIpWy0xXQ0KDQoNCmRlZiBzZXRfY2x1c3Rlcl9pbml0X3BhcmFtcyhwYXJhbXM6IGRpY3QsIHNwZWNzOiB0eXBpbmcuTGlzdFtDbHVzdGVySW5pdFNwZWNdLCBjbHVzdGVyX25hbWU6IHN0ciwgdGFyZ2V0X3BhcmFtczogZGljdCkgLT4gTm9uZToNCiAgICBvcmRlciA9IDEwMDAwDQogICAgZm9yIHNwZWMgaW4gc3BlY3M6DQogICAgICAgIGZvciB0YXJnZXQgaW4gc3BlYy50YXJnZXRzOg0KICAgICAgICAgICAgdGFyZ2V0X2tleSA9IGYie3RhcmdldF9wYXJhbXNbdGFyZ2V0Lmxvd2VyKCldfSINCiAgICAgICAgICAgIGlmIG5vdCBwYXJhbXMuZ2V0KHRhcmdldF9rZXkpOg0KICAgICAgICAgICAgICAgIHBhcmFtc1t0YXJnZXRfa2V5XSA9IHt9DQoNCiAgICAgICAgICAgIHBhcmFtc1t0YXJnZXRfa2V5XVtzcGVjLmNsdXN0ZXJfaW5pdF9rZXldID0gew0KICAgICAgICAgICAgICAgICJPcmRlciI6IG9yZGVyLA0KICAgICAgICAgICAgICAgICJTcGVjIjogc3BlYy5zcGVjLA0KICAgICAgICAgICAgICAgICJOYW1lIjogc3BlYy5jbHVzdGVyX2luaXRfa2V5LA0KICAgICAgICAgICAgICAgICJQcm9qZWN0Ijogc3BlYy5wcm9qZWN0LA0KICAgICAgICAgICAgICAgICJMb2NrZXIiOiAiYXp1cmUtc3RvcmFnZSIsDQogICAgICAgICAgICAgICAgIlZlcnNpb24iOiBzcGVjLnZlcnNpb24NCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIG9yZGVyICs9IDEwMA0KDQoNCmRlZiBtYWluKCk6DQogICAgcGFyc2VyID0gYXJncGFyc2UuQXJndW1lbnRQYXJzZXIoZGVzY3JpcHRpb249IlRPRE8gUkRIIikNCiAgICBwYXJzZXIuYWRkX2FyZ3VtZW50KCItLWxvY2tlciIsIGRlZmF1bHQ9ImF6dXJlLXN0b3JhZ2UiKQ0KICAgIHBhcnNlci5hZGRfYXJndW1lbnQoIi0tY2x1c3Rlci1pbml0LXdvcmtpbmctZGlyIiwgZGVmYXVsdD0iY2x1c3Rlci1pbml0IikNCiAgICBzdWJwYXJzZXJzID0gcGFyc2VyLmFkZF9zdWJwYXJzZXJzKCkNCiAgICBjY3dfcGFyc2VyID0gc3VicGFyc2Vycy5hZGRfcGFyc2VyKCJzbHVybSIpDQogICAgIyBUT0RPIHRoaXMgbmVlZHMgdG8gYmUgYnkgY2x1c3RlciB0eXBlDQogICAgdGFyZ2V0X3BhcmFtcyA9IHsNCiAgICAgICAgImxvZ2luIjogIkxvZ2luQ2x1c3RlckluaXRTcGVjcyIsDQogICAgICAgICJncHUiOiAiR1BVQ2x1c3RlckluaXRTcGVjcyIsDQogICAgICAgICJocGMiOiAiSFBDQ2x1c3RlckluaXRTcGVjcyIsDQogICAgICAgICJodGMiOiAiSFRDQ2x1c3RlckluaXRTcGVjcyIsDQogICAgICAgICJzY2hlZHVsZXIiOiAiU2NoZWR1bGVyQ2x1c3RlckluaXRTcGVjcyIsDQogICAgICAgICJkeW5hbWljIjogIkR5bmFtaWNDbHVzdGVySW5pdFNwZWNzIiwNCiAgICAgICAgIm9vZCI6ICJDbHVzdGVySW5pdFNwZWNzIg0KICAgIH0NCiAgICBjY3dfcGFyc2VyLnNldF9kZWZhdWx0cyhjbHVzdGVyX3R5cGU9InNsdXJtIiwgdGFyZ2V0X3BhcmFtcz10YXJnZXRfcGFyYW1zKQ0KICAgIGNjd19wYXJzZXIuYWRkX2FyZ3VtZW50KCItLWRiUGFzc3dvcmQiLCBkZXN0PSJkYlBhc3N3b3JkIiwgZGVmYXVsdD0iIiwgaGVscD0iTXlTUUwgZGF0YWJhc2UgcGFzc3dvcmQiKQ0KICAgIA0KICAgIG9vZF9wYXJzZXIgPSBzdWJwYXJzZXJzLmFkZF9wYXJzZXIoIm9vZCIpDQogICAgb29kX3BhcnNlci5zZXRfZGVmYXVsdHMoY2x1c3Rlcl90eXBlPSJvb2QiLCB0YXJnZXRfcGFyYW1zPXRhcmdldF9wYXJhbXMpDQogICAgDQogICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKCkNCg0KICAgIGlmIGFyZ3MuY2x1c3Rlcl90eXBlID09ICJzbHVybSI6DQogICAgICAgIG91dHB1dF9wYXJhbXMgPSBnZXRfanNvbl9kaWN0KCdpbml0aWFsX3BhcmFtcy5qc29uJykNCiAgICBlbHNlOg0KICAgICAgICBvdXRwdXRfcGFyYW1zID0ge30NCiAgICBjY3dfb3V0cHV0cyA9IGdldF9qc29uX2RpY3QoJ2Njd091dHB1dHMuanNvbicpDQoNCiAgICBzcGVjcyA9IGRvd25sb2FkX2NsdXN0ZXJfaW5pdChjY3dfb3V0cHV0cywgb3MucGF0aC5qb2luKG9zLmdldGN3ZCgpLCBhcmdzLmNsdXN0ZXJfaW5pdF93b3JraW5nX2RpciksIGFyZ3MubG9ja2VyKQ0KICAgIHNldF9jbHVzdGVyX2luaXRfcGFyYW1zKG91dHB1dF9wYXJhbXMsIHNwZWNzLCBhcmdzLmNsdXN0ZXJfdHlwZSwgYXJncy50YXJnZXRfcGFyYW1zKQ0KICAgIGlmIGFyZ3MuY2x1c3Rlcl90eXBlID09ICJzbHVybSI6DQogICAgICAgIHNldF9zbHVybV9wYXJhbXMob3V0cHV0X3BhcmFtcywgYXJncy5kYlBhc3N3b3JkLCBjY3dfb3V0cHV0cykNCiAgICBlbHNlOg0KICAgICAgICBzZXRfb29kX3BhcmFtcyhvdXRwdXRfcGFyYW1zLCBjY3dfb3V0cHV0cykNCiAgICBwcmludChqc29uLmR1bXBzKG91dHB1dF9wYXJhbXMsIGluZGVudD00KSkNCg0KDQppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOg0KICAgIG1haW4oKQ== \ No newline at end of file +IyEvdXNyL2Jpbi9lbnYgcHl0aG9uDQoNCmltcG9ydCBhcmdwYXJzZQ0KaW1wb3J0IGhhc2hsaWINCmltcG9ydCBqc29uDQppbXBvcnQgb3MNCmltcG9ydCBzaHV0aWwNCmZyb20gc3VicHJvY2VzcyBpbXBvcnQgY2hlY2tfb3V0cHV0DQppbXBvcnQgc3lzDQppbXBvcnQgdHlwaW5nDQoNCg0KZGVmIGdldF9qc29uX2RpY3QoZmlsZV9uYW1lKToNCiAgICBhYnNfcGF0aCA9IG9zLnBhdGguYWJzcGF0aChmaWxlX25hbWUpDQogICAgd2l0aCBvcGVuKGFic19wYXRoKSBhcyBmcjoNCiAgICAgICAgcmV0dXJuIGpzb24ubG9hZChmcikNCg0KDQpkZWYgc2V0X3NsdXJtX3BhcmFtcyhwYXJhbXMsIGRiUGFzc3dvcmQsIG91dHB1dHMpOg0KICAgIHBhcmFtc1snUmVnaW9uJ10gPSBvdXRwdXRzWydsb2NhdGlvbiddWyd2YWx1ZSddDQogICAgaWYgb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWyd0eXBlJ10gPT0gJ25ldyc6DQogICAgICAgIHN1Ym5ldElEID0gb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWydjb21wdXRlU3VibmV0SWQnXQ0KICAgICAgICBzdWJuZXRfdG9rcyA9IHN1Ym5ldElELnNwbGl0KCIvIikNCiAgICAgICAgaWYgbGVuKHN1Ym5ldF90b2tzKSA+PSAxMToNCiAgICAgICAgICAgIHBhcmFtc1snU3VibmV0SWQnXSA9ICIvIi5qb2luKFtzdWJuZXRfdG9rc1s0XSwgc3VibmV0X3Rva3NbOF0sIHN1Ym5ldF90b2tzWzEwXV0pDQogICAgICAgIGVsc2U6DQogICAgICAgICAgICBwcmludChmIlVuZXhwZWN0ZWQgc3VibmV0IGlkIHtzdWJuZXRJRH0gLSBwYXNzaW5nIGFzIFN1Ym5ldElkIGRpcmVjdGx5IGluc3RlYWQgb2YgcmVzb3VyY2VfZ3JvdXAvdm5ldF9uYW1lL3N1Ym5ldF9uYW1lIiwgZmlsZT1zeXMuc3RkZXJyKQ0KICAgICAgICAgICAgcGFyYW1zWydTdWJuZXRJZCddID0gc3VibmV0SUQNCiAgICBlbHNlOg0KICAgICAgICBwYXJhbXNbJ1N1Ym5ldElkJ10gPSAnLycuam9pbihbb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWydyZyddLCBvdXRwdXRzWyd2bmV0J11bJ3ZhbHVlJ11bJ25hbWUnXSwgb3V0cHV0c1sndm5ldCddWyd2YWx1ZSddWydjb21wdXRlU3VibmV0TmFtZSddXSkNCiAgICAgICAgDQogICAgIyBEZWZpbmUgQXZhaWxhYmlsaXR5IFpvbmUNCiAgICBwYXJhbXNbJ0RlZmluZU5vZGVzQXZhaWxhYmlsaXR5Wm9uZSddID0gYW55KCdhdmFpbGFiaWxpdHlab25lJyBpbiB6b25lTGlzdCBmb3Igem9uZUxpc3QgaW4gW291dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHBjJ10sIG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnZ3B1J11dKQ0KICAgIA0KICAgIGZvciBuYSBpbiBbJ0Q2NEQnLCAnRDE2RCcsICdNNjQnXToNCiAgICAgICAgcGFyYW1zW2Yne25hfU1hY2hpbmVUeXBlJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bbmEubG93ZXIoKV1bJ3NrdSddDQogICAgICAgIHBhcmFtc1tmJ01heHtuYX1Ob2RlQ291bnQnXSA9IGludChvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bbmEubG93ZXIoKV1bJ21heE5vZGVzJ10pDQogICAgICAgIHBhcmFtc1tmJ3tuYX1JbWFnZU5hbWUnXSA9IG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVtuYS5sb3dlcigpXVsnb3NJbWFnZSddDQoNCiAgICAjSFBDDQogICAgcGFyYW1zWydIUENNYWNoaW5lVHlwZSddID0gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydocGMnXVsnc2t1J10NCiAgICBwYXJhbXNbJ01heEhQQ0V4ZWN1dGVOb2RlQ291bnQnXSA9IGludChvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2hwYyddWydtYXhOb2RlcyddKQ0KICAgIHBhcmFtc1snSFBDSW1hZ2VOYW1lJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2hwYyddWydvc0ltYWdlJ10NCiAgICBwYXJhbXNbJ0hQQ0F2YWlsYWJpbGl0eVpvbmUnXSA9IG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnaHBjJ11bJ2F2YWlsYWJpbGl0eVpvbmUnXSBpZiBwYXJhbXNbJ0RlZmluZU5vZGVzQXZhaWxhYmlsaXR5Wm9uZSddIGFuZCAnYXZhaWxhYmlsaXR5Wm9uZScgaW4gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydocGMnXSBlbHNlIE5vbmUNCg0KICAgICNHUFUNCiAgICBwYXJhbXNbJ0dQVU1hY2hpbmVUeXBlJ10gPSBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2dwdSddWydza3UnXQ0KICAgIHBhcmFtc1snTWF4R1BVRXhlY3V0ZU5vZGVDb3VudCddID0gaW50KG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnZ3B1J11bJ21heE5vZGVzJ10pDQogICAgcGFyYW1zWydHUFVJbWFnZU5hbWUnXSA9IG91dHB1dHNbJ3BhcnRpdGlvbnMnXVsndmFsdWUnXVsnZ3B1J11bJ29zSW1hZ2UnXQ0KICAgIHBhcmFtc1snR1BVQXZhaWxhYmlsaXR5Wm9uZSddID0gb3V0cHV0c1sncGFydGl0aW9ucyddWyd2YWx1ZSddWydncHUnXVsnYXZhaWxhYmlsaXR5Wm9uZSddIGlmIHBhcmFtc1snRGVmaW5lTm9kZXNBdmFpbGFiaWxpdHlab25lJ10gYW5kICdhdmFpbGFiaWxpdHlab25lJyBpbiBvdXRwdXRzWydwYXJ0aXRpb25zJ11bJ3ZhbHVlJ11bJ2dwdSddIGVsc2UgTm9uZQ0KDQogICAgI3NjaGVkdWxlciBub2RlDQogICAgI3BhcmFtc1snc2x1cm0nXSAjaXMgdGhpcyB0aGUgc2x1cm0gdmVyc2lvbj8/PyBubywgc28gd2hhdCBpcyBpdD8NCiAgICBwYXJhbXNbJ1NjaGVkdWxlck1hY2hpbmVUeXBlJ10gPSBvdXRwdXRzWydzY2hlZHVsZXJOb2RlJ11bJ3ZhbHVlJ11bJ3NrdSddDQogICAgcGFyYW1zWydTY2hlZHVsZXJJbWFnZU5hbWUnXSA9IG91dHB1dHNbJ3NjaGVkdWxlck5vZGUnXVsndmFsdWUnXVsnb3NJbWFnZSddDQogICAgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX3ZlcnNpb24nXSA9IG91dHB1dHNbJ3NsdXJtU2V0dGluZ3MnXVsndmFsdWUnXVsndmVyc2lvbiddDQogICAgIyBpZiBvdXRwdXRzWydzbHVybVNldHRpbmdzJ11bJ3ZhbHVlJ11bJ2NhblVzZVNsdXJtSEEnXToNCiAgICAjICAgICBwYXJhbXNbJ2NvbmZpZ3VyYXRpb25fc2x1cm1faGFfZW5hYmxlZCddID0gb3V0cHV0c1snc2x1cm1TZXR0aW5ncyddWyd2YWx1ZSddWydzbHVybUhBJ10NCiAgICBwYXJhbXNbJ2NvbmZpZ3VyYXRpb25fc2x1cm1fYWNjb3VudGluZ19lbmFibGVkJ10gPSBib29sKG91dHB1dHNbJ2RhdGFiYXNlSW5mbyddWyd2YWx1ZSddKQ0KICAgIGlmIHBhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV9hY2NvdW50aW5nX2VuYWJsZWQnXToNCiAgICAgICAgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2FjY291bnRpbmdfdXNlciddID0gb3V0cHV0c1snZGF0YWJhc2VJbmZvJ11bJ3ZhbHVlJ11bJ2RhdGFiYXNlVXNlciddDQogICAgaWYgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2FjY291bnRpbmdfZW5hYmxlZCddOg0KICAgICAgICBwYXJhbXNbJ2NvbmZpZ3VyYXRpb25fc2x1cm1fYWNjb3VudGluZ19wYXNzd29yZCddID0gZGJQYXNzd29yZA0KICAgIGlmIHBhcmFtc1snY29uZmlndXJhdGlvbl9zbHVybV9hY2NvdW50aW5nX2VuYWJsZWQnXToNCiAgICAgICAgcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2FjY291bnRpbmdfdXJsJ10gPSBvdXRwdXRzWydkYXRhYmFzZUluZm8nXVsndmFsdWUnXVsndXJsJ10NCiAgICAjcGFyYW1zWydjb25maWd1cmF0aW9uX3NsdXJtX2FjY291bnRpbmdfY2VydGlmaWNhdGVfdXJsJ10NCg0KICAgICNsb2dpbiBub2RlKHMpDQogICAgcGFyYW1zWydsb2dpbk1hY2hpbmVUeXBlJ10gPSAob3V0cHV0c1snbG9naW5Ob2RlcyddWyd2YWx1ZSddWydza3UnXSkuc3RyaXAoKQ0KICAgIHBhcmFtc1snTnVtYmVyTG9naW5Ob2RlcyddID0gaW50KG91dHB1dHNbJ2xvZ2luTm9kZXMnXVsndmFsdWUnXVsnaW5pdGlhbE5vZGVzJ10pDQogICAgcGFyYW1zWydMb2dpbkltYWdlTmFtZSddID0gb3V0cHV0c1snbG9naW5Ob2RlcyddWyd2YWx1ZSddWydvc0ltYWdlJ10NCiAgICBwYXJhbXNbJ0VuYWJsZU5vZGVIZWFsdGhDaGVja3MnXSA9IG91dHB1dHNbJ3NsdXJtU2V0dGluZ3MnXVsndmFsdWUnXVsnaGVhbHRoQ2hlY2tFbmFibGVkJ10NCg0KICAgICNFeGVjdXRlIG5vZGUgdGFncw0KICAgIHBhcmFtc1snTm9kZVRhZ3MnXSA9IG91dHB1dHNbJ25vZGVBcnJheVRhZ3MnXVsndmFsdWUnXQ0KDQogICAgI05ldHdvcmsgQXR0YWNoZWQgU3RvcmFnZQ0KICAgIHBhcmFtc1snVXNlQnVpbHRpblNoYXJlZCddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnaG9tZSddWyd0eXBlJ10gPT0gJ25mcy1uZXcnIA0KICAgIGlmIHBhcmFtc1snVXNlQnVpbHRpblNoYXJlZCddOg0KICAgICAgICBwYXJhbXNbJ0ZpbGVzeXN0ZW1TaXplJ10gPSBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydob21lJ11bJ25mc0NhcGFjaXR5SW5HYiddDQogICAgZWxzZToNCiAgICAgICAgcGFyYW1zWydORlNUeXBlJ10gPSAnbmZzJyBpZiBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydob21lJ11bJ3R5cGUnXSBpbiBbJ25mcy1leGlzdGluZycsJ2FuZi1uZXcnXSBlbHNlICdsdXN0cmUnDQogICAgICAgICMgV2Ugbm8gbG9uZ2VyIG5lZWQgdG8gaGFuZGxlIHRoZXNlIGRpZmZlcmVudGx5IGJhc2VkIG9uIHRoZSBmcyB0eXBlLCBhcyBlYWNoDQogICAgICAgICMgZnMgbW9kdWxlJ3MgY29tbW9uIG91dHB1dHMgbWFwIHRvIHRoZXNlLg0KICAgICAgICBwYXJhbXNbJ05GU1NoYXJlZEV4cG9ydFBhdGgnXSA9IG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2hvbWUnXVsnZXhwb3J0UGF0aCddDQogICAgICAgIHBhcmFtc1snTkZTU2hhcmVkTW91bnRPcHRpb25zJ10gPSBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydob21lJ11bJ21vdW50T3B0aW9ucyddDQogICAgICAgIHBhcmFtc1snTkZTQWRkcmVzcyddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnaG9tZSddWydpcEFkZHJlc3MnXQ0KDQogICAgcGFyYW1zWydBZGRpdGlvbmFsTkZTJ10gPSBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydhZGRpdGlvbmFsJ11bJ3R5cGUnXSAhPSAnZGlzYWJsZWQnDQogICAgaWYgcGFyYW1zWydBZGRpdGlvbmFsTkZTJ106DQogICAgICAgIHBhcmFtc1snQWRkaXRpb25hbE5GU1R5cGUnXSA9ICduZnMnIGlmIG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2FkZGl0aW9uYWwnXVsndHlwZSddIGluIFsnbmZzLWV4aXN0aW5nJywnYW5mLW5ldyddIGVsc2UgJ2x1c3RyZScNCiAgICAgICAgcGFyYW1zWydBZGRpdGlvbmFsTkZTTW91bnRQb2ludCddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnYWRkaXRpb25hbCddWydtb3VudFBhdGgnXQ0KICAgICAgICBwYXJhbXNbJ0FkZGl0aW9uYWxORlNFeHBvcnRQYXRoJ10gPSBvdXRwdXRzWydmaWxlckluZm9GaW5hbCddWyd2YWx1ZSddWydhZGRpdGlvbmFsJ11bJ2V4cG9ydFBhdGgnXQ0KICAgICAgICBwYXJhbXNbJ0FkZGl0aW9uYWxORlNNb3VudE9wdGlvbnMnXSA9IG91dHB1dHNbJ2ZpbGVySW5mb0ZpbmFsJ11bJ3ZhbHVlJ11bJ2FkZGl0aW9uYWwnXVsnbW91bnRPcHRpb25zJ10NCiAgICAgICAgcGFyYW1zWydBZGRpdGlvbmFsTkZTQWRkcmVzcyddID0gb3V0cHV0c1snZmlsZXJJbmZvRmluYWwnXVsndmFsdWUnXVsnYWRkaXRpb25hbCddWydpcEFkZHJlc3MnXQ0KDQogICAgIyBNb25pdG9yaW5nDQogICAgcGFyYW1zWydNb25pdG9yaW5nRW5hYmxlZCddID0gb3V0cHV0c1snbW9uaXRvcmluZyddWyJ2YWx1ZSJdWydpbmdlc3Rpb25FbmRwb2ludCddICE9ICcnDQogICAgcGFyYW1zWydNb25pdG9yaW5nSW5nZXN0aW9uRW5kcG9pbnQnXSA9IG91dHB1dHNbJ21vbml0b3JpbmcnXVsndmFsdWUnXVsnaW5nZXN0aW9uRW5kcG9pbnQnXQ0KICAgIHBhcmFtc1snTW9uaXRvcmluZ0lkZW50aXR5Q2xpZW50SWQnXSA9IG91dHB1dHNbJ21vbml0b3JpbmcnXVsndmFsdWUnXVsnaWRlbnRpdHlDbGllbnRJZCddDQoNCiAgICBwYXJhbXNbJ01hbmFnZWRJZGVudGl0eSddID0gb3V0cHV0c1snaHViTUknXVsndmFsdWUnXQ0KDQoNCmRlZiBzZXRfb29kX3BhcmFtcyhwYXJhbXMsIG91dHB1dHMpOg0KICAgIHNsdXJtX3BhcmFtcyA9IGdldF9qc29uX2RpY3QoJ2luaXRpYWxfcGFyYW1zLmpzb24nKQ0KICAgICMgV2Ugd2FudCB0byBlc3NlbnRpYWxseSBpbmhlcml0IGNlcnRhaW4gc2V0dGluZ3MgZnJvbSB0aGUgc2x1cm0gY2x1c3Rlci4NCiAgICBzZXRfc2x1cm1fcGFyYW1zKHNsdXJtX3BhcmFtcywgIiIsIG91dHB1dHMpDQogICAgcGFyYW1zWydORlNBZGRyZXNzJ10gPSBzbHVybV9wYXJhbXMuZ2V0KCdORlNBZGRyZXNzJykgb3IgJ2Njdy1zY2hlZHVsZXInDQogICAgcGFyYW1zWydORlNTaGFyZWRFeHBvcnRQYXRoJ10gPSBzbHVybV9wYXJhbXMuZ2V0KCdORlNTaGFyZWRFeHBvcnRQYXRoJykgb3IgJy9zaGFyZWQnDQogICAgcGFyYW1zWydORlNTaGFyZWRNb3VudE9wdGlvbnMnXSA9IHNsdXJtX3BhcmFtcy5nZXQoJ05GU1NoYXJlZE1vdW50T3B0aW9ucycpDQogICAgcGFyYW1zWydTdWJuZXRJZCddID0gc2x1cm1fcGFyYW1zWyJTdWJuZXRJZCJdDQogICAgcGFyYW1zWydSZWdpb24nXSA9IHNsdXJtX3BhcmFtc1snUmVnaW9uJ10NCiAgICBwYXJhbXNbJ0NyZWRlbnRpYWxzJ10gPSBzbHVybV9wYXJhbXNbJ0NyZWRlbnRpYWxzJ10NCg0KICAgIHBhcmFtc1snTWFjaGluZVR5cGUnXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgnc2t1JykNCiAgICBwYXJhbXNbJ01hbmFnZWRJZGVudGl0eSddID0gb3V0cHV0c1snb29kJ11bJ3ZhbHVlJ10uZ2V0KCdtYW5hZ2VkSWRlbnRpdHknKQ0KICAgIHBhcmFtc1snQm9vdERpc2tTaXplJ10gPSBvdXRwdXRzWydvb2QnXVsndmFsdWUnXS5nZXQoJ0Jvb3REaXNrU2l6ZScpDQogICAgcGFyYW1zWydJbWFnZU5hbWUnXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgnb3NJbWFnZScpDQoNCiAgICBwYXJhbXNbJ29vZF9zZXJ2ZXJfbmFtZSddID0gb3V0cHV0c1snb29kJ11bJ3ZhbHVlJ10uZ2V0KCdmcWRuJywnJykNCiAgICBwYXJhbXNbJ29vZF9lbnRyYV91c2VyX21hcF9tYXRjaCddID0gb3V0cHV0c1snb29kJ11bJ3ZhbHVlJ10uZ2V0KCd1c2VyRG9tYWluJykNCiAgICBwYXJhbXNbJ29vZF9lbnRyYV9jbGllbnRfaWQnXSA9IG91dHB1dHNbJ29vZCddWyd2YWx1ZSddLmdldCgnY2xpZW50SWQnKQ0KICAgIHBhcmFtc1snb29kX2VudHJhX3RlbmFudF9pZCddID0gb3V0cHV0c1snb29kJ11bJ3ZhbHVlJ10uZ2V0KCd0ZW5hbnRJZCcpDQogICAgcGFyYW1zWydvb2RfbmljJ10gPSBvdXRwdXRzWydvb2QnXVsndmFsdWUnXS5nZXQoJ25pYycpDQoNCg0KY2xhc3MgQ2x1c3RlckluaXRTcGVjOg0KICAgIGRlZiBfX2luaXRfXyhzZWxmLCBwcm9qZWN0OiBzdHIsIHZlcnNpb246IHN0ciwgc3BlYzogc3RyLCB0YXJnZXRzOiB0eXBpbmcuTGlzdFtzdHJdKToNCiAgICAgICAgc2VsZi5wcm9qZWN0ID0gcHJvamVjdA0KICAgICAgICBzZWxmLnZlcnNpb24gPSB2ZXJzaW9uDQogICAgICAgIHNlbGYuc3BlYyA9IHNwZWMNCiAgICAgICAgc2VsZi50YXJnZXRzID0gdGFyZ2V0cw0KICAgICAgICBzZWxmLmNsdXN0ZXJfaW5pdF9rZXkgPSBmIntzZWxmLnByb2plY3R9OntzZWxmLnNwZWN9OntzZWxmLnZlcnNpb259Ig0KDQoNCmRlZiBkb3dubG9hZF9jbHVzdGVyX2luaXQob3V0cHV0cywgcm9vdF9mb2xkZXIsIGxvY2tlcikgLT4gdHlwaW5nLkxpc3RbQ2x1c3RlckluaXRTcGVjXToNCiAgICByZXQgPSBbXQ0KICAgIGZvciByZWNvcmQgaW4gKG91dHB1dHNbJ2NsdXN0ZXJJbml0U3BlY3MnXS5nZXQoInZhbHVlIikgb3IgW10pOg0KICAgICAgICB1cmwgPSBfc3RyaXBfdGFnc19mcm9tX2dpdGh1Yl91cmwocmVjb3JkKQ0KICAgICAgICB1cmxfaGFzaCA9IGhhc2hsaWIuc2hhMjU2KHVybC5lbmNvZGUoKSkNCiAgICAgICAgDQogICAgICAgIGZvbGRlciA9IG9zLnBhdGguam9pbihyb290X2ZvbGRlciwgdXJsX2hhc2guaGV4ZGlnZXN0KCkpDQogICAgICAgIGlmIG5vdCBvcy5wYXRoLmV4aXN0cyhmb2xkZXIpOg0KICAgICAgICAgICAgIyBkb3dubG9hZCBhbmQgbW92ZSB0byBhdm9pZCByZXBlYXRlZCBmYWlsdXJlcyB3aXRoIHBhcnRpYWwgZG93bmxvYWRzL3VwbG9hZHMNCiAgICAgICAgICAgIGNoZWNrX291dHB1dChbIi91c3IvbG9jYWwvYmluL2N5Y2xlY2xvdWQiLCAicHJvamVjdCIsICJmZXRjaCIsIHVybCwgZm9sZGVyICsgIi50bXAiXSkNCiAgICAgICAgICAgIGNoZWNrX291dHB1dChbIi91c3IvbG9jYWwvYmluL2N5Y2xlY2xvdWQiLCAicHJvamVjdCIsICJ1cGxvYWQiLCBsb2NrZXJdLCBjd2Q9Zm9sZGVyICsgIi50bXAiKQ0KICAgICAgICAgICAgc2h1dGlsLm1vdmUoZm9sZGVyICsgIi50bXAiLCBmb2xkZXIpDQogICAgICAgICAgICB3aXRoIG9wZW4ob3MucGF0aC5qb2luKGZvbGRlciwgImRvd25sb2FkLXVybCIpLCAidyIpIGFzIGZ3Og0KICAgICAgICAgICAgICAgIGZ3LndyaXRlKHVybCkNCiAgICAgICAgcHJval9pbmZvX3JhdyA9IGNoZWNrX291dHB1dChbIi91c3IvbG9jYWwvYmluL2N5Y2xlY2xvdWQiLCAicHJvamVjdCIsICJpbmZvIl0sIGN3ZD1mb2xkZXIpLmRlY29kZSgpDQogICAgICAgIHByb2pfaW5mbyA9IHt9DQogICAgICAgIGZvciBsaW5lIGluIHByb2pfaW5mb19yYXcuc3BsaXRsaW5lcygpOg0KICAgICAgICAgICAga2V5LCByZXN0ID0gbGluZS5zcGxpdCgiOiIsIDEpDQogICAgICAgICAgICBwcm9qX2luZm9ba2V5Lmxvd2VyKCldID0gcmVzdC5zdHJpcCgpDQogICAgICAgIHJldC5hcHBlbmQoQ2x1c3RlckluaXRTcGVjKHByb2pfaW5mb1sibmFtZSJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qX2luZm9bInZlcnNpb24iXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3JkLmdldCgic3BlYyIpIG9yICJkZWZhdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3JkWyJ0YXJnZXQiXSkpDQogICAgcmV0dXJuIHJldA0KDQoNCmRlZiBfc3RyaXBfdGFnc19mcm9tX2dpdGh1Yl91cmwocmVjb3JkKToNCiAgICB1cmwgPSByZWNvcmRbImdpdEh1YlJlbGVhc2VVUkwiXQ0KICAgIGlmICIvdGFnLyIgaW4gdXJsOg0KICAgICAgICByZXR1cm4gdXJsLnJlcGxhY2UoIi90YWciLCAiIikNCiAgICByZXR1cm4gdXJsDQoNCg0KZGVmIF92ZXJzaW9uX2Zyb21fdXJsKHJlY29yZCk6DQogICAgaWYgcmVjb3JkLmdldCgidmVyc2lvbiIpOg0KICAgICAgICByZXR1cm4gcmVjb3JkWyJ2ZXJzaW9uIl0NCiAgICByZXR1cm4gcmVjb3JkWyJnaXRIdWJSZWxlYXNlVVJMIl0uc3BsaXQoIi8iKVstMV0NCg0KDQpkZWYgc2V0X2NsdXN0ZXJfaW5pdF9wYXJhbXMocGFyYW1zOiBkaWN0LCBzcGVjczogdHlwaW5nLkxpc3RbQ2x1c3RlckluaXRTcGVjXSwgY2x1c3Rlcl9uYW1lOiBzdHIsIHRhcmdldF9wYXJhbXM6IGRpY3QpIC0+IE5vbmU6DQogICAgb3JkZXIgPSAxMDAwMA0KICAgIGZvciBzcGVjIGluIHNwZWNzOg0KICAgICAgICBmb3IgdGFyZ2V0IGluIHNwZWMudGFyZ2V0czoNCiAgICAgICAgICAgIHRhcmdldF9rZXkgPSBmInt0YXJnZXRfcGFyYW1zW3RhcmdldC5sb3dlcigpXX0iDQogICAgICAgICAgICBpZiBub3QgcGFyYW1zLmdldCh0YXJnZXRfa2V5KToNCiAgICAgICAgICAgICAgICBwYXJhbXNbdGFyZ2V0X2tleV0gPSB7fQ0KDQogICAgICAgICAgICBwYXJhbXNbdGFyZ2V0X2tleV1bc3BlYy5jbHVzdGVyX2luaXRfa2V5XSA9IHsNCiAgICAgICAgICAgICAgICAiT3JkZXIiOiBvcmRlciwNCiAgICAgICAgICAgICAgICAiU3BlYyI6IHNwZWMuc3BlYywNCiAgICAgICAgICAgICAgICAiTmFtZSI6IHNwZWMuY2x1c3Rlcl9pbml0X2tleSwNCiAgICAgICAgICAgICAgICAiUHJvamVjdCI6IHNwZWMucHJvamVjdCwNCiAgICAgICAgICAgICAgICAiTG9ja2VyIjogImF6dXJlLXN0b3JhZ2UiLA0KICAgICAgICAgICAgICAgICJWZXJzaW9uIjogc3BlYy52ZXJzaW9uDQogICAgICAgICAgICB9DQogICAgICAgICAgICBvcmRlciArPSAxMDANCg0KDQpkZWYgbWFpbigpOg0KICAgIHBhcnNlciA9IGFyZ3BhcnNlLkFyZ3VtZW50UGFyc2VyKGRlc2NyaXB0aW9uPSJUT0RPIFJESCIpDQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgiLS1sb2NrZXIiLCBkZWZhdWx0PSJhenVyZS1zdG9yYWdlIikNCiAgICBwYXJzZXIuYWRkX2FyZ3VtZW50KCItLWNsdXN0ZXItaW5pdC13b3JraW5nLWRpciIsIGRlZmF1bHQ9ImNsdXN0ZXItaW5pdCIpDQogICAgc3VicGFyc2VycyA9IHBhcnNlci5hZGRfc3VicGFyc2VycygpDQogICAgY2N3X3BhcnNlciA9IHN1YnBhcnNlcnMuYWRkX3BhcnNlcigic2x1cm0iKQ0KICAgICMgVE9ETyB0aGlzIG5lZWRzIHRvIGJlIGJ5IGNsdXN0ZXIgdHlwZQ0KICAgIHRhcmdldF9wYXJhbXMgPSB7DQogICAgICAgICJsb2dpbiI6ICJMb2dpbkNsdXN0ZXJJbml0U3BlY3MiLA0KICAgICAgICAiZ3B1IjogIkdQVUNsdXN0ZXJJbml0U3BlY3MiLA0KICAgICAgICAiaHBjIjogIkhQQ0NsdXN0ZXJJbml0U3BlY3MiLA0KICAgICAgICAiZDY0ZCI6ICJENjREQ2x1c3RlckluaXRTcGVjcyIsDQogICAgICAgICJkMTZkIjogIkQxNkRDbHVzdGVySW5pdFNwZWNzIiwNCiAgICAgICAgIm02NCI6ICJNNjRDbHVzdGVySW5pdFNwZWNzIiwNCiAgICAgICAgInNjaGVkdWxlciI6ICJTY2hlZHVsZXJDbHVzdGVySW5pdFNwZWNzIiwNCiAgICAgICAgImR5bmFtaWMiOiAiRHluYW1pY0NsdXN0ZXJJbml0U3BlY3MiLA0KICAgICAgICAib29kIjogIkNsdXN0ZXJJbml0U3BlY3MiDQogICAgfQ0KICAgIGNjd19wYXJzZXIuc2V0X2RlZmF1bHRzKGNsdXN0ZXJfdHlwZT0ic2x1cm0iLCB0YXJnZXRfcGFyYW1zPXRhcmdldF9wYXJhbXMpDQogICAgY2N3X3BhcnNlci5hZGRfYXJndW1lbnQoIi0tZGJQYXNzd29yZCIsIGRlc3Q9ImRiUGFzc3dvcmQiLCBkZWZhdWx0PSIiLCBoZWxwPSJNeVNRTCBkYXRhYmFzZSBwYXNzd29yZCIpDQogICAgDQogICAgb29kX3BhcnNlciA9IHN1YnBhcnNlcnMuYWRkX3BhcnNlcigib29kIikNCiAgICBvb2RfcGFyc2VyLnNldF9kZWZhdWx0cyhjbHVzdGVyX3R5cGU9Im9vZCIsIHRhcmdldF9wYXJhbXM9dGFyZ2V0X3BhcmFtcykNCiAgICANCiAgICBhcmdzID0gcGFyc2VyLnBhcnNlX2FyZ3MoKQ0KDQogICAgaWYgYXJncy5jbHVzdGVyX3R5cGUgPT0gInNsdXJtIjoNCiAgICAgICAgb3V0cHV0X3BhcmFtcyA9IGdldF9qc29uX2RpY3QoJ2luaXRpYWxfcGFyYW1zLmpzb24nKQ0KICAgIGVsc2U6DQogICAgICAgIG91dHB1dF9wYXJhbXMgPSB7fQ0KICAgIGNjd19vdXRwdXRzID0gZ2V0X2pzb25fZGljdCgnY2N3T3V0cHV0cy5qc29uJykNCg0KICAgIHNwZWNzID0gZG93bmxvYWRfY2x1c3Rlcl9pbml0KGNjd19vdXRwdXRzLCBvcy5wYXRoLmpvaW4ob3MuZ2V0Y3dkKCksIGFyZ3MuY2x1c3Rlcl9pbml0X3dvcmtpbmdfZGlyKSwgYXJncy5sb2NrZXIpDQogICAgc2V0X2NsdXN0ZXJfaW5pdF9wYXJhbXMob3V0cHV0X3BhcmFtcywgc3BlY3MsIGFyZ3MuY2x1c3Rlcl90eXBlLCBhcmdzLnRhcmdldF9wYXJhbXMpDQogICAgaWYgYXJncy5jbHVzdGVyX3R5cGUgPT0gInNsdXJtIjoNCiAgICAgICAgc2V0X3NsdXJtX3BhcmFtcyhvdXRwdXRfcGFyYW1zLCBhcmdzLmRiUGFzc3dvcmQsIGNjd19vdXRwdXRzKQ0KICAgIGVsc2U6DQogICAgICAgIHNldF9vb2RfcGFyYW1zKG91dHB1dF9wYXJhbXMsIGNjd19vdXRwdXRzKQ0KICAgIHByaW50KGpzb24uZHVtcHMob3V0cHV0X3BhcmFtcywgaW5kZW50PTQpKQ0KDQoNCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6DQogICAgbWFpbigp \ No newline at end of file diff --git a/bicep/files-to-load/slurm.txt b/bicep/files-to-load/slurm.txt new file mode 100644 index 00000000..bb92be06 --- /dev/null +++ b/bicep/files-to-load/slurm.txt @@ -0,0 +1,1109 @@ + +################################ +## Cluster Configuration File ## +################################ + + +[cluster Slurm] +FormLayout = selectionpanel +Category = Schedulers + +Autoscale = $Autoscale + + [[node defaults]] + UsePublicNetwork = $UsePublicNetwork + Credentials = $Credentials + SubnetId = $SubnetId + Region = $Region + KeyPairLocation = ~/.ssh/cyclecloud.pem + # hubMI is assigned here + Azure.Identities = $ManagedIdentity + Tags = $NodeTags + + # Slurm autoscaling supports both Terminate and Deallocate shutdown policies + ShutdownPolicy = $configuration_slurm_shutdown_policy + + # Lustre mounts require termination notifications to unmount + EnableTerminateNotification = ${NFSType == "lustre" || NFSSchedType == "lustre" || AdditionalNFSType == "lustre" || EnableTerminateNotification} + TerminateNotificationTimeout = 10m + CloudInit="""#!/bin/bash +sed -i 's/^SHELL.*/SHELL=\/bin\/bash/g' /etc/default/useradd + +CLUSTER_NAME=$( jetpack config cyclecloud.cluster.name ) +mkdir -p /etc/slurm/ +ln -s /sched/${CLUSTER_NAME}/topology.conf /etc/slurm/ + + """ + + + [[[configuration]]] + + slurm.version = $configuration_slurm_version + slurm.user.uid = 11100 + slurm.user.gid = 11100 + munge.user.uid = 11101 + munge.user.gid = 11101 + slurm.enable_healthchecks = true + slurm.accounting.enabled = $configuration_slurm_accounting_enabled + slurm.accounting.url = $configuration_slurm_accounting_url + slurm.accounting.user = $configuration_slurm_accounting_user + slurm.accounting.password = $configuration_slurm_accounting_password + slurm.accounting.certificate_url = $configuration_slurm_accounting_certificate_url + slurm.accounting.storageloc = $configuration_slurm_accounting_storageloc + slurm.additional.config = $additional_slurm_config + slurm.ha_enabled = $configuration_slurm_ha_enabled + slurm.launch_parameters = $configuration_slurm_launch_parameters + + # Disable ip-XXXXXXXX hostname generation + cyclecloud.hosts.standalone_dns.enabled = ${NodeNameIsHostname==false} + cyclecloud.hosts.simple_vpc_dns.enabled = ${NodeNameIsHostname==false} + + # For fast spin-up after Deallocate, force an immediate re-converge on boot + cyclecloud.converge_on_boot = true + + # Disable normal NFS exports and mounts + cyclecloud.mounts.sched.disabled = true + cyclecloud.mounts.shared.disabled = true + cyclecloud.exports.sched.disabled = true + cyclecloud.exports.shared.disabled = true + cyclecloud.exports.sched.samba.enabled = false + cyclecloud.exports.shared.samba.enabled = false + cyclecloud.exports.defaults.samba.enabled = false + cshared.server.legacy_links_disabled = true + + # May be used to identify the ID in cluster-init scripts + cluster.identities.default = $ManagedIdentity + + monitoring.ingestion_endpoint = $MonitoringIngestionEndpoint + monitoring.identity_client_id = $MonitoringIdentityClientId + monitoring.enabled = $MonitoringEnabled + cyclecloud.enable_chef = false + + [[[cluster-init cyclecloud/healthagent:default:1.0.2]]] + [[[cluster-init cyclecloud/slurm:default:4.0.1]]] + Optional = true + [[[cluster-init cyclecloud/slurm:pyxis:4.0.1]]] + + [[[volume boot]]] + Size = ${ifThenElse(BootDiskSize > 0, BootDiskSize, undefined)} + SSD = True + + [[[configuration cyclecloud.mounts.nfs_shared]]] + type = $NFSType + mountpoint = /shared + export_path = ${ifThenElse(NFSType == "lustre", strcat("tcp:/lustrefs", NFSSharedExportPath), NFSSharedExportPath)} + address = $NFSAddress + options = $NFSSharedMountOptions + + [[[configuration cyclecloud.mounts.nfs_sched]]] + type = $NFSSchedType + mountpoint = /sched + export_path = ${ifThenElse(NFSSchedType == "lustre", strcat("tcp:/lustrefs", NFSSchedExportPath), NFSSchedExportPath)} + address = ${ifThenElse(UseBuiltinSched && !configuration_slurm_ha_enabled, undefined, NFSSchedAddress)} + options = $NFSSchedMountOptions + + [[[configuration cyclecloud.mounts.additional_nfs]]] + disabled = ${AdditionalNFS isnt true} + type = $AdditionalNFSType + address = $AdditionalNFSAddress + mountpoint = $AdditionalNFSMountPoint + export_path = ${ifThenElse(AdditionalNFSType == "lustre", strcat("tcp:/lustrefs", AdditionalNFSExportPath), AdditionalNFSExportPath)} + options = $AdditionalNFSMountOptions + + [[node scheduler]] + MachineType = $SchedulerMachineType + ImageName = $SchedulerImageName + IsReturnProxy = $ReturnProxy + AdditionalClusterInitSpecs = $SchedulerClusterInitSpecs + ComputerName = ${toLower(regexps("([^a-zA-Z0-9-])", ifThenElse(SchedulerHostName=="Cluster Prefix", StrJoin("-", ClusterName, "scheduler"), ifThenElse(Size(Trim(SchedulerHostName)) == 0 || SchedulerHostName == "Generated", undefined, SchedulerHostName)), "-"))} + # indented version, for clarity. + # ${toLower( + # regexps("([^a-zA-Z0-9-])", + # ifThenElse(SchedulerHostName=="Cluster Prefix", + # StrJoin("-", ClusterName, "scheduler"), + # ifThenElse(Size(Trim(SchedulerHostName)) == 0 || SchedulerHostName == "Generated", + # undefined, + # SchedulerHostName)), + # "-"))} + Zone = ${ifThenElse(configuration_slurm_ha_enabled, SchedulerZone, undefined)} + + [[[configuration]]] + slurm.role = scheduler + # Disable NFS mount of built-in /sched since it is a local volume mount: cyclecloud.mounts.builtinsched + cyclecloud.mounts.nfs_sched.disabled = ${UseBuiltinSched && !configuration_slurm_ha_enabled} + cyclecloud.mounts.nfs_shared.disabled = ${UseBuiltinShared && !configuration_slurm_ha_enabled} + slurm.secondary_scheduler_name = ${ifThenElse(configuration_slurm_ha_enabled, "scheduler-ha-1", undefined)} + + [[[cluster-init cyclecloud/healthagent:default:1.0.2]]] + [[[cluster-init cyclecloud/slurm:scheduler:4.0.1]]] + [[[cluster-init cyclecloud/slurm:pyxis:4.0.1]]] + + [[[network-interface eth0]]] + AssociatePublicIpAddress = $UsePublicNetwork + + [[[volume sched]]] + Size = $SchedFilesystemSize + SSD = True + Mount = builtinsched + Persistent = True + Disabled = ${!UseBuiltinSched || configuration_slurm_ha_enabled} + + [[[volume shared]]] + Size = $FilesystemSize + SSD = True + Mount = builtinshared + Persistent = True + Disabled = ${!UseBuiltinShared || configuration_slurm_ha_enabled} + + [[[configuration cyclecloud.mounts.builtinsched]]] + disabled = ${!UseBuiltinSched || configuration_slurm_ha_enabled} + mountpoint = /sched + fs_type = xfs + + [[[configuration cyclecloud.mounts.builtinshared]]] + disabled = ${!UseBuiltinShared || configuration_slurm_ha_enabled} + mountpoint = /shared + fs_type = xfs + + [[[configuration cyclecloud.exports.builtinsched]]] + disabled = ${!UseBuiltinSched || configuration_slurm_ha_enabled} + export_path = /sched + options = no_root_squash + samba.enabled = false + type = nfs + + [[[configuration cyclecloud.exports.builtinshared]]] + disabled = ${!UseBuiltinShared || configuration_slurm_ha_enabled} + export_path = /shared + samba.enabled = false + type = nfs + + [[nodearray scheduler-ha]] + Extends = scheduler + IsReturnProxy = false + InitialCount = $configuration_slurm_ha_enabled + Zone = $SchedulerHAZone + # Do not inherit property from node that is not used in nodearray + # The equivalent is ComputerNamePrefix for nodearray, however Cluster-init will handle renaming of all hosts in a VMSS + ComputerName := undefined + [[[configuration]]] + autoscale.enabled = false + slurm.node_prefix = ${ifThenElse(NodeNamePrefix=="Cluster Prefix", StrJoin("-", ClusterName, ""), NodeNamePrefix)} + slurm.use_nodename_as_hostname = $NodeNameIsHostname + slurm.is_primary_scheduler = false + + [[nodearray login]] + InitialCount = $NumberLoginNodes + MachineType = $loginMachineType + ImageName = $LoginImageName + AdditionalClusterInitSpecs = $LoginClusterInitSpecs + + [[[cluster-init cyclecloud/healthagent:default:1.0.2]]] + [[[cluster-init cyclecloud/slurm:login:4.0.1]]] + [[[cluster-init cyclecloud/slurm:pyxis:4.0.1]]] + + [[[configuration]]] + slurm.role = login + autoscale.enabled = false + slurm.node_prefix = ${ifThenElse(NodeNamePrefix=="Cluster Prefix", StrJoin("-", ClusterName, ""), NodeNamePrefix)} + slurm.use_nodename_as_hostname = $NodeNameIsHostname + + + [[nodearray login-arm64]] + Extends = login + MachineType = $loginArm64MachineType + ImageName = $LoginArm64ImageName + + + + + [[node nodearraybase]] + Abstract = true + [[[configuration]]] + slurm.role = execute + slurm.autoscale = true + + slurm.node_prefix = ${ifThenElse(NodeNamePrefix=="Cluster Prefix", StrJoin("-", ClusterName, ""), NodeNamePrefix)} + slurm.use_nodename_as_hostname = $NodeNameIsHostname + + [[[cluster-init cyclecloud/healthagent:default:1.0.2]]] + [[[cluster-init cyclecloud/slurm:execute:4.0.1]]] + [[[cluster-init cyclecloud/slurm:pyxis:4.0.1]]] + + [[[network-interface eth0]]] + AssociatePublicIpAddress = $ExecuteNodesPublic + + [[nodearray hpc]] + Extends = nodearraybase + MachineType = $HPCMachineType + ImageName = $HPCImageName + MaxCount = $MaxHPCExecuteNodeCount + Azure.MaxScalesetSize = $HPCMaxScalesetSize + AdditionalClusterInitSpecs = $HPCClusterInitSpecs + EnableNodeHealthChecks = $EnableNodeHealthChecks + + + [[[configuration]]] + slurm.default_partition = true + slurm.hpc = true + slurm.partition = hpc + + [[nodearray d64d]] + Extends = nodearraybase + MachineType = $D64DMachineType + ImageName = $D64DImageName + MaxCount = $MaxD64DNodeCount + + AdditionalClusterInitSpecs = $D64DClusterInitSpecs + + [[[configuration]]] + slurm.hpc = false + slurm.partition = d64d + # set pcpu = false for all hyperthreaded VMs + slurm.use_pcpu = false + + [[nodearray d16d]] + Extends = nodearraybase + MachineType = $D16DMachineType + ImageName = $D16DImageName + MaxCount = $MaxD16DNodeCount + + AdditionalClusterInitSpecs = $D16DClusterInitSpecs + + [[[configuration]]] + slurm.hpc = false + slurm.partition = d16d + # set pcpu = false for all hyperthreaded VMs + slurm.use_pcpu = false + + [[nodearray m64]] + Extends = nodearraybase + MachineType = $M64MachineType + ImageName = $M64ImageName + MaxCount = $MaxM64NodeCount + + AdditionalClusterInitSpecs = $M64ClusterInitSpecs + + [[[configuration]]] + slurm.hpc = false + slurm.partition = m64 + # set pcpu = false for all hyperthreaded VMs + slurm.use_pcpu = false + + [[nodearray gpu]] + + EnableAcceleratedNetworking=true + + + # GB200: Peregrine disallows SinglePlacementGroup, but pkey requires SingleScaleset + Azure.Overprovision = false + Azure.MaxScaleSetSize = 1000 + Azure.SingleScaleset = true + Azure.SinglePlacementGroup = false + + Extends = nodearraybase + MachineType = $GPUMachineType + ImageName = $GPUImageName + MaxCount = $MaxGPUExecuteNodeCount + EnableNodeHealthChecks = $EnableNodeHealthChecks + + Interruptible = $GPUUseLowPrio + MaxPrice = $GPUSpotMaxPrice + AdditionalClusterInitSpecs = $GPUClusterInitSpecs + + [[[configuration]]] + slurm.default_partition = true + slurm.hpc = false + slurm.partition = gpu + #Parameter to enable or disable IMEX service on a per-job basis + #IMEX Support is enabled by default for GB200 but can be disabled by setting param to False + slurm.imex.enabled=True + + [[nodearray gpu2]] + extends = gpu + MaxCount = $MaxGPU2ExecuteNodeCount + Azure.SinglePlacementGroup = false + Azure.MaxScalesetSize = $HPCMaxScalesetSize + [[[configuration]]] + slurm.default_partition = false + slurm.hpc = false + slurm.partition = gpu2 + #Parameter to enable or disable IMEX service on a per-job basis + #IMEX Support is enabled by default for GB200 but can be disabled by setting param to False + slurm.imex.enabled=True + + [[nodearray dynamic]] + Extends = nodearraybase + MachineType = $DynamicMachineType + ImageName = $DynamicImageName + MaxCoreCount = $MaxDynamicExecuteCoreCount + + Interruptible = $DynamicUseLowPrio + MaxPrice = $DynamicSpotMaxPrice + AdditionalClusterInitSpecs = $DynamicClusterInitSpecs + [[[configuration]]] + slurm.hpc = false + # Slurm only allows a single feature to be defined in a Nodeset. If multiple features are defined here, only first value will be used for the nodeset. + slurm.dynamic_feature := "dyn" + # If this option is used, slurmd is started with this configuration for dynamic nodes. + #slurm.dynamic_config := "-Z --conf \"Feature=dyn\"" + # set pcpu = false for all hyperthreaded VMs + slurm.use_pcpu = false + slurm.autoscale = $EnableDynamicPartition + +[parameters About] +Order = 1 + + [[parameters About Slurm]] + + [[[parameter slurm]]] + HideLabel = true + Config.Plugin = pico.widget.HtmlTemplateWidget + Config.Template = '''
Follow the instructions in theREADMEfor details on instructions on extending and configuring the Project for your environment. |
Slurm is the most widely used workload manager in HPC, as the scheduler of choice for six of the top ten systems in the TOP500 and with market penetration of more than 70%. Slurm is an advanced, open-source scheduler designed to satisfy the demanding needs of high-performance computing (HPC), high-throughput computing (HTC), and artificial intelligence (AI). |
Commercial Support provided by SchedMD |
Get more from your HPC investment! SchedMD, the company behind Slurm development, can answer your Slurm questions and explain our options for consultation, training, support, and migration. |
Slurm at a glance |
Slurm provides massive scalability and can easily manage performance requirements for small cluster, large cluster, and supercomputer needs. Slurm outperforms competitive schedulers with compute rates at: |
|
Slurm’s plug-in based architecture enables optimization and control in scheduling operations to meet organizational priorities. With first class resource management for GPUs, Slurm allows users to request GPU resources alongside CPUs. This flexibility ensures that jobs are executed quickly and efficiently, while maximizing resource utilization. |
Other Slurm features include: |
|
The directories /sched and /shared are network attached mounts and exist on all nodes of the cluster.
+
+ Options for providing these mounts:
+ [Builtin]: The scheduler node is an NFS server that provides the mountpoint to the other nodes of the cluster (not supported for HA configurations).
+ [External NFS]: A network attached storage such as Azure Netapp Files, HPC Cache, or another VM running an NFS server provides the mountpoint.
+ [Azure Managed Lustre]: An Azure Managed Lustre deployment provides the mountpoint.
+
+ Note: the cluster must be terminated for changes to filesystem mounts to take effect. +
''' + Conditions.Hidden := false + + [[parameters Scheduler Mount]] + Order = 20 + Label = File-system Mount for /sched + + [[[parameter About sched]]] + HideLabel = true + Config.Plugin = pico.widget.HtmlTemplateWidget + Config.Template = '''Slurm's configuration is linked in from the /sched directory. It is managed by the scheduler node.
Uncheck the box below to disable the built-in NFS export of the /sched directory and use an external file-system.
Warning: switching an active cluster over to NFS or Lustre from Builtin will delete the shared disk.
" + Conditions.Hidden := UseBuiltinSched || configuration_slurm_ha_enabled + + [[[parameter NFSSchedType]]] + Label = FS Type + ParameterType = StringList + Config.Label = Type of shared filesystem to use for this cluster + Config.Plugin = pico.form.Dropdown + Config.Entries := {[Label="External NFS"; Value="nfs"], [Label="Azure Managed Lustre"; Value="lustre"]} + DefaultValue = nfs + Conditions.Hidden := UseBuiltinSched && !configuration_slurm_ha_enabled + + [[[parameter NFSSchedAddress]]] + Label = IP Address + Description = The IP address or hostname of the NFS server or Lustre FS. Also accepts a list comma-separated addresses, for example, to mount a frontend load-balanced Azure HPC Cache. + Config.ParameterType = String + Conditions.Hidden := UseBuiltinSched && !configuration_slurm_ha_enabled + + [[[parameter NFSSchedExportPath]]] + Label = Export Path + Description = The path exported by the file system + DefaultValue = /sched + Conditions.Hidden := UseBuiltinSched && !configuration_slurm_ha_enabled + + [[[parameter NFSSchedMountOptions]]] + Label = Mount Options + Description = NFS Client Mount Options + Conditions.Hidden := UseBuiltinSched && !configuration_slurm_ha_enabled + + + [[[parameter SchedFilesystemSize]]] + Label = Size (GB) + Description = The filesystem size (cannot be changed after initial start) + DefaultValue = 30 + Config.Plugin = pico.form.NumberTextBox + Config.MinValue = 10 + Config.MaxValue = 10240 + Config.IntegerOnly = true + Conditions.Excluded := !UseBuiltinSched || configuration_slurm_ha_enabled + + + + [[parameters Default NFS Share]] + Order = 30 + Label = File-system Mount for /shared + + [[[parameter About shared]]] + HideLabel = true + Config.Plugin = pico.widget.HtmlTemplateWidget + Config.Template = '''Users' home directories reside within the /shared mountpoint with the base homedir /shared/home.
Uncheck the box below to disable the built-in NFS export of the /shared directory and use an external file-system.
Warning: switching an active cluster over to NFS or Lustre from Builtin will delete the shared disk.
" + Conditions.Hidden := UseBuiltinShared || configuration_slurm_ha_enabled + + [[[parameter NFSType]]] + Label = FS Type + ParameterType = StringList + Config.Label = Type of shared filesystem to use for this cluster + Config.Plugin = pico.form.Dropdown + Config.Entries := {[Label="External NFS"; Value="nfs"], [Label="Azure Managed Lustre"; Value="lustre"]} + DefaultValue = nfs + Conditions.Hidden := UseBuiltinShared && !configuration_slurm_ha_enabled + + [[[parameter NFSAddress]]] + Label = IP Address + Description = The IP address or hostname of the NFS server or Lustre FS. Also accepts a list comma-separated addresses, for example, to mount a frontend load-balanced Azure HPC Cache. + Config.ParameterType = String + Conditions.Hidden := UseBuiltinShared && !configuration_slurm_ha_enabled + + [[[parameter NFSSharedExportPath]]] + Label = Export Path + Description = The path exported by the file system + DefaultValue = /shared + Conditions.Hidden := UseBuiltinShared && !configuration_slurm_ha_enabled + + [[[parameter NFSSharedMountOptions]]] + Label = Mount Options + Description = NFS Client Mount Options + Conditions.Hidden := UseBuiltinShared && !configuration_slurm_ha_enabled + + + [[[parameter FilesystemSize]]] + Label = Size (GB) + Description = The filesystem size (cannot be changed after initial start) + DefaultValue = 100 + Config.Plugin = pico.form.NumberTextBox + Config.MinValue = 10 + Config.MaxValue = 10240 + Config.IntegerOnly = true + Conditions.Excluded := !UseBuiltinShared || configuration_slurm_ha_enabled + + [[parameters Additional NFS Mount]] + Order = 40 + Label = Additional Filesystem Mount + [[[parameter Additional Shared FS Mount Readme]]] + HideLabel = true + Config.Plugin = pico.widget.HtmlTemplateWidget + Config.Template := "Mount another shared file-system endpoint on the cluster nodes.
" + Order = 20 + + [[[parameter AdditionalNFS]]] + HideLabel = true + DefaultValue = false + Widget.Plugin = pico.form.BooleanCheckBox + Widget.Label = Add Shared Filesystem mount + + [[[parameter AdditionalNFSType]]] + Label = FS Type + ParameterType = StringList + Config.Label = Shared filesystem type of the additional mount + Config.Plugin = pico.form.Dropdown + Config.Entries := {[Label="External NFS"; Value="nfs"], [Label="Azure Managed Lustre"; Value="lustre"]} + DefaultValue = nfs + Conditions.Excluded := AdditionalNFS isnt true + + [[[parameter AdditionalNFSAddress]]] + Label = IP Address + Description = The IP address or hostname of the additional mount. Also accepts a list comma-separated addresses, for example, to mount a frontend load-balanced Azure HPC Cache. + Config.ParameterType = String + Conditions.Excluded := AdditionalNFS isnt true + + [[[parameter AdditionalNFSMountPoint]]] + Label = Mount Point + Description = The path at which to mount the Filesystem + DefaultValue = /data + Conditions.Excluded := AdditionalNFS isnt true + + [[[parameter AdditionalNFSExportPath]]] + Label = Export Path + Description = The path exported by the file system + DefaultValue = /data + Conditions.Excluded := AdditionalNFS isnt true + + [[[parameter AdditionalNFSMountOptions]]] + Label = Mount Options + Description = Filesystem Client Mount Options + Conditions.Excluded := AdditionalNFS isnt true + + +[parameters Advanced Settings] +Order = 20 + + [[parameters Azure Settings]] + Order = 10 + + [[[parameter Credentials]]] + Description = The credentials for the cloud provider + ParameterType = Cloud.Credentials + + [[[parameter ManagedIdentity]]] + Label = Managed Id + Description = Optionally assign an Azure user assigned managed identity to all nodes to access Azure resources using assigned roles. + ParameterType = Azure.ManagedIdentity + DefaultValue = =undefined + + [[[parameter BootDiskSize]]] + Description = Optional: Size of the OS/boot disk in GB for all nodes in the cluster (leave at 0 to use Image size) + ParameterType = Integer + Config.Plugin = pico.form.NumberTextBox + Config.MinValue = 0 + Config.MaxValue = 32,000 + Config.IntegerOnly = true + Config.Increment = 64 + DefaultValue = 0 + + [[[parameter EnableDynamicPartition]]] + Label = Enable Dynamic Partition + DefaultValue = true + Widget.Plugin = pico.form.BooleanCheckBox + Widget.Label = Create a partition for the dynamic nodearray + + [[[parameter NodeTags]]] + Label = VM Tags + Description = Tags applied to all nodes + ParameterType = Record + DefaultValue := [] + Config.MultiSelect = false + + [[parameters Slurm Settings ]] + + Order = 5 + + [[[parameter slurm_version_warning]]] + HideLabel = true + Config.Plugin = pico.widget.HtmlTemplateWidget + Config.Template := "| Note: For SLES HPC, we can only install the version supported by SLES HPC's zypper repos. At the time of this release, that is 23.02.7 |
| Note: Checking this box will create persistent databases and tables in SQL DB provided. Deleting this cluster will not automatically delete those databases. User is responsible for periodically purging/archiving their slurm databases to maintain costs. |