UNPKG

cloud-blender

Version:

A high level library for cloud compute operations

1,427 lines (1,085 loc) 76.9 kB
'use strict'; var request = require('request'), underscore = require('underscore'), AzureError = require('./azure-error'), fs = require('fs'), url = require('url'), util = require('util'), xml2js = require('xml2js'), parseString = xml2js.parseString, interval = 2000, operationValidateInterval = 15000, xMsVersion = '2014-06-01', tunnelingProxyURL; module.exports = (function () { var azureStorageInst; azureStorageInst = {}; function getAzureStorage(regionCtx) { var instId; regionCtx = regionCtx || {}; instId = util.format('%s_%s_%s', regionCtx.cloudRegion || '', regionCtx.storageAccount || '', regionCtx.storageAccessKey || ''); if (azureStorageInst[instId]) { return azureStorageInst[instId]; } azureStorageInst[instId] = require('./azure_storage')({ storageAccount: regionCtx.storageAccount, storageAccessKey: regionCtx.storageAccessKey, host: (function () { var storageAcc; if (regionCtx.cloudRegion !== 'China East' && regionCtx.cloudRegion !== 'China North') { return undefined; } storageAcc = regionCtx.storageAccount; if (!storageAcc) { return { primaryHost: url.format({ protocol: 'https:', host: 'table.core.chinacloudapi.cn' }), secondaryHost: url.format({ protocol: 'https:', host: 'table.core.chinacloudapi.cn' }) }; } return { primaryHost: url.format({ protocol: 'https:', host: storageAcc + '.table.core.chinacloudapi.cn' }), secondaryHost: url.format({ protocol: 'https:', host: storageAcc + '-secondary.table.core.chinacloudapi.cn' }), }; })() }); return azureStorageInst[instId]; } function azureRetryDeleteRequest(settings, pollingCount, interval, callback) { var deleteRequestSettings = { method: 'DELETE', headers: { 'x-ms-version': settings.xMsVersion }, url: settings.url, cert: settings.azureCert, key: settings.azureKey }; request(deleteRequestSettings, function (err, res, body) { if (!res){ callback(new AzureError('response is not valid-'+err)); return; } // in case of retry code and there we didn't reached to max polling if (pollingCount > 0 && underscore.contains(settings.retryCodes, res.statusCode) === true) { setTimeout(azureRetryDeleteRequest, interval, settings, pollingCount - 1, interval, callback); } // in case owe got an error code which is not success/retry if ((err || underscore.contains(settings.successCode, res.statusCode) === false) && underscore.contains(settings.retryCodes, res.statusCode) === false) { callback(err || new AzureError('res.statusCode-' + res.statusCode + ' ' + res.body)); return; } if (underscore.contains(settings.successCode, res.statusCode) === true) { callback(null, res.statusCode); return; } }); } function azureRetryRequest(settings, pollingCount, interval, callback) { request[settings.restType]({ uri: settings.url, headers: { 'x-ms-version': settings.xMsVersion, 'Content-Type': 'application/xml' }, cert: settings.azureCert, key: settings.azureKey, body: settings.xmlBody }, function (err, res, body) { if (!res){ callback(new AzureError('response is not valid-'+err)); return; } // in case of retry code and there we didn't reached to max polling if (pollingCount > 0 && underscore.contains(settings.retryCodes, res.statusCode) === true) { setTimeout(azureRetryRequest, interval, settings, pollingCount - 1, interval, callback); } // in case owe got an error code which is not success/retry if ((err || underscore.contains(settings.successCode, res.statusCode) === false) && underscore.contains(settings.retryCodes, res.statusCode) === false) { callback(err || new AzureError('res.statusCode-' + res.statusCode + ' ' + res.body)); return; } if (underscore.contains(settings.successCode, res.statusCode) === true) { callback(null, res); return; } }); } function azureValidateRetryRequest(ctx, settings, pollingCount, interval, callback) { request[settings.restType]({ uri: settings.url, headers: { 'x-ms-version': settings.xMsVersion, 'Content-Type': 'application/xml' }, cert: settings.azureCert, key: settings.azureKey, body: settings.xmlBody }, function (err, res, body) { if (res) { // in case owe got an error code which is not success/retry if ((err || underscore.contains(settings.successCode, res.statusCode) === false) && underscore.contains(settings.retryCodes, res.statusCode) === false) { callback(err || new AzureError('res.statusCode-' + res.statusCode + ' ' + res.body), {}); return; } else { azureGetOpertionStatus(ctx, settings, res.headers['x-ms-request-id'], 15, operationValidateInterval, function (errStatus, resultStatus) { // in case of retry code and there we didn't reached to max polling or status code is success and the operation failed. if ((pollingCount > 0 && underscore.contains(settings.retryCodes, res.statusCode) === true) || (pollingCount > 0 && underscore.contains(settings.successCode, res.statusCode) === true && resultStatus === 'Failed')) { setTimeout(azureValidateRetryRequest, interval, ctx, settings, pollingCount - 1, interval, callback); } // in case both ter rest and the operation Succeeded if (underscore.contains(settings.successCode, res.statusCode) === true && resultStatus === 'Succeeded') { callback(null, res); return; } }); } } else { setTimeout(azureValidateRetryRequest, interval, ctx, settings, pollingCount - 1, interval, callback); } }); } function azureGetOpertionStatus(ctx, settings, requestId, pollingCount, interval, callback) { var getSettings = { url: 'https://' + ctx.regionContext.apiPrefix + '/' + settings.subscriptionId + '/operations/' + requestId, xMsVersion: settings.xMsVersion, azureCert: settings.azureCert, azureKey: settings.azureKey, successCode: 200 }; azureGetRequest(getSettings, function (errGet, resultGet) { if (!resultGet){ callback(new AzureError('response is not valid-'+errGet)); return; } // in case the status is still in InProgress or missing result if ((pollingCount > 0 && !resultGet) || (pollingCount > 0 && resultGet.Operation.Status[0] === 'InProgress')) { setTimeout(azureGetOpertionStatus, interval, ctx, settings, requestId, pollingCount - 1, interval, callback); } // in case pollingCount=0 or status ='Failed' if ((pollingCount === 0 && !resultGet) || errGet || resultGet.Operation.Status[0] === 'Failed' || (pollingCount === 0 && resultGet.Operation.Status[0] !== 'Succeeded')) { callback(errGet || 'Failed', 'Failed'); return; } // in case of Status = 'Succeeded' if (resultGet.Operation.Status[0] === 'Succeeded') { callback(null, 'Succeeded'); return; } }); } function azureGetRequest(settings, callback) { var getSettings = { url: settings.url, headers: { 'x-ms-version': settings.xMsVersion }, cert: settings.azureCert, key: settings.azureKey }; request(getSettings, function (err, response, body) { if (!response){ callback(new AzureError('response is not valid-'+err)); return; } if (err || response.statusCode !== settings.successCode) { callback(err || new AzureError('res.statusCode-' + response.statusCode + ' ' + response.body)); return; } parseString(response.body, function (err, result) { if (err) { callback(err); return; } callback(null, result); }); }); } function checkPollCloudServices(settings, subscriptionId, azureCert, azureKey, newServicesArr, pollingCount, interval, callback) { var getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + subscriptionId + '/services/hostedservices', xMsVersion: xMsVersion, azureCert: azureCert, azureKey: azureKey, successCode: 200 }; azureGetRequest(getSettings, function (err, result) { // in case of err if (err) { if (pollingCount === 0) { callback(new AzureError('max polling for cloud services')); return; } else { setTimeout(checkPollCloudServices, interval, settings, subscriptionId, azureCert, azureKey, newServicesArr, pollingCount - 1, interval, callback); } } // extract an array of all services names which where created else { // extract an array of all services names which where created var services = underscore.flatten(underscore.pluck(underscore.filter(result.HostedServices.HostedService, function (service) { return service.HostedServiceProperties[0].Status[0] === 'Created'; }), 'ServiceName')); if (underscore.difference(newServicesArr, services).length === 0) { callback(null, true); return; } else { if (pollingCount === 0) { callback(new AzureError('max polling for cloud services')); return; } else { setTimeout(checkPollCloudServices, interval, settings, subscriptionId, azureCert, azureKey, newServicesArr, pollingCount - 1, interval, callback); } } } }); } function createCLoudServices(settings, callback) { var numberOfServices = Math.ceil((settings.nodes.length / settings.regionContext.limits.maxRolesPerService)), numberOfVms = settings.nodes.length, cloudServices = [], callbackIndex = 0, errors = []; for (var i = 1; i <= numberOfServices; i++) { var serviceName = 'serviceCreatedByStorm' + i + (new Date().valueOf()), xmlBody = '<CreateHostedService xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<ServiceName>' + serviceName + '</ServiceName>' + '<Label>1234</Label>' + '<Description>description-of-cloud-service</Description>' + '<Location>' + settings.regionContext.cloudRegion + '</Location>' + '<ExtendedProperties>' + '<ExtendedProperty>' + '<Name>' + serviceName + '</Name>' + '</ExtendedProperty>' + '</ExtendedProperties>' + '</CreateHostedService>'; cloudServices.push({id: serviceName, minIndex: (settings.regionContext.limits.maxRolesPerService * (i - 1)), maxIndex: ((settings.regionContext.limits.maxRolesPerService * (i) > numberOfVms) ? numberOfVms - 1 : (settings.regionContext.limits.maxRolesPerService * (i)) - 1)}); var postSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: [201], xmlBody: xmlBody, retryCodes: [307], restType: 'post' }; azureRetryRequest(postSettings, 40, interval, function (err, result) { if (err) { errors.push(err); } callbackIndex += 1; if (callbackIndex === numberOfServices) { if (errors.length > 0) { callback(errors); return; } checkPollCloudServices(settings, settings.regionContext.subscriptionId, settings.regionContext.azureCert, settings.regionContext.azureKey, underscore.pluck(cloudServices, 'id'), 20, interval, function (err, res) { if (err) { callback(err); return; } callback(null, cloudServices); }); } }); } } function uploadCLoudServiceCertificate(settings, cloudService, callback) { var pemFile = settings.regionContext.azureSshPem, xmlBody = '<CertificateFile xmlns="http://schemas.microsoft.com/windowsazure">' + '<Data>' + pemFile + '</Data>' + '<CertificateFormat>pfx</CertificateFormat>' + '<Password></Password>' + '</CertificateFile>', postSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService + '/certificates', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: [202], xmlBody: xmlBody, retryCodes: [307, 409], restType: 'post' }; azureRetryRequest(postSettings, 40, interval, function (err, result) { if (err) { callback(err); return; } callback(null, result); }); } function createCLoudDeployment(settings, cloudService, servicesIndex, callback) { uploadCLoudServiceCertificate(settings, cloudService.id, function (err, res) { if (err) { callback(err); return; } var vmImageName = settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].imageId, instanceType = settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].instanceType, userData, deploymentName = 'deploymentCreatedByStorm' + (new Date().valueOf()), nodeName = 'nodeCreatedByStorm' + (new Date().valueOf()), postSettings, cloudServiceSetting ; if (settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].userData) { userData = new Buffer(JSON.stringify(settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].userData)).toString('base64'); } else { userData = 'IHt9'; } getImageOsType(settings, vmImageName, function (err, res) { var xmlBody, azureStorage; if (err) { callback(err); return; } azureStorage = getAzureStorage(settings.regionContext); if (res === 'Linux') { xmlBody = '<Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<Name>' + deploymentName + '</Name>' + '<DeploymentSlot>Production</DeploymentSlot>' + '<Label>' + deploymentName + '</Label>' + '<RoleList>' + '<Role i:type="PersistentVMRole">' + '<RoleName>' + nodeName + '</RoleName>' + '<RoleType>PersistentVMRole</RoleType>' + '<ConfigurationSets><ConfigurationSet i:type="LinuxProvisioningConfigurationSet">' + '<ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>' + '<HostName>' + nodeName + '</HostName>' + '<UserName>ubuntu</UserName>' + '<UserPassword></UserPassword>' + '<DisableSshPasswordAuthentication>true</DisableSshPasswordAuthentication>' + '<SSH>' + '<PublicKeys>' + '<PublicKey>' + '<Fingerprint>' + settings.regionContext.azureFingerPrint + '</Fingerprint>' + '<Path>/home/azureuser/.ssh/authorized_keys</Path>' + '</PublicKey>' + '</PublicKeys>' + '<KeyPairs>' + '<KeyPair>' + '<Fingerprint>' + settings.regionContext.azureFingerPrint + '</Fingerprint>' + '<Path>/home/azureuser/.ssh/id_rsa</Path>' + '</KeyPair>' + '</KeyPairs>' + '</SSH>' + '<CustomData>' + userData + '</CustomData>' + '</ConfigurationSet>' + '<ConfigurationSet>' + '<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>' + '<InputEndpoints>' + '<InputEndpoint>' + '<LocalPort>22</LocalPort>' + '<Name>SSH</Name>' + '<Port>22</Port>' + '<Protocol>TCP</Protocol>' + '<EndpointAcl>' + '<Rules>' + '<Rule>' + '<Order>0</Order>' + '<Action>permit</Action>' + '<RemoteSubnet>15.0.0.0/8</RemoteSubnet>'+ '<Description>hp-acl-rule1</Description>' + '</Rule>'+ '<Rule>'+ '<Order>1</Order>'+ '<Action>permit</Action>'+ '<RemoteSubnet>16.0.0.0/8</RemoteSubnet>'+ '<Description>hp-acl-rule2</Description>'+ '</Rule>'+ '</Rules>'+ '</EndpointAcl>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>35358</LocalPort>' + '<Name>PORT1</Name>' + '<Port>35358</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>35357</LocalPort>' + '<Name>PORT2</Name>' + '<Port>35357</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>6500</LocalPort>' + '<Name>PORT3</Name>' + '<Port>6500</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>6600</LocalPort>' + '<Name>PORT4</Name>' + '<Port>6600</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '</InputEndpoints>' + '</ConfigurationSet>' + '</ConfigurationSets>' + '<VMImageName>' + vmImageName + '</VMImageName>' + '<RoleSize>' + instanceType + '</RoleSize>' + '</Role>' + '</RoleList>' + '</Deployment>'; } else if (res === 'Windows') { xmlBody = '<Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<Name>' + deploymentName + '</Name>' + '<DeploymentSlot>Production</DeploymentSlot>' + '<Label>' + deploymentName + '</Label>' + '<RoleList>' + '<Role i:type="PersistentVMRole">' + '<RoleName>' + nodeName + '</RoleName>' + '<RoleType>PersistentVMRole</RoleType>' + '<ConfigurationSets>' + '<ConfigurationSet>' + '<ConfigurationSetType>WindowsProvisioningConfiguration</ConfigurationSetType>' + '<ComputerName>storm</ComputerName>' + '<AdminPassword>'+settings.regionContext.azureWindowsRdpPass+'</AdminPassword>' + '<EnableAutomaticUpdates>false</EnableAutomaticUpdates>' + '<AdminUsername>ubuntu</AdminUsername>' + '<CustomData>' + userData + '</CustomData>' + '</ConfigurationSet>' + '<ConfigurationSet>' + '<ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>' + '<InputEndpoints>' + '<InputEndpoint>' + '<LocalPort>22</LocalPort>' + '<Name>SSH</Name>' + '<Port>22</Port>' + '<Protocol>TCP</Protocol>' + '<EndpointAcl>' + '<Rules>' + '<Rule>' + '<Order>0</Order>' + '<Action>permit</Action>' + '<RemoteSubnet>15.0.0.0/8</RemoteSubnet>'+ '<Description>hp-acl-rule1</Description>' + '</Rule>'+ '<Rule>'+ '<Order>1</Order>'+ '<Action>permit</Action>'+ '<RemoteSubnet>16.0.0.0/8</RemoteSubnet>'+ '<Description>hp-acl-rule2</Description>'+ '</Rule>'+ '</Rules>'+ '</EndpointAcl>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>35358</LocalPort>' + '<Name>PORT1</Name>' + '<Port>35358</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>35357</LocalPort>' + '<Name>PORT2</Name>' + '<Port>35357</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>6500</LocalPort>' + '<Name>PORT3</Name>' + '<Port>6500</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>6600</LocalPort>' + '<Name>PORT4</Name>' + '<Port>6600</Port>' + '<Protocol>TCP</Protocol>' + '</InputEndpoint>' + '<InputEndpoint>' + '<LocalPort>3389</LocalPort>' + '<Name>RDP</Name>' + '<Port>3389</Port>' + '<Protocol>TCP</Protocol>' + '<EndpointAcl>' + '<Rules>' + '<Rule>' + '<Order>0</Order>' + '<Action>permit</Action>' + '<RemoteSubnet>15.0.0.0/8</RemoteSubnet>'+ '<Description>hp-acl-rule1</Description>' + '</Rule>'+ '<Rule>'+ '<Order>1</Order>'+ '<Action>permit</Action>'+ '<RemoteSubnet>16.0.0.0/8</RemoteSubnet>'+ '<Description>hp-acl-rule2</Description>'+ '</Rule>'+ '</Rules>'+ '</EndpointAcl>' + '</InputEndpoint>' + '</InputEndpoints>' + '</ConfigurationSet>' + '</ConfigurationSets>' + '<VMImageName>' + vmImageName + '</VMImageName>' + '<RoleSize>' + instanceType + '</RoleSize>' + '<ProvisionGuestAgent>true</ProvisionGuestAgent>' + '</Role>' + '</RoleList>' + '</Deployment>'; } postSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService.id + '/deployments', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: [202], xmlBody: xmlBody, retryCodes: [307, 409, 400, 503], restType: 'post', subscriptionId: settings.regionContext.subscriptionId }; azureValidateRetryRequest(settings, postSettings, 15, operationValidateInterval, function (err, result) { if (err) { cloudServiceSetting = {cloudService: cloudService.id, deployment: deploymentName, deploymentNode: {nodeName: nodeName, launchStatus: 'failed to create deployment', tags: settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].tags}}; callback(err, cloudServiceSetting); return; } cloudServiceSetting = {cloudService: cloudService.id, deployment: deploymentName, deploymentNode: {nodeName: nodeName, launchStatus: 'OK', tags: settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].tags}}; azureStorage.addNodeTagging(settings.regionContext.cloudRegion, nodeName, settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].tags, 'OK', cloudServiceSetting, function (err, tagRetval) { if (err) { cloudServiceSetting = {cloudService: cloudService.id, deployment: deploymentName, deploymentNode: {nodeName: nodeName, launchStatus: 'failed to create node tag', tags: settings.nodes[servicesIndex * settings.regionContext.limits.maxRolesPerService].tags}}; callback('err add node tagging-' + err, cloudServiceSetting); return; } callback(null, cloudServiceSetting); }); }); }); }); } function getCloudServicesByLocation(settings, callback) { var getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: 200 }; azureGetRequest(getSettings, function (err, result) { if (err) { callback(err); return; } var services = underscore.flatten(underscore.pluck(underscore.filter(result.HostedServices.HostedService, function (service) { return (service.HostedServiceProperties[0].Status[0] === 'Created' && service.HostedServiceProperties[0].Location[0] === settings.regionContext.cloudRegion); }), 'ServiceName')); callback(null, services); }); } function margeNodesLists(settings, storageNodes, nodesList, callback) { var azureStorage, finalResults = nodesList, numberOfTagsNodes = storageNodes.length, nodeTagsIndex = 0; azureStorage = getAzureStorage(settings.regionContext); if (numberOfTagsNodes === 0) { callback(null, nodesList); } underscore.forEach(storageNodes, function (nodeResult) { // check if the node exists already from rest API if not we will add it from the storage. if (underscore.contains(underscore.pluck(nodesList.nodes, 'id'), nodeResult.RowKey) === false) { if ((new Date()).getTime() > (Date.parse(nodeResult.Timestamp) + (1000 * 60 * 60 * 2))) { azureStorage.deleteTagging('node', nodeResult.PartitionKey, nodeResult.RowKey, function (err, deleteTagRetval) { if (err) { callback(err, {rawResult: 'failed to delete node-' + nodeResult.RowKey}); return; } }); } else { var tag = {}; underscore.forEach(underscore.filter(underscore.keys(nodeResult), function (key) { return key.indexOf('key') > -1; }), function (key) { tag[nodeResult[key]] = nodeResult['values' + key.substring(4, key.length)]; }); var node = { id: nodeResult.RowKey, status: ((nodeResult.launchStatus === 'OK') ? 'Starting' : 'ERROR_' + nodeResult.launchStatus), addresses: null, tags: tag }; finalResults.nodes.push(node); } } nodeTagsIndex += 1; if (nodeTagsIndex === numberOfTagsNodes) { callback(null, finalResults); return; } }); } function margeImagesLists(settings, storageImages, imageList, callback) { var azureStorage, finalResults = imageList, numberOfTagsImages = storageImages.length, imageTagsIndex = 0; azureStorage = getAzureStorage(settings.regionContext); if (numberOfTagsImages === 0) { callback(null, imageList); } underscore.forEach(storageImages, function (imageResult) { // check if the node exists already from rest API if not we will add it from the storage. if (underscore.contains(underscore.pluck(imageList.images, 'id'), imageResult.RowKey) === false) { if ((new Date()).getTime() > (Date.parse(imageResult.Timestamp) + (1000 * 60 * 60 * 2))) { azureStorage.deleteTagging('image', imageResult.PartitionKey, imageResult.RowKey, function (err, deleteTagRetval) { if (err) { callback(err, {rawResult: 'failed to delete Image Tagging-' + imageResult.RowKey}); return; } }); } else { var tag = {}; underscore.forEach(underscore.filter(underscore.keys(imageResult), function (key) { return key.indexOf('key') > -1; }), function (key) { tag[imageResult[key]] = imageResult['values' + key.substring(4, 5)]; }); var image = { id: imageResult.RowKey, status: 'starting', tags: tag }; finalResults.images.push(image); } } imageTagsIndex += 1; if (imageTagsIndex === numberOfTagsImages) { callback(null, finalResults); return; } }); } function stopNode(settings, cloudService, deployment, node, pollingCount, interval, callback) { var xmlBody = '<ShutdownRoleOperation xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' + '<OperationType>ShutdownRoleOperation</OperationType>' + '</ShutdownRoleOperation>', postSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService + '/deployments/' + deployment + '/roleinstances/' + node + '/Operations', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: [202], xmlBody: xmlBody, retryCodes: [307, 409], restType: 'post' }; azureRetryRequest(postSettings, 40, interval, function (err, result) { if (err) { callback(err); return; } var getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService + '/deployments/' + deployment, xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: 200 }; azureGetRequest(getSettings, function (err, resultNode) { var nodeStatus = underscore.filter(resultNode.Deployment.RoleInstanceList[0].RoleInstance, function (noderec) { return noderec.RoleName[0] === node; })[0].PowerState[0]; if (err) { callback(err); return; } if (nodeStatus === 'Stopped') { setTimeout(function () { callback(null, true); return; }, 20000); } else { if (pollingCount === 0) { callback(new AzureError('max polling for stop Node')); return; } else { setTimeout(stopNode, interval, settings, cloudService, deployment, node, pollingCount - 1, interval, callback); } } } ) ; }); } function getDeploymentNodeList(settings, cloudService, callback) { var azureStorage, DeploymentFinalResults = {nodes: []}, getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService + '/deploymentslots/Production', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: 200 }; azureStorage = getAzureStorage(settings.regionContext); azureGetRequest(getSettings, function (err, resultNodes) { var numberOfNodes, nodeIndex = 0, errors = []; if (resultNodes && resultNodes.Deployment.RoleInstanceList[0].RoleInstance) { var nodeList = resultNodes.Deployment.RoleInstanceList[0].RoleInstance; DeploymentFinalResults.rawResult = nodeList; numberOfNodes = nodeList.length; if (numberOfNodes === 0) { callback(null, DeploymentFinalResults); return; } underscore.forEach(nodeList, function (nodeResult) { azureStorage.getNodeTagging(settings.regionContext.cloudRegion, nodeResult.RoleName[0], function (err, tagRetval) { var tagging, vIp; if (err) { // errors.push(err); tagging = {}; } if (!tagRetval) { tagging = {}; } else { tagging = tagRetval.finalTagging; } if (nodeResult.InstanceEndpoints) { vIp = nodeResult.InstanceEndpoints[0].InstanceEndpoint[0].Vip[0]; } var node = { id: nodeResult.RoleName[0], status: ((nodeResult.PowerState[0] === 'Started' && (!nodeResult.GuestAgentStatus || (nodeResult.GuestAgentStatus && nodeResult.GuestAgentStatus[0].Status[0]==='Ready')))? 'ACTIVE' : nodeResult.PowerState[0]), addresses: [(!nodeResult.IpAddress?null:nodeResult.IpAddress[0]), vIp], tags: tagging }; DeploymentFinalResults.nodes.push(node); nodeIndex += 1; if (nodeIndex === numberOfNodes) { callback(underscore.without(errors, ''), DeploymentFinalResults); return; } }); }); } else { callback(null, DeploymentFinalResults); return; } }); } function deleteService(settings, cloudService, deployment, callback) { getDeploymentNodeList(settings, cloudService, function (err, result) { if (err && (err[0])) { callback(err); return; } if (underscore.filter(result.nodes,function (node) { return (node.status !== 'Stopped'); }).length === 0) { var DelSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService+'?comp=media', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: [202], retryCodes: [307, 409] }; // delete the image cloud service azureRetryDeleteRequest(DelSettings, 40, interval, function (err, delResService) { if (err) { callback(err); return; } callback(null, true); return; }); } else { callback(null, true); return; } }); } function getNodePIp(settings, cloudService, deployment, nodeId, pollingCount, interval, callback) { var nodeCheck, PIp, getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + cloudService + '/deployments/' + deployment, xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: 200 }; azureGetRequest(getSettings, function (err, resultNodes) { if (err) { callback(err); return; } if (resultNodes.Deployment.RoleInstanceList[0] && resultNodes.Deployment.RoleInstanceList[0].RoleInstance) { nodeCheck = underscore(resultNodes.Deployment.RoleInstanceList[0].RoleInstance, function (node) { return node.RoleName[0] === nodeId; }); if (!nodeCheck) { setTimeout(getNodePIp, interval, settings, cloudService, deployment, nodeId, pollingCount - 1, interval, callback); } else { PIp = nodeCheck._wrapped[0].InstanceEndpoints[0].InstanceEndpoint[0].Vip[0]; callback(null, PIp); return; } } else { setTimeout(getNodePIp, interval, settings, cloudService, deployment, nodeId, pollingCount - 1, interval, callback); } }); } function getImageOsType(settings, imageName, callback) { var imageInfo, getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/vmimages?location=' + settings.regionContext.cloudRegion, xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: 200 }; azureGetRequest(getSettings, function (err, resultImages) { var img; if (err) { callback(err); return; } img = underscore.filter(resultImages.VMImages.VMImage, function (image) { return image.Name[0] === imageName; }); imageInfo = underscore.filter(resultImages.VMImages.VMImage, function (image) { return image.Name[0] === imageName; })[0].OSDiskConfiguration[0].OS[0]; callback(null, imageInfo); return; }); } function getNodeInfo(settings, nodeId, callback) { var azureStorage; azureStorage = getAzureStorage(settings.regionContext); azureStorage.getNodeTagging(settings.regionContext.cloudRegion, nodeId, function (err, tagRetval) { if (err || !tagRetval) { getCloudServicesByLocation(settings, function (err, res) { var numberOfCloudService, cloudServicIndex = 0, errors = []; if (err || underscore.isEmpty(res)) { callback([err], {}); return; } numberOfCloudService = res.length; underscore.forEach(res, function (resCloudService) { var getSettings = { url: 'https://' + settings.regionContext.apiPrefix + '/' + settings.regionContext.subscriptionId + '/services/hostedservices/' + resCloudService + '/deploymentslots/Production', xMsVersion: xMsVersion, azureCert: settings.regionContext.azureCert, azureKey: settings.regionContext.azureKey, successCode: 200 }; azureGetRequest(getSettings, function (err, resultNodes) { if (resultNodes) { if (underscore.contains(underscore.flatten(underscore.flatten(underscore.pluck(resultNodes.Deployment.RoleInstanceList[0].RoleInstance, 'RoleName'))), nodeId) === true) { callback([null], {cloudService: resCloudService, deployment: resultNodes.Deployment.Name[0], tagCheck: false}); return; } } cloudServicIndex += 1; if (cloudServicIndex === numberOfCloudService) { if (errors.length > 0) { callback(errors, {}); return; } callback([new AzureError('node doesn\'t exist')]); } }); }); }); } else { callback([null], {cloudService: tagRetval.cloudService, deployment: tagRetval.deployment, tagCheck: true}); return; } }); } var that = { setProxy: function (proxyUrl) { tunnelingProxyURL = proxyUrl; }, createRegionContext: function (regionAuthSettings, regionLimits) { var apiPrefix; apiPrefix = 'management.core.windows.net'; regionAuthSettings = regionAuthSettings || {}; if (regionAuthSettings.cloudRegion === 'China East' || regionAuthSettings.cloudRegion === 'China North') { apiPrefix = 'management.core.chinacloudapi.cn'; } return { cloudRegion: regionAuthSettings.cloudRegion, azureCert: regionAuthSettings.azureCert, azureKey: regionAuthSettings.azureKey, subscriptionId: regionAuthSettings.subscriptionId, limits: regionLimits, pollingCount: 60, azureSshPem: regionAuthSettings.azureSshPem, azureFingerPrint: regionAuthSettings.azureFingerPrint, azureWindowsRdpPass: regionAuthSettings.azureWindowsRdpPass, providerName: 'azure', apiPrefix: apiPrefix, storageAccount: regionAuthSettings.storageAccount, storageAccessKey: regionAuthSettings.storageAccessKey }; }, createPreparation: function (settings, callback) { var azureStorage; azureStorage = getAzureStorage(settings.regionContext); azureStorage.createTable('nodesTagging', function (err, res) { if (err) { callback(err); return; } azureStorage.createTable('imageTagging', function (err, res) { if (err) { callback(err); return; } azureStorage.createTable('nodesIps', function (err, res) { if (err) { callback(err); return; } var cloudServices = []; createCLoudServices(settings, function (err, result) { if (err) { callback(err); return; } var numberOfServices = Math.ceil((settings.nodes.length / settings.regionContext.limits.maxRolesPerService)), servicesIndex = 0, errors = []; underscore.forEach(result, function (cloudService) { var newCloudService; createCLoudDeployment(settings, cloudService, result.indexOf(cloudService), function (err, result) { if (err) { errors.push(err); } newCloudService = {id: cloudService.id, minIndex: cloudService.minIndex, maxIndex: cloudService.maxIndex, deployment: result.deployment, deploymentNode: result.deploymentNode }; cloudServices.push(newCloudService); servicesIndex += 1; if (servicesIndex === (numberOfServices)) { if (errors.length > 0) { callback(errors, cloudServices); return; } else { console.log('final cloudServices+deployments-' + JSON.stringify(cloudServices)); callback(null, cloudServices); } } }); }); }); }); }); }); }, listNodes: function (settings, callback) { var finalResults = {rawResult: {}, nodes: []}, azureStorage; azureStorage = getAzureStorage(settings.regionContext); getCloudServicesByLocation(settings, function (err, res) { var numberOfCloudService, cloudServicIndex = 0, errors = []; if (err || underscore.isEmpty(res)) { callback(err, {}); return; } numberOfCloudService = res.length; underscore.forEach(res, function (cloudService) { getDeploymentNodeList(settings, cloudService, function (err, deploymentRes) { if (err && (err[0])) { errors.push(err); } finalResults.rawResult = underscore.extend(finalResults.rawResult, deploymentRes.rawResult); finalResults.nodes = underscore.union(finalResults.nodes, deploymentRes.nodes); cloudServicIndex += 1; if (cloudServicIndex === numberOfCloudService) { if (errors.length > 0) { callback(errors, {}); return; } azureStorage.getNodes(settings.regionContext.cloudRegion, function (error, resultNodes) { if (error) { callback(err, {}); return; } margeNodesLists(settings, resultNodes, finalResults, function (err, res) { if (err) { callback(err, {}); return; } callback(null, res); }); }); } }); }); }); }, createNode: function (settings, cloudServicesTestSettings, nodeIndex, callback) { var resultNode, userData, launchStatus, cloudService = underscore.filter(cloudServicesTestSettings, function (service) { return (nodeIndex >= service.minIndex && nodeIndex <= service.maxIndex); }), nodeName = 'nodeCreatedByStorm' + (new Date().valueOf()), xmlBody, azureStorage; azureStorage = getAzureStorage(settings.regionContext); if (settings.nodeParams.userData) { userDa