cloud-blender
Version:
A high level library for cloud compute operations
1,058 lines (908 loc) • 51.9 kB
JavaScript
/*jslint node: true */
'use strict';
var request = require('request'),
underscore = require('underscore'),
xMsVersion = '2015-05-01-preview',
rgMsVersion = '2016-02-01',
templateMsVersion = '2016-02-01',
deleteMsVersion = '2016-03-30',
AzureError = require('./azure-error.js'),
Authentication = {},
adal = require('adal-node'),
tunnelingProxyURL,
interval = 11000,
azure = require('azure-storage'),
CBError = require('./cb-error'),
CBErrorCodes = require('./cb-error-codes'),
foldersStructure = 'Microsoft.Compute/Images/images/';
module.exports = (function () {
function checkReachedFinishedState(settings, code, maxNumOfIterations, callback) {
if (maxNumOfIterations === 0) {
return callback(new CBError('can\'t verify if operation finished'));
}
azureRequest('GET', settings, function (err, taskResult) {
if (err) {
return callback(new CBError(err.message, err, 1, true, CBErrorCodes.UNSPECIFIED_ERROR));
}
if (taskResult.statuses[1].code === code) {
return callback(null, taskResult);
} else {
setTimeout(checkReachedFinishedState, 4000, settings, code, maxNumOfIterations - 1, callback);
}
});
}
function getAuthenticationToken(settings, callback) {
if (Authentication.expiresOn && (new Date().valueOf()) + (1000 * 60 * 10) < Authentication.expiresOn) {
callback(null, Authentication.token);
return;
}
else {
var AuthenticationContext = adal.AuthenticationContext,
tenantID = settings.tenantId,
clientID = settings.clientId,
resource = "https://management.azure.com/",
authURL = "https://login.windows.net/" + tenantID,
secret = settings.secret,
context = new AuthenticationContext(authURL);
context.acquireTokenWithClientCredentials(resource, clientID, secret, function (err, tokenResponse) {
if (err) {
callback(new AzureError('err generate token-' + err));
return;
}
else {
Authentication.token = tokenResponse.accessToken;
Authentication.expiresOn = tokenResponse.expiresOn.valueOf();
callback(null, tokenResponse.accessToken);
}
});
}
}
function checkTemplateStatus(settings, templateResult, maxNumOfIterations, callback) {
if (maxNumOfIterations === 0) {
var correlationId = 0;
if (templateResult && templateResult.properties && templateResult.properties.correlationId){
correlationId = templateResult.properties.correlationId;
}
return callback(new CBError('can\'t check if template id: '+ templateResult.id + ' status is finished. correlation id: '+ correlationId, 'finished number of iteratation, still template deployment is running', 1, false, CBErrorCodes.AZURE_ARM_DEPLOYMENT_ERROR));
}
var getTemplateInfo = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/' + templateResult.id + '?api-version=' + templateMsVersion,
successCode: 200
};
azureRequest('GET', getTemplateInfo, function (err, templateInfo) {
if (err) {
return callback(new CBError(err.message, err, 1, true, CBErrorCodes.AZURE_ARM_DEPLOYMENT_ERROR));
}
if (templateInfo.properties.provisioningState === 'Succeeded' || templateInfo.properties.provisioningState === 'Ready') {
return callback(null, templateInfo);
} else if (templateInfo.properties.provisioningState === 'Failed' || templateInfo.properties.provisioningState === 'Canceled') {
return callback(new CBError(templateInfo.properties.error.message, templateInfo.properties.error, templateInfo.properties.error.code, false, CBErrorCodes.AZURE_ARM_DEPLOYMENT_ERROR));
} else {
setTimeout(checkTemplateStatus, 4000, settings, templateResult, maxNumOfIterations - 1, callback);
}
});
}
function checkDeleteStatus(settings, deleteResult, maxNumOfIterations, callback) {
if (maxNumOfIterations === 0) {
return callback(new CBError('can\'t check if vm is deleted', 'finished number of iteratation, the vm is not deleted still', 1, true));
}
var getDeleteResult = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/' + deleteResult.id + '?api-version=' + deleteMsVersion,
successCode: 404
};
azureRequest('GET', getDeleteResult, function (err, deleteStatus) {
if (err) {
setTimeout(checkDeleteStatus, 4000, settings, deleteResult, maxNumOfIterations - 1, callback);
return;
}
return callback(null, deleteStatus);
});
}
function azureRetryRequest(method, reqSettings, pollingCount, interval, callback) {
var Settings, Authorization;
getAuthenticationToken(reqSettings.regionContext, function (err, result) {
if (err) {
callback(new AzureError(err));
return;
}
Authorization = result;
Settings = {
method: method,
url: reqSettings.url,
headers: {
Authorization: 'Bearer ' + Authorization,
'Content-Type': 'application/json'
},
body: JSON.stringify(reqSettings.jsonBody)
};
request(Settings, function (err, response, body) {
if (err || !response) {
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 && ((response.statusCode === 503) || (response.statusCode === 429) ||
(underscore.contains(reqSettings.retryCodes, response.statusCode) === true))) {
setTimeout(azureRetryRequest, interval, method, reqSettings, pollingCount - 1, interval, callback);
return;
}
if ((reqSettings.successCode === response.statusCode) ||
(underscore.contains(reqSettings.successCode, response.statusCode) === true)) {
if (method.toUpperCase() === 'DELETE' || method.toUpperCase() === 'POST') {
callback(null, response.statusCode);
return;
}
else {
callback(null, JSON.parse(response.body));
return;
}
}
// in case we got an error code which is not success/retry
callback(new AzureError('res.statusCode - ' + response.statusCode + ' ' + response.body));
});
});
}
function azureRequest(method, reqSettings, callback) {
var Settings, Authorization;
getAuthenticationToken(reqSettings.regionContext, function (err, result) {
if (err) {
callback(new AzureError(err));
return;
}
Authorization = result;
Settings = {
method: method,
url: reqSettings.url,
headers: {
Authorization: 'Bearer ' + Authorization,
'Content-Type': 'application/json'
},
body: JSON.stringify(reqSettings.jsonBody)
};
request(Settings, function (err, response, body) {
if (!response) {
callback(new AzureError('response is not valid-' + err));
return;
}
if (err || response.statusCode !== reqSettings.successCode) {
callback(err || new AzureError('res.statusCode-' + response.statusCode + ' ' + response.body));
return;
}
if (method.toUpperCase() === 'DELETE' || method.toUpperCase() === 'POST') {
callback(null, reqSettings.successCode);
return;
}
else {
callback(null, JSON.parse(response.body));
return;
}
});
});
}
function getBlobsList(regionContext, container, imagesArray, foldersPrefix, callback) {
var blobSvc = azure.createBlobService(regionContext.storageAccount, regionContext.storageAccessKey), blobFullName, images = [];
if(imagesArray.length > 0) {
underscore.each(imagesArray, function (image, index) {
blobSvc.listBlobsSegmentedWithPrefix(container, foldersPrefix + image, null, function (error, result, response) {
if (error) {
return callback(error);
}
if (result && (result.entries.length === 1)) {
result.entries[0].Name = result.entries[0].name;
result.entries[0].Properties = {};
result.entries[0].Properties['Last-Modified'] = result.entries[0].lastModified;
images.push(result.entries[0]);
}
if (index === imagesArray.length - 1) {
callback(null, images);
}
});
});
}
else{
blobSvc.listBlobsSegmentedWithPrefix(container, foldersPrefix, null, function(error, result) {
if (error){
return callback(error);
}
if(result.continuationToken != null)
{
aggregateBlobs(result.entries, regionContext, container, foldersPrefix, result.continuationToken, function (error, results){
if (error){
return callback(error);
}
fillImagesList(results, images,callback);
});
}
else{
fillImagesList(result.entries, images,callback);
}
});
}
}
function aggregateBlobs(entries, regionContext, container, foldersPrefix, continuationToken, callback) {
var blobSvc = azure.createBlobService(regionContext.storageAccount, regionContext.storageAccessKey);
blobSvc.listBlobsSegmentedWithPrefix (container, foldersPrefix, continuationToken, function (error, result){
if(error){
callback(error, null);
}
entries = entries.concat(result.entries);
if(result.continuationToken !== null) {
aggregateBlobs(entries, regionContext, container, foldersPrefix, result.continuationToken, callback);
}
else{
callback(null, entries);
}
} );
}
function fillImagesList(entries, images, callback){
underscore.each(entries, function(image){
image.Name = image.name;
image.Properties = {};
image.Properties['Last-Modified'] = image.lastModified;
images.push(image);
});
callback(null, images);
}
function getBlobFullName(regionContext, container, BlobPrefix, foldersPrefix, numberOfRetry, callback) {
var blobSvc = azure.createBlobService(regionContext.storageAccount, regionContext.storageAccessKey), blobFullName;
blobSvc.listBlobsSegmentedWithPrefix(container, foldersPrefix + BlobPrefix, null, function (error, result, response) {
if (error) {
return callback(error);
}
if (result && (result.entries.length === 1)) {
blobFullName = result.entries[0].name;
return callback(null, blobFullName.substr(foldersPrefix.length));
} else if (result && (result.entries.length > 1)) {
var newArr = result.entries.filter(function (entry) {
return (entry.blobType === 'PageBlob');
});
if (newArr.length === 1) {
blobFullName = newArr[0].name;
return callback(null, blobFullName.substr(foldersPrefix.length));
} else {
return callback(new AzureError('looking for: ' + foldersPrefix + BlobPrefix + ' in container: ' + container + '. found ' + result.entries.length + ' images by its prefix: ' + JSON.stringify(result.entries)));
}
} else {
if (numberOfRetry === 0) {
return callback(new AzureError('looking for: ' + foldersPrefix + BlobPrefix + ' in container: ' + container + '. can\'t find image by its prefix'));
} else {
setTimeout(getBlobFullName, 2000, regionContext, container, BlobPrefix, foldersPrefix, numberOfRetry - 1, callback);
}
}
});
}
function deleteBlob(regionContext, container, Blob, pollingCount, interval, callback) {
var blobSvc = azure.createBlobService(regionContext.storageAccount, regionContext.storageAccessKey);
blobSvc.deleteBlob(container, Blob, function (error, response) {
if (!response) {
callback(new AzureError('delete blob response is not valid-' + error));
return;
}
// in case of retry code and there we didn't reached to max polling
if (pollingCount > 0 && response.statusCode === 412) {
setTimeout(deleteBlob, interval, regionContext, container, Blob, pollingCount - 1, interval, callback);
}
// in case we got an error code which is not success/retry
if ((pollingCount === 0 && response.statusCode === 412) || (response.statusCode !== 412 && response.statusCode !== 202)) {
callback(error || new AzureError('error delete blob res.statusCode-' + response.statusCode + ' ' + response.body));
return;
}
if (response.statusCode === 202) {
callback(null, response.statusCode);
return;
}
});
}
function addNodeIp(partition, id, ip, callback) {
var tableService = azure.createTableService();
var entGen = azure.TableUtilities.entityGenerator,
entity = {};
entity.PartitionKey = entGen.String(partition);
entity.RowKey = entGen.String('_' + id);
entity.ip = entGen.String(ip);
tableService.insertEntity('nodesIps', entity, function (error, result, response) {
if (error) {
callback(error);
return;
}
callback(null, true);
});
}
function getVmIp(settings, vmName, pollingCount, interval, callback) {
that.listNodes(settings, function (err, res) {
var ip, vm;
if (underscore.isEmpty(res) || underscore.isEmpty(res.nodes)) {
if (pollingCount > 0) {
setTimeout(getVmIp, interval, settings, vmName, pollingCount - 1, interval, callback);
} else {
callback(new AzureError('failed to find public ip for ' + vmName));
}
return;
}
vm = underscore.filter(res.nodes, function (node) {
return node.id === vmName;
});
if (vm && vm[0] && vm[0].addresses) {
ip = vm[0].addresses[1];
}
if (ip) {
callback(null, ip);
return;
}
if (pollingCount > 0) {
setTimeout(getVmIp, interval, settings, vmName, pollingCount - 1, interval, callback);
return;
}
callback(new AzureError('VM ip to-' + vmName + ' is missing'));
});
}
function deleteNetworkInterface(settings, callback) {
var settingsNetwork = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/networkInterfaces?api-version=' + xMsVersion,
successCode: 200
},
settingsVM = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines?api-version=' + xMsVersion,
successCode: 200
}, vmNetworks, deleteINterval = (1000 * 60 * 10), settingsDelNetwork, errors = [], index = 0;
azureRequest('GET', settingsNetwork, function (err, resNetwork) {
if (err) {
callback([err]);
return;
}
azureRequest('GET', settingsVM, function (err, resVm) {
if (err) {
callback([err]);
return;
}
vmNetworks = underscore.map(resVm.value, function (vm) {
return vm.properties.networkProfile.networkInterfaces[0].id;
});
if (resNetwork.value.length === 0) {
callback([]);
return;
}
underscore.each(resNetwork.value, function (network) {
// all the network interface without VM and which were not created in the last 10 minutes
if (vmNetworks.indexOf(network.id) === -1 && (!network.tags || !network.tags.createTime || (parseInt(network.tags.createTime) + deleteINterval) < new Date().valueOf())) {
console.log('network interface-' + network.id + 'on region-' + settings.regionContext.cloudRegion + ' will be deleted');
settingsDelNetwork = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/networkInterfaces/' + network.name + '?api-version=' + xMsVersion,
successCode: 202
};
azureRequest('DELETE', settingsDelNetwork, function (err, resVm) {
index += 1;
if (err) {
errors.push(err);
}
if (index === resNetwork.value.length) {
callback(errors);
return;
}
});
}
else {
index += 1;
if (index === resNetwork.value.length) {
callback(errors);
return;
}
}
});
});
});
}
function deleteIp(settings, callback) {
var settingsIps = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/publicIPAddresses?api-version=' + xMsVersion,
successCode: 200
},
settingsNetwork = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/networkInterfaces?api-version=' + xMsVersion,
successCode: 200
}, networkIps, deleteINterval = (1000 * 60 * 10), settingsDelIp, index = 0, errors = [];
azureRequest('GET', settingsIps, function (err, resIps) {
if (err) {
callback([err]);
return;
}
azureRequest('GET', settingsNetwork, function (err, resNetwork) {
if (err) {
callback([err]);
return;
}
networkIps = underscore.map(resNetwork.value, function (network) {
return network.properties.ipConfigurations[0].properties.publicIPAddress.id;
});
if (resIps.value.length === 0) {
callback([]);
return;
}
underscore.each(resIps.value, function (ip) {
// all the network interface without VM and which were not created in the last 10 minutes
if (networkIps.indexOf(ip.id) === -1 && (!ip.tags || !ip.tags.createTime || (parseInt(ip.tags.createTime) + deleteINterval) < new Date().valueOf())) {
console.log('public IP-' + ip.id + 'on region-' + settings.regionContext.cloudRegion + ' will be deleted');
settingsDelIp = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/publicIPAddresses/' + ip.name + '?api-version=' + xMsVersion,
successCode: 202
};
azureRequest('DELETE', settingsDelIp, function (err, resVm) {
index += 1;
if (err) {
errors.push(err);
}
if (index === resIps.value.length) {
callback(errors);
return;
}
});
}
else {
index += 1;
if (index === resIps.value.length) {
callback(errors);
return;
}
}
});
});
});
}
function deleteDiscs(settings, callback) {
var settingsVM = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines?api-version=' + xMsVersion,
successCode: 200
}, deleteINterval = (1000 * 60 * 10), VMdiscs, index = 0, errors = [];
azureRequest('GET', settingsVM, function (err, resVm) {
if (err) {
callback([err]);
return;
}
getBlobsList(settings.regionContext, 'vhds', function (err, resBlob) {
VMdiscs = underscore.map(resVm.value, function (Vm) {
return Vm.properties.storageProfile.osDisk.vhd.uri.substring(Vm.properties.storageProfile.osDisk.vhd.uri.lastIndexOf('/') + 1);
});
if (!resBlob) {
callback([]);
return;
}
if (resBlob.Name) {
resBlob = [resBlob];
}
underscore.each(resBlob, function (blob) {
if (VMdiscs.indexOf(blob.Name) === -1) {
console.log('disc-' + blob.Name + 'on region-' + settings.regionContext.cloudRegion + ' will be deleted');
deleteBlob(settings.regionContext, 'vhds', blob.Name, 3, interval, function (err, resDelete) {
index += 1;
if (err) {
errors.push(err);
}
if (index === resBlob.length) {
callback(errors);
return;
}
});
}
else {
index += 1;
if (index === resBlob.length) {
callback(errors);
return;
}
}
});
});
});
}
function deleteDeployment (settings, deployName){
var settingsDelDeploy = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/microsoft.resources/deployments/' + deployName + '?api-version=' + templateMsVersion,
successCode: 202
};
azureRequest('DELETE', settingsDelDeploy, function (err, resDelDeploy) {
if (err) {
return;
}
});
}
var that = {
setProxy: function (proxyUrl) {
tunnelingProxyURL = proxyUrl;
},
createRegionContext: function (regionAuthSettings, regionLimits) {
return {
subscriptionId: regionAuthSettings.subscriptionId,
cloudRegion: regionAuthSettings.cloudRegion,
limits: regionLimits,
pollingCount: 60,
providerName: 'azure_v2',
groupId: regionAuthSettings.groupId,
tenantId: regionAuthSettings.tenantId,
clientId: regionAuthSettings.clientId,
secret: regionAuthSettings.secret,
imagesContainer: regionAuthSettings.imagesContainer,
vhdsContainer: regionAuthSettings.vhdsContainer,
storageAccount: regionAuthSettings.storageAccount,
storageAccessKey: regionAuthSettings.storageAccessKey,
keyData: regionAuthSettings.keyData,
WinRdpPassword: regionAuthSettings.WinRdpPassword,
networkSecurityGroup: regionAuthSettings.networkSecurityGroup,
networkSecurityResourceGroup: regionAuthSettings.networkSecurityResourceGroup
};
},
createPreparation: function (settings, callback) {
if (!settings.vnetName) {
var deploymentName = 'deployment' + (new Date().valueOf()),
deploymentTemplate = require('../templates/arm-deployment.js')(),
deploymentTemplateParams = require('../templates/arm-deployment-params.js')(),
deploymentSettings = {
regionContext: settings.regionContext,
jsonBody: {
properties: {
template: deploymentTemplate,
mode: 'Incremental',
parameters: deploymentTemplateParams,
debugSetting: {
detailLevel: 'requestContent, responseContent'
}
}
},
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourcegroups/' + settings.regionContext.groupId + '/providers/microsoft.resources/deployments/' + deploymentName + '?api-version=' + templateMsVersion,
successCode: 201
};
deploymentTemplateParams.vnetName.value = 'vnet' + (new Date().valueOf());
deploymentTemplateParams.nsgResourceGroup.value = settings.regionContext.networkSecurityResourceGroup;
deploymentTemplateParams.nsgName.value = settings.regionContext.networkSecurityGroup;
azureRequest('PUT', deploymentSettings, function (err, templateResult) {
if (err) {
return callback(new CBError(err.message, {
rawResult: {},
node: {}
}, 1, true, CBErrorCodes.AZURE_ARM_DEPLOYMENT_ERROR));
}
function vnetReadyCallback(err, result) {
deleteDeployment(settings, deploymentName);
if (err) {
err.isFatal = true;
if (err.errorList && err.errorList.length > 0){
err.errorList[0].isFatal = true;
}
return callback(err);
}
settings.vnetName = result.properties.parameters.vnetName.value;
return callback(null, settings.vnetName);
}
setTimeout(checkTemplateStatus, 4000, settings, templateResult, 225, vnetReadyCallback);
});
} else {
callback(null, settings.vnetName);
}
},
createNode: function (settings, cloudServicesTestSettings, nodeIndex, callback) {
var nodeName = 'vm' + (new Date().valueOf()), settingsVmLinux, settingsVmLinuxParams, settingsVmWindows, settingsVmWindowsParams,
userData, osType, vnetName, deploymentSettings;
if (cloudServicesTestSettings) {
vnetName = cloudServicesTestSettings;
} else {
vnetName = settings.vnetName;
}
if (settings.nodeParams.userData) {
userData = new Buffer(JSON.stringify(settings.nodeParams.userData)).toString('base64');
} else {
userData = 'IHt9';
}
settingsVmLinux = require('../templates/arm-create-linux-vm.js')();
settingsVmLinuxParams = require('../templates/arm-create-linux-vm-params.js')();
settingsVmWindows = require('../templates/arm-create-win-vm.js')();
settingsVmWindowsParams = require('../templates/arm-create-win-vm-params.js')();
osType = (settings.nodeParams.tags.environment.indexOf('windows') !== -1 ? 'windows' : 'linux');
deploymentSettings = {
regionContext: settings.regionContext,
jsonBody: {
properties: {
template: osType === 'windows' ? settingsVmWindows : settingsVmLinux,
mode: 'Incremental',
parameters: osType === 'windows' ? settingsVmWindowsParams : settingsVmLinuxParams,
debugSetting: {
detailLevel: 'requestContent, responseContent'
}
}
},
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourcegroups/' + settings.regionContext.groupId + '/providers/microsoft.resources/deployments/' + nodeName + '?api-version=' + templateMsVersion,
successCode: 201
};
if (osType === 'windows') {
settingsVmWindowsParams.adminPassword.value = settings.regionContext.WinRdpPassword;
settingsVmWindowsParams.storageAccountName.value = settings.regionContext.storageAccount;
settingsVmWindowsParams.customData.value = userData;
settingsVmWindowsParams.osImageVhdUri.value = settings.regionContext.imagesContainer + settings.nodeParams.imageId;
// settingsVmWindowsParams.tags.value = settings.nodeParams.tags;
//Tagging done that way because of Microsoft limitations
//"Currently, Resource Manager does not support processing an object for the tag names and values..." (https://azure.microsoft.com/en-us/documentation/articles/resource-group-using-tags/)
settingsVmWindows.resources[2].tags = settings.nodeParams.tags;
settingsVmWindowsParams.vmName.value = nodeName;
settingsVmWindowsParams.vmSize.value = settings.nodeParams.instanceType;
settingsVmWindowsParams.vnetName.value = vnetName;
settingsVmWindowsParams.vnetResourceGroup.value = settings.regionContext.groupId;
} else {
settingsVmLinuxParams.storageAccountName.value = settings.regionContext.storageAccount;
settingsVmLinuxParams.customData.value = userData;
settingsVmLinuxParams.keyData.value = settings.regionContext.keyData;
settingsVmLinuxParams.osImageVhdUri.value = settings.regionContext.imagesContainer + settings.nodeParams.imageId;
// settingsVmLinuxParams.tags.value = settings.nodeParams.tags;
//Tagging done that way because of Microsoft limitations
//"Currently, Resource Manager does not support processing an object for the tag names and values..." (https://azure.microsoft.com/en-us/documentation/articles/resource-group-using-tags/)
settingsVmLinux.resources[2].tags = settings.nodeParams.tags;
settingsVmLinuxParams.vmName.value = nodeName;
settingsVmLinuxParams.vmSize.value = settings.nodeParams.instanceType;
settingsVmLinuxParams.vnetName.value = vnetName;
settingsVmLinuxParams.vnetResourceGroup.value = settings.regionContext.groupId;
}
azureRequest('PUT', deploymentSettings, function (err, templateResult) {
if (err) {
return callback(new CBError(err.message, err, 1, true, CBErrorCodes.AZURE_ARM_DEPLOYMENT_ERROR));
}
function intermidiateCallback(err, success) {
var resultNode = {
rawResult: 'node-' + nodeName + ' was created.',
node: {
id: nodeName,
status: 'Starting',
addresses: null,
tags: settings.nodeParams.tags,
vnetName: vnetName
}
};
deleteDeployment(settings, nodeName);
if (err) {
return callback(err, resultNode);
}
callback(null, resultNode);
}
setTimeout(checkTemplateStatus, 4000, settings, templateResult, 350, intermidiateCallback);
});
},
listNodes: function (settings, callback) {
var finalResults = {rawResult: {}, nodes: []},
settingsListVms = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines?api-version=' + xMsVersion,
successCode: 200
},
settingsNetworkInterfaces = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/networkInterfaces?api-version=' + xMsVersion,
successCode: 200
},
settingsPublicIpAddresses = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Network/publicIPAddresses?api-version=' + xMsVersion,
successCode: 200
};
azureRetryRequest('GET', settingsListVms, 3, interval, function (err, resListVms) {
if (err) {
callback(err, finalResults);
return;
}
if (underscore.isEmpty(resListVms)) {
callback(undefined, finalResults);
return;
}
azureRetryRequest('GET', settingsNetworkInterfaces, 3, interval, function (err, resListNetworkInterfaces) {
if (err) {
callback(err, finalResults);
return;
}
azureRetryRequest('GET', settingsPublicIpAddresses, 3, interval, function (err, resListIpAddresses) {
if (err) {
callback(err, finalResults);
return;
}
underscore.each(resListVms.value, function (vm) {
if (vm) {
//exclude deleted/deleting vms
if (vm.properties && ((vm.properties.provisioningState === 'Deleting') || (vm.properties.provisioningState === 'Deleted'))) {
return;
}
}
var network,
publicIP,
node = {
id: vm.name,
status: (vm.properties.provisioningState === 'Succeeded' ? 'ACTIVE' : vm.properties.provisioningState),
tags: vm.tags,
addresses: []
};
if (node.status === 'Failed') {
node.status = 'ERROR (Failed)';
}
if ((resListNetworkInterfaces && !underscore.isEmpty(resListNetworkInterfaces.value)) &&
(resListIpAddresses && !underscore.isEmpty(resListIpAddresses.value))) {
network = underscore.find(resListNetworkInterfaces.value, function (network) {
return network.id === vm.properties.networkProfile.networkInterfaces[0].id;
});
if (network && network.properties && network.properties.ipConfigurations[0] &&
network.properties.ipConfigurations[0].properties && network.properties.ipConfigurations[0].properties.publicIPAddress) {
publicIP = underscore.find(resListIpAddresses.value, function (ip) {
return ip.id === network.properties.ipConfigurations[0].properties.publicIPAddress.id;
});
if (publicIP) {
node.addresses = [network.properties.ipConfigurations[0].properties.privateIPAddress,
publicIP.properties.ipAddress
];
}
}
}
finalResults.nodes.push(node);
});
finalResults.rawResult = resListVms;
callback(null, finalResults);
});
});
});
},
deleteNode: function (settings, callback) {
if (!settings.node) {
callback(null, {rawResult: 'no node to delete'});
return;
}
var settingsVm = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines/' + settings.node.id + '?api-version=' + deleteMsVersion
}, settingsGetVm = underscore.extend({successCode: 200}, settingsVm),
settingsDelVm = underscore.extend({successCode: 202}, settingsVm),
settingsDelNic, settingDelIP, vmNic, vmOsDisc, settingsGetNic;
azureRequest('GET', settingsGetVm, function (err, resVm) {
if (err) {
callback(err, {rawResult: {}});
return;
}
vmNic = resVm.properties.networkProfile.networkInterfaces[0].id;
settingsGetNic = {
regionContext: settings.regionContext,
url: 'https://management.azure.com' + vmNic + '?api-version=' + deleteMsVersion,
successCode: 200
};
callback(null, {rawResult: {}});
azureRequest('GET', settingsGetNic, function (err, resNic) {
if (err) {
callback(err, {rawResult: {}});
return;
}
var vmNicIp = resNic.properties.ipConfigurations[0].properties.publicIPAddress.id;
var vnetStr = resNic.properties.ipConfigurations[0].properties.subnet.id.indexOf('/subnets/');
var vmVnet = resNic.properties.ipConfigurations[0].properties.subnet.id.substr(0, vnetStr);
var settingsGetVnet = {
regionContext: settings.regionContext,
url: 'https://management.azure.com' + vmVnet + '?api-version=' + deleteMsVersion,
successCode: 200
};
azureRequest('DELETE', settingsDelVm, function (err, resDelVm) {
if (err) {
callback(err, {rawResult: {}});
return;
}
function deleteRestOfIt(error, success) {
if (error) {
return;
}
vmOsDisc = resVm.properties.storageProfile.osDisk.vhd.uri.substring(resVm.properties.storageProfile.osDisk.vhd.uri.lastIndexOf('/') + 1);
deleteBlob(settings.regionContext, 'vhds', vmOsDisc, 20, interval, function (err, resDelVHD) {
if (err) {
return;
}
// console.log('blob deleted: ', resDelVHD);
});
settingsDelNic = {
regionContext: settings.regionContext,
url: 'https://management.azure.com' + vmNic + '?api-version=' + deleteMsVersion,
successCode: 202
};
settingDelIP = {
regionContext: settings.regionContext,
url: 'https://management.azure.com' + vmNicIp + '?api-version=' + deleteMsVersion,
successCode: 202
};
azureRequest('DELETE', settingsDelNic, function (err, resDelNic) {
if (err) {
return;
}
// console.log('nic deleted: ', resDelNic);
azureRequest('DELETE', settingDelIP, function (err, resDelIP) {
if (err) {
return;
}
// console.log('IP deleted: ', resDelIP);
azureRequest('GET', settingsGetVnet, function (err, resVnet) {
if (err) {
return;
}
if (!resVnet.properties.subnets[0].properties.ipConfigurations) {
var settingsDelVnet = settingsGetVnet;
settingsDelVnet.successCode = 202;
azureRequest('DELETE', settingsDelVnet, function (err, resDelVnet) {
if (err) {
return;
}
// console.log('vnet deleted: ', resDelVnet);
});
}
});
});
});
}
setTimeout(checkDeleteStatus, 4000, settings, resVm, 50, deleteRestOfIt);
});
});
});
},
createImage: function (settings, callback) {
var settingsStopVm = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines/' + settings.imageParams.nodeId + '/deallocate?api-version=' + xMsVersion,
successCode: 202
},
settingsVMStatus = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines/' + settings.imageParams.nodeId + '/InstanceView?api-version=' + xMsVersion,
successCode: 200
},
settingsGeneralizeVm = {
regionContext: settings.regionContext,
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines/' + settings.imageParams.nodeId + '/generalize?api-version=' + xMsVersion,
successCode: 200,
retryCodes: 409
}, imageName = 'image' + (new Date().valueOf()),
settingsImage = {
regionContext: settings.regionContext,
jsonBody: {
"vhdPrefix": imageName,
"destinationContainerName": 'images',
"overwriteVhds": true
},
url: 'https://management.azure.com/subscriptions/' + settings.regionContext.subscriptionId + '/resourceGroups/' + settings.regionContext.groupId + '/providers/Microsoft.Compute/virtualMachines/' + settings.imageParams.nodeId + '/capture?api-version=' + xMsVersion,
successCode: 202,
retryCodes: 409
};
azureRequest('POST', settingsStopVm, function (err, res) {
if (err) {
callback(err);
return;
}
checkReachedFinishedState(settingsVMStatus, 'PowerState/deallocated', 50, function (err, res) {
azureRequest('POST', settingsGeneralizeVm, function (err, res) {
if (err) {
callback(err);
return;
}
checkReachedFinishedState(settingsVMStatus, 'OSState/generalized', 20, function (err, res) {
azureRequest('POST', settingsImage, function (err, res) {
if (err) {
callback(err);
return;
}
getBlobFullName(settings.regionContext, 'system', imageName, foldersStructure, 3, function (err, res) {
if (err || !res) {
callback(err || new AzureError('image was not created properly'));
return;
}
return callback(null, {rawResult: null, imageId: res});
});
});
});
});
});
});
},
deleteImage: function (settings, callback) {
deleteBlob(settings.regionContext, 'system','Microsoft.Compute/