azure-cli
Version:
Microsoft Azure Cross Platform Command Line tool
1,398 lines (1,238 loc) • 185 kB
JavaScript
//
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
var _ = require('underscore');
var fs = require('fs');
var url = require('url');
var async = require('async');
var util = require('util');
var utils = require('../../../util/utils');
var blobUtils = require('../../../util/blobUtils');
var pageBlob = require('../iaas/upload/pageBlob');
var CommunityUtil = require('../../../util/communityUtil');
var crypto = require('crypto');
var VNetUtil = require('../../../util/vnet.util');
var EndPointUtil = require('../../../util/endpointUtil');
var underscore = require('underscore');
var $ = utils.getLocaleString;
var profile = require('../../../util/profile');
var path = require('path');
var openssl = require('openssl-wrapper');
var exec = require('child_process').exec;
var vmUtils = require('./vmUtils');
var CHEFPUBLISHER = 'Chef.Bootstrap.WindowsAzure';
function VMClient(cli, subscription) {
this.cli = cli;
this.subscription = subscription;
}
_.extend(VMClient.prototype, {
createVM: function(dnsName, imageName, userName, password, options, callback, logger) {
var self = this;
var dnsPrefix = utils.getDnsPrefix(dnsName);
var vmSize = getVMSize(options, logger);
if (options.rdp) {
if (typeof options.rdp === 'boolean') {
options.rdp = 3389;
} else if ((options.rdp != parseInt(options.rdp, 10)) || (options.rdp > 65535)) {
return callback(new Error($('--rdp [port] must be an integer less than or equal to 65535')));
}
}
// Note: The optional argument --no-ssh-password maps to options.sshPassword.
// if --no-ssh-password is specified in the command line then options.sshPassword
// will be set to 'false' by commander. If --no-ssh-password is not specified as
// an option then options.sshPassword will be set to true by commander.
if (options.ssh) {
if (typeof options.ssh === 'boolean') {
options.ssh = 22;
} else if ((options.ssh != parseInt(options.ssh, 10)) || (options.ssh > 65535)) {
return callback(new Error($('--ssh [port] must be an integer less than or equal to 65535')));
}
} else if ((!options.sshPassword || options.sshCert) && options.sshEndpoint) {
return callback(new Error($('--no-ssh-password and --ssh-cert can only be used with --ssh or --no-ssh-endpoint parameter')));
}
if (!options.sshPassword && !options.sshCert) {
return callback(new Error($('--no-ssh-password can only be used with the --ssh-cert parameter')));
}
if (options.customData) {
// Size of customData file should be less then 64 KB
var stats = fs.statSync(options.customData);
var maxSize = 65535; // 64 KB
if (stats['size'] > maxSize) {
return callback(new Error($('--custom-data must be less than 64 KB')));
}
}
if (options.staticIp) {
var vnetUtil = new VNetUtil();
var parsedIp = vnetUtil.parseIPv4(options.staticIp);
if (parsedIp.error) {
return callback(parsedIp.error);
}
if (!options.virtualNetworkName) {
return callback(new Error($('--virtual-network-name must be specified when the --static-ip option is given')));
}
if (options.subnetNames) {
logger.warn('--static-ip, --subnet-names will be ignored and the static ip subnet will be used');
options.subnetNames = null;
}
} else if (options.subnetNames) {
if (!options.virtualNetworkName) {
return callback(new Error($('--virtual-network-name must be specified when the --subnet-names option is given')));
}
}
var nicConfiguration = parseNICParams(options.nicConfig);
if (nicConfiguration.error) {
return callback(new Error(nicConfiguration.error));
}
if (nicConfiguration.networkInterfaces.length !== 0) {
if (!options.staticIp) {
if (!options.subnetNames || !options.virtualNetworkName) {
return callback(new Error($('--virtual-network-name and --subnet-names must be specified when the --nic-config option is given')));
}
}
}
var computeManagementClient = self.createComputeManagementClient();
var managementClient = self.createManagementClient();
var storageClient = self.createStorageClient();
var networkClient = self.createNetworkClient();
createVM({
dnsPrefix: dnsPrefix,
imageName: imageName,
password: password,
userName: userName,
subscription: options.subscription,
size: vmSize,
location: options.location,
affinityGroup: options.affinityGroup,
imageTarget: options.blobUrl,
ssh: options.ssh,
sshCert: options.sshCert,
logger: logger,
noSshPassword: options.sshPassword === false,
noSshEndpoint: options.sshEndpoint === false,
rdp: options.rdp,
connect: options.connect,
community: options.community,
vmName: options.vmName,
virtualNetworkName: options.virtualNetworkName,
subnetNames: options.subnetNames,
staticIp: options.staticIp,
reservedIp: options.reservedIp,
publicIp: options.publicIp,
availabilitySet: options.availabilitySet,
customData: options.customData,
networkInterfaces: nicConfiguration.networkInterfaces,
computeManagementClient: computeManagementClient,
managementClient: managementClient,
storageClient: storageClient,
networkClient: networkClient
}, callback, logger, self.cli);
},
createVMfromJson: function(dnsName, roleFile, options, callback, logger) {
var self = this;
function stripBOM(content) {
// Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
// because the buffer-to-string conversion in `fs.readFileSync()`
// translates it to FEFF, the UTF-16 BOM.
if (content.charCodeAt(0) === 0xFEFF || content.charCodeAt(0) === 0xFFFE) {
content = content.slice(1);
}
return content;
}
var dnsPrefix = utils.getDnsPrefix(dnsName);
logger.verbose(util.format($('Loading role file: %s'), roleFile));
var jsonFile = fs.readFileSync(roleFile, 'utf8');
var role = JSON.parse(stripBOM(jsonFile));
// remove resourceExtensionReferences if empty
if (role.resourceExtensionReferences.length === 0) {
delete role.resourceExtensionReferences;
}
var computeManagementClient = self.createComputeManagementClient();
var managementClient = self.createManagementClient();
var storageClient = self.createStorageClient();
var networkClient = self.createNetworkClient();
createVM({
subscription: options.subscription,
location: options.location,
affinityGroup: options.affinityGroup,
dnsPrefix: dnsPrefix,
connect: options.connect,
role: role,
sshCert: options.sshCert,
virtualNetworkName: options.virtualNetworkName,
computeManagementClient: computeManagementClient,
managementClient: managementClient,
storageClient: storageClient,
networkClient: networkClient
}, callback, logger, self.cli);
},
listVMs: function(options, callback, logger) {
var self = this;
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var vms = [];
if (deployments.length > 0) {
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole') {
vms.push(createVMView(roles[j], deployments[i]));
}
}
}
}
}
self.cli.interaction.formatOutput(vms, function(outputData) {
if (outputData.length === 0) {
logger.info($('No VMs found'));
} else {
logger.table(outputData, function(row, item) {
row.cell($('Name'), item.VMName);
row.cell($('Status'), item.InstanceStatus);
row.cell($('Location'), item.Location ? item.Location : item.AffinityGroup);
row.cell($('DNS Name'), item.DNSName);
row.cell($('IP Address'), item.IPAddress);
});
}
});
return callback();
}
});
},
showVM: function(name, options, callback, logger) {
var self = this;
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var vms = [];
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === name) {
vms.push(createVMView(roles[j], deployments[i]));
}
}
}
}
// got vms, show detailed info about it
if (vms.length > 0) {
var vmOut = vms.length === 1 ? vms[0] : vms;
if (logger.format().json) {
logger.json(vmOut);
} else {
utils.logLineFormat(vmOut, logger.data);
}
} else {
logger.warn($('No VMs found'));
}
return callback();
}
});
},
deleteVM: function(vmName, options, callback, logger) {
var self = this;
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
options.dnsPrefix = options.dnsName;
var found = null;
var role = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === vmName) {
if (found) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
found = deployments[i];
role = roles[j];
}
}
}
}
// got unique vm, delete it
if (found) {
var deleteVMInternal = function() {
var progress = self.cli.interaction.progress($('Deleting VM'));
deleteRoleOrDeployment(computeManagementClient, found.svc, found.deploy, vmName, options, self.cli, callback, progress);
};
// confirm deleting if required
if (options.quiet)
deleteVMInternal();
else self.cli.interaction.confirm(util.format($('Delete the VM %s ? [y/n] '), vmName), function(dummy, shouldDelete) {
if (shouldDelete) {
deleteVMInternal();
} else {
return callback();
}
});
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
startVM: function(name, options, callback, logger) {
var self = this;
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var found = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === name) {
if (found) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
found = deployments[i];
found.roleInstance = getRoleInstance(roles[j].roleName, deployments[i].deploy);
}
}
}
}
// got unique vm, start it
if (found) {
var progress = self.cli.interaction.progress($('Starting VM'));
computeManagementClient.virtualMachines.start(found.svc, found.deploy.name,
found.roleInstance.instanceName,
function(error) {
progress.end();
return callback(error);
});
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
restartVM: function(name, options, callback, logger) {
var self = this;
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var found = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === name) {
if (found) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
found = deployments[i];
found.roleInstance = getRoleInstance(roles[j].roleName, deployments[i].deploy);
}
}
}
}
// got unique vm, restart it
if (found) {
var progress = self.cli.interaction.progress($('Restarting VM'));
computeManagementClient.virtualMachines.restart(found.svc, found.deploy.name,
found.roleInstance.instanceName,
function(error) {
progress.end();
return callback(error);
});
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
shutdownVM: function(name, options, callback, logger) {
var self = this;
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var found = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === name) {
if (found) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
found = deployments[i];
found.roleInstance = getRoleInstance(roles[j].roleName, deployments[i].deploy);
}
}
}
}
// got unique vm, shutting down it
if (found) {
var parameters = {
postShutdownAction: 'StoppedDeallocated'
};
// if --stay-provisioned argument is provided shutdown vm to "Stopped" state
if (options.stayProvisioned) {
parameters.postShutdownAction = 'Stopped';
}
var progress = self.cli.interaction.progress($('Shutting down VM'));
computeManagementClient.virtualMachines.shutdown(found.svc, found.deploy.name,
found.roleInstance.instanceName, parameters,
function(error) {
progress.end();
return callback(error);
});
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
captureVM: function(vmName, targetImageName, options, callback, logger) {
var self = this;
var vmImageTypes = ['Generalized', 'Specialized'];
var result = validateVMCaptureParams(vmImageTypes, options.osState, options['delete']);
if (result.error) {
return callback(new Error(result.error));
}
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var found = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === vmName) {
if (found) {
// found duplicates, emit error
return callback($('VM name is not unique'));
}
found = deployments[i];
found.roleInstance = getRoleInstance(roles[j].roleName, deployments[i].deploy);
}
}
}
}
if (found) {
progress = self.cli.interaction.progress(util.format($('Checking image with name %s exists'), targetImageName));
vmUtils.getImageInfo(computeManagementClient, targetImageName, function(error, response) {
progress.end();
if (!error) {
var image = response.vmImage || response.osImage;
if (image) {
var imageType = 'OS Image';
if (response.vmImage) {
if (response.vmImage.oSDiskConfiguration.oSState === 'Specialized') {
imageType = 'Specialized VM Image';
} else {
imageType = 'Generalized VM Image';
}
return callback(new Error(util.format($('Another image of type "%s" exists with the same name. Image capture is being aborted to avoid duplicates and potential conflicts. Please use another name for the image'), imageType)));
}
}
return captureVMIntern();
} else {
return callback(error);
}
});
var captureVMIntern = function() {
if (!options.osState) {
var osImageCaptureOptions = {
postCaptureAction: 'Delete',
targetImageName: targetImageName,
targetImageLabel: options.label || targetImageName // does not work without label
};
progress = self.cli.interaction.progress($('Capturing VM'));
computeManagementClient.virtualMachines.captureOSImage(found.svc, found.deploy.name, found.roleInstance.instanceName, osImageCaptureOptions, function(error) {
progress.end();
return callback(error);
});
} else {
if (found.roleInstance.instanceStatus === 'ReadyRole') {
logger.warn($('The VM image capture operation has been started while the VM is still running. This may cause data corruption while creating VMs from this image. Please shutdown the VM using the "azure vm shutdown" command before capturing the image'));
}
var vmImageCaptureOptions = {
oSState: result.vmImageType,
vMImageName: targetImageName,
vMImageLabel: options.label || targetImageName // does not work without label
};
var progress = self.cli.interaction.progress($('Capturing VM'));
computeManagementClient.virtualMachines.captureVMImage(found.svc, found.deploy.name, found.roleInstance.instanceName, vmImageCaptureOptions, function(error) {
progress.end();
return callback(error);
});
}
};
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
exportVM: function(vmName, filePath, options, callback, logger) {
var self = this;
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var found = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === vmName) {
if (found) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
found = roles[j];
}
}
}
}
// got unique role, export to file
if (found) {
var progress = self.cli.interaction.progress('Exporting the VM');
var prepareForExport = function(role) {
for (var key in role) {
// Remove namespace @ node
if (key === '@' || key === 'OsVersion') {
delete role[key];
} else if (key === 'dataVirtualHardDisks') {
// Remove Links of all DataVirtualHardDisks since
// while importing we need to pass only DiskName
// which will be already linked with a vhd
for (var i = 0; i < role[key].length; i++) {
delete role[key][i].mediaLink;
//IOType is impicitly determined from the media link hence it is not required
//when a vm is created from the exported json file
if (role[key][i].iOType) {
delete role[key][i].iOType;
}
}
} else if (key === 'oSVirtualHardDisk') {
delete role[key].mediaLink;
delete role[key].sourceImageName;
//IOType is impicitly determined from the media link hence it is not required
//when a vm is created from the exported json file
if (role[key].iOType) {
delete role[key].iOType;
}
}
// Remove namespace in inner objects
if (typeof role[key] === 'object') {
prepareForExport(role[key]);
}
}
};
prepareForExport(found);
if (found.dataVirtualHardDisks.length && !found.dataVirtualHardDisks[0].logicalUnitNumber) {
found.dataVirtualHardDisks[0].logicalUnitNumber = '0';
}
progress.end();
var roleAsString = JSON.stringify(found);
fs.writeFile(filePath, roleAsString, function(err) {
if (err) {
return callback(err);
} else {
logger.info(util.format($('VM %s exported to %s'), vmName, filePath));
return callback();
}
});
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
/*
* Server does not support changing nic collection, addNic can be used once server start
* support it, (can be used to enable PS Set-AzureNetworkInterfaceConfig equivalent)
*/
addNic: function (vmName, nicName, options, callback) {
var self = this;
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
}
var computeManagementClient = self.createComputeManagementClient();
getVMDeploymentExtended(deployments, vmName, self.cli, computeManagementClient, function (error, vmDeployment) {
if (error) {
return callback(error);
}
var persistentVMRole = vmDeployment.persistentVMRole;
var networkConfiguration = getNetworkConfigSet(persistentVMRole);
if (!networkConfiguration) {
return callback($('Network configuration not found on the VM'));
}
if (!networkConfiguration.networkInterfaces) {
networkConfiguration.networkInterfaces = [];
}
var networkInterface = utils.findFirstCaseIgnore(networkConfiguration.networkInterfaces, { name: nicName });
if (networkInterface) {
return callback(util.format($('NIC with name "%s" already exists'), nicName));
}
networkInterface = {
name: nicName,
iPConfigurations: [
{
subnetName: options.subnetName,
staticVirtualNetworkIPAddress: options.staticIp
}
]
};
networkConfiguration.networkInterfaces.push(networkInterface);
var progress = self.cli.interaction.progress($('Updating network configuration'));
computeManagementClient.virtualMachines.update(vmDeployment.deployment.svc, vmDeployment.deployment.deploy.name, vmName, persistentVMRole, function(error) {
progress.end();
return callback(error);
});
});
});
},
listLocations: function(options, callback, logger) {
var self = this;
var managementClient = self.createManagementClient();
var progress = self.cli.interaction.progress($('Getting locations'));
managementClient.locations.list(function(error, response) {
progress.end();
if (error) {
return callback(error);
} else {
var locations = response.locations;
if (locations.length === 0) {
logger.info($('No locations found'));
} else {
self.cli.interaction.formatOutput(locations, function(outputData) {
if (outputData.length === 0) {
logger.info($('No locations'));
} else {
logger.table(outputData, function(row, item) {
row.cell($('Name'), item.name);
});
}
});
}
return callback();
}
});
},
createEP: function(vmName, lbport, vmport, options, callback, logger) {
var self = this;
var endPointUtil = new EndPointUtil();
var epInput = {};
epInput.lbPort = {
'value': lbport,
'argName': 'lb-port'
};
if (vmport) {
epInput.vmPort = {
'value': vmport,
'argName': 'vm-port'
};
}
if (options.endpointName) {
epInput.name = {
'value': options.endpointName,
'argName': '--endpoint-name'
};
}
if (options.endpointProtocol) {
epInput.protocol = {
'value': options.endpointProtocol,
'argName': '--endpoint-protocol'
};
}
if (options.lbSetName) {
epInput.lbSetName = {
'value': options.lbSetName,
'argName': '--lb-set-name'
};
}
if (options.probePort) {
epInput.probePort = {
'value': options.probePort,
'argName': '--probe-port'
};
}
if (options.probeProtocol) {
epInput.probeProtocol = {
'value': options.probeProtocol,
'argName': '--probe-protocol'
};
}
if (options.probePath) {
epInput.probePath = {
'value': options.probePath,
'argName': '--probe-path'
};
}
if (options.enableDirectServerReturn) {
epInput.directServerReturn = {
'value': 'true',
'argName': '--enable-direct-server-return'
};
}
if (options.internalLoadBalancerName) {
epInput.internalLoadBalancerName = {
'value': options.internalLoadBalancerName,
'argName': '--internal-load-balancer-name'
};
}
var result = endPointUtil.verifyAndGetEndPointObj(epInput, [], false); // endpoint parameters validation
if (result.error) {
return callback(new Error(result.error));
}
var newEndPoints = [result.endPoint];
var newEndPointsResult = endPointUtil.verifyEndPoints(newEndPoints);
if (newEndPointsResult.error) {
return callback(new Error(newEndPointsResult.error));
}
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var result = getVMDeployment(deployments, vmName);
if (result.error) {
return callback(result.error);
} else {
// Get all LB settings defined in this hosted service
var lbsetConfigs = endPointUtil.getAllLBSettings(result.deployment.deploy.roles);
// If any of the new endpoint has lb set name, if same lb settings is
// defined for this hosted service then overwrite user provided lb
// settings with this.
for (var l = 0; l < newEndPoints.length; l++) {
var lbSetName = newEndPoints[l].loadBalancedEndpointSetName;
if (lbSetName) {
lbSetName = lbSetName.toLowerCase();
if (lbSetName in lbsetConfigs) {
if (underscore.contains(lbsetConfigs[lbSetName].VmNames, vmName)) {
return callback(new Error(
util.format($('this VM already has an endpoint with lb set name %s. lb set name should be unique'),
lbSetName)));
}
logger.info(util.format($('cloud service already has an lb set defined with name %s, using this existing lb settings configuration'),
lbSetName));
newEndPoints[l].loadBalancerProbe =
lbsetConfigs[lbSetName].ProbSettings;
newEndPoints[l].enableDirectServerReturn =
lbsetConfigs[lbSetName].enableDirectServerReturn;
}
}
if (newEndPoints[l].loadBalancerName) {
var err = checkInternalLoadBalancerExists(result.deployment.deploy.loadBalancers, newEndPoints[l].loadBalancerName);
if (err) {
return callback(new Error(err));
}
}
}
var progress = self.cli.interaction.progress($('Reading network configuration'));
computeManagementClient.virtualMachines.get(result.deployment.svc, result.deployment.deploy.name, vmName, function(error, response) {
progress.end();
if (error) {
return callback(error);
} else {
var persistentVMRole = response;
var configurationSets = persistentVMRole.configurationSets;
var m = 0;
for (; m < configurationSets.length; m++) {
if (configurationSets[m].configurationSetType === 'NetworkConfiguration') {
break;
}
}
if (!configurationSets[m].inputEndpoints) {
configurationSets[m].inputEndpoints = [];
}
var endpoints = configurationSets[m].inputEndpoints;
var endpointCount = endpoints.length;
for (var n = 0; n < endpointCount; n++) {
var key = endpoints[n].port + ':' + endpoints[n].protocol;
if (key in newEndPointsResult.protocolPorts) {
return callback(new Error(
util.format($('this VM already has a %s load balancer port %s. lb port and protocol together should be unique'),
endpoints[n].protocol, endpoints[n].port)));
}
key = endpoints[n].name.toLowerCase();
if (key in newEndPointsResult.endPointNames) {
return callback(new Error(
util.format($('this VM already has an endpoint with name %s, endpoint name should unique'),
key)));
}
}
configurationSets[m].inputEndpoints = configurationSets[m].inputEndpoints.concat(newEndPoints);
progress = self.cli.interaction.progress($('Updating network configuration'));
computeManagementClient.virtualMachines.update(result.deployment.svc, result.deployment.deploy.name, vmName, persistentVMRole, function(error) {
progress.end();
return callback(error);
});
}
});
}
}
});
},
createMultipleEP: function(vmName, endpoints, options, callback, logger) {
var self = this;
var message = 'each endpoint in the endpoints argument should be of the form \r\n <lb-port>[:<vm-port>[:<protocol>[:<enable-direct-server-return>[:<lb-set-name>[:<probe-protocol>[:<probe-port>[:<probe-path>[:<internal-lb-name>]]]]]]]] \r\n and prob-path Should be relative';
var endpointsAsList = endpoints.split(',');
var inputEndpoints = [];
var endPointUtil = new EndPointUtil();
endpointsAsList.forEach(function(endpointInfoStr, j) {
if (!endpointInfoStr) {
return callback(new Error(message));
}
var endpointInfoAsList = endpointInfoStr.split(':');
if (endpointInfoAsList.length > 9) {
return callback(new Error(message));
}
var i = 0;
var epInput = {};
endpointInfoAsList.forEach(function(item) {
if (!item) {
return callback(new Error(message));
}
switch (i) {
case 0:
epInput.lbPort = {
value: item,
argName: 'lb-port'
};
break;
case 1:
epInput.vmPort = {
value: item,
argName: 'vm-port'
};
break;
case 2:
epInput.protocol = {
value: item,
argName: 'protocol'
};
break;
case 3:
epInput.directServerReturn = {
value: item,
argName: 'enable-direct-server-return'
};
break;
case 4:
epInput.lbSetName = {
value: item,
argName: 'lb-set-name'
};
break;
case 5:
epInput.probeProtocol = {
value: item,
argName: 'probe-protocol'
};
break;
case 6:
epInput.probePort = {
value: item,
argName: 'probe-port'
};
break;
case 7:
epInput.probePath = {
value: item,
argName: 'probe-path'
};
break;
case 8:
epInput.internalLoadBalancerName = {
'value': item,
'argName': 'internal-lb-name'
};
break;
}
i++;
});
j++;
var result = endPointUtil.verifyAndGetEndPointObj(epInput, [], false);
if (result.error) {
return callback(new Error(util.format('%s (endpoint %s)', result.error, j)));
}
inputEndpoints.push(result.endPoint);
});
var newEndPoints = inputEndpoints;
var newEndPointsResult = endPointUtil.verifyEndPoints(newEndPoints);
if (newEndPointsResult.error) {
return callback(new Error(newEndPointsResult.error));
}
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var result = getVMDeployment(deployments, vmName);
if (result.error) {
return callback(result.error);
} else {
// Get all LB settings defined in this hosted service
var lbsetConfigs = endPointUtil.getAllLBSettings(result.deployment.deploy.roles);
// If any of the new endpoint has lb set name, if same lb settings is
// defined for this hosted service then overwrite user provided lb
// settings with this.
for (var l = 0; l < newEndPoints.length; l++) {
var lbSetName = newEndPoints[l].loadBalancedEndpointSetName;
if (lbSetName) {
lbSetName = lbSetName.toLowerCase();
if (lbSetName in lbsetConfigs) {
if (underscore.contains(lbsetConfigs[lbSetName].VmNames, vmName)) {
return callback(new Error(
util.format($('this VM already has an endpoint with lb set name %s. lb set name should be unique'),
lbSetName)));
}
logger.info(util.format($('cloud service already has an lb set defined with name %s, using this existing lb settings configuration'),
lbSetName));
newEndPoints[l].loadBalancerProbe =
lbsetConfigs[lbSetName].ProbSettings;
newEndPoints[l].enableDirectServerReturn =
lbsetConfigs[lbSetName].EnableDirectServerReturn;
}
}
if (newEndPoints[l].loadBalancerName) {
var err = checkInternalLoadBalancerExists(result.deployment.deploy.loadBalancers, newEndPoints[l].loadBalancerName);
if (err) {
return callback(new Error(err));
}
}
}
var progress = self.cli.interaction.progress($('Reading network configuration'));
computeManagementClient.virtualMachines.get(result.deployment.svc, result.deployment.deploy.name, vmName, function(error, response) {
progress.end();
if (error) {
return callback(error);
} else {
var persistentVMRole = response;
var configurationSets = persistentVMRole.configurationSets;
var m = 0;
for (; m < configurationSets.length; m++) {
if (configurationSets[m].configurationSetType === 'NetworkConfiguration') {
break;
}
}
if (!configurationSets[m].inputEndpoints) {
configurationSets[m].inputEndpoints = [];
}
var endpoints = configurationSets[m].inputEndpoints;
var endpointCount = endpoints.length;
for (var n = 0; n < endpointCount; n++) {
var key = endpoints[n].port + ':' + endpoints[n].protocol;
if (key in newEndPointsResult.protocolPorts) {
return callback(new Error(
util.format($('this VM already has a %s load balancer port %s. lb port and protocol together should be unique'),
endpoints[n].protocol, endpoints[n].port)));
}
key = endpoints[n].name.toLowerCase();
if (key in newEndPointsResult.endPointNames) {
return callback(new Error(
util.format($('this VM already has an endpoint with name %s, endpoint name should unique'),
key)));
}
}
configurationSets[m].inputEndpoints = configurationSets[m].inputEndpoints.concat(newEndPoints);
progress = self.cli.interaction.progress($('Updating network configuration'));
computeManagementClient.virtualMachines.update(result.deployment.svc, result.deployment.deploy.name, vmName, persistentVMRole, function(error) {
progress.end();
return callback(error);
});
}
});
}
}
});
},
listEPs: function(name, options, callback, logger) {
var self = this;
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var role = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === name) {
if (role) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
role = roles[j];
}
}
}
}
var endpointName = options.endpointName;
if (role) {
var networkConfigSet = getNetworkConfigSet(role, endpointName);
if (!networkConfigSet.inputEndpoints) {
if (logger.format().json) {
logger.json([]);
} else {
logger.warn($('No VMs found'));
}
return callback();
} else {
logger.table(networkConfigSet.inputEndpoints, function(row, item) {
row.cell('Name', item.name);
row.cell('Protocol', item.protocol);
row.cell('Public Port', item.port);
row.cell('Private Port', item.localPort);
row.cell('Virtual IP', item.virtualIPAddress || '');
row.cell('EnableDirectServerReturn', item.enableDirectServerReturn);
row.cell('Load Balanced', item.loadBalancedEndpointSetName ? 'Yes' : 'No');
});
return callback();
}
} else {
logger.warn($('No VMs found'));
return callback();
}
}
});
},
showStaticIP: function(vmName, options, callback, logger) {
var self = this;
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var role = null;
for (var i = 0; i < deployments.length; i++) {
var roles = deployments[i].deploy.roles;
if (roles) {
for (var j = 0; j < roles.length; j++) {
if (roles[j].roleType === 'PersistentVMRole' &&
roles[j].roleName === vmName) {
if (role) {
// found duplicates, emit error
return callback(new Error($('VM name is not unique')));
}
role = roles[j];
}
}
}
}
if (role) {
var networkConfigSet = getNetworkConfigSet(role);
var ipAddress = networkConfigSet.staticVirtualNetworkIPAddress;
if (ipAddress) {
var staticIPConfig = {
Network: {
StaticIP: ipAddress
}
};
if (logger.format().json) {
logger.json(staticIPConfig);
} else {
utils.logLineFormat(staticIPConfig, logger.data);
}
} else {
logger.info(util.format($('No static IP address set for VM %s'), vmName));
}
return callback();
} else {
logger.warn($('No VMs found'));
}
}
});
},
setStaticIP: function(vmName, ipAddress, options, callback) {
var self = this;
var progress;
var vnetUtil = new VNetUtil();
var parsedIp = vnetUtil.parseIPv4(ipAddress);
if (parsedIp.error) {
return callback(parsedIp.error);
}
var computeManagementClient = self.createComputeManagementClient(options);
var networkClient = self.createNetworkClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var result = getVMDeployment(deployments, vmName);
if (result.error) {
return callback(result.error);
} else {
var virtualNetworkName = result.deployment.deploy.virtualNetworkName;
if (!virtualNetworkName) {
return callback(new Error($('The VM does not belong to any virtual networks.')));
}
progress = self.cli.interaction.progress($('Looking up virtual network'));
getNetworkInfo(networkClient, virtualNetworkName, function(error, networkInfo) {
progress.end();
if (error) {
return callback(error);
} else {
var subnetResult = getIPAddressSubnet(networkInfo, ipAddress);
if (subnetResult.error) {
return callback(subnetResult.error);
}
if (!subnetResult.subnetName) {
return callback(new Error(util.format($('The static address %s doesn\'t belong to the address space defined by the role\'s subnets.'), ipAddress)));
}
progress = self.cli.interaction.progress($('Reading network configuration'));
computeManagementClient.virtualMachines.get(result.deployment.svc, result.deployment.deploy.name, vmName, function(error, response) {
progress.end();
if (error) {
return callback(error);
} else {
var role = response;
var networkConfigSet = getNetworkConfigSet(role);
networkConfigSet.staticVirtualNetworkIPAddress = ipAddress;
networkConfigSet.subnetNames = [
subnetResult.subnetName
];
progress = self.cli.interaction.progress($('Updating network configuration'));
computeManagementClient.virtualMachines.update(result.deployment.svc, result.deployment.deploy.name, vmName, role, function(error) {
progress.end();
return callback(error);
});
}
});
}
});
}
}
});
},
removeStaticIP: function(vmName, options, callback) {
var self = this;
var computeManagementClient = self.createComputeManagementClient(options);
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var result = getVMDeployment(deployments, vmName);
if (result.error) {
return callback(result.error);
} else {
var progress = self.cli.interaction.progress($('Reading network configuration'));
computeManagementClient.virtualMachines.get(result.deployment.svc, result.deployment.deploy.name, vmName, function(error, response) {
progress.end();
if (error) {
return callback(error);
} else {
var role = response;
var networkConfigSet = getNetworkConfigSet(role);
if (!networkConfigSet.staticVirtualNetworkIPAddress) {
// Nothing to do
return callback();
}
networkConfigSet.staticVirtualNetworkIPAddress = null;
progress = self.cli.interaction.progress($('Updating network configuration'));
computeManagementClient.virtualMachines.update(result.deployment.svc, result.deployment.deploy.name, vmName, role, function(error) {
progress.end();
return callback(error);
});
}
});
}
}
});
},
listPublicIPs: function(vmName, options, callback, logger) {
var self = this;
if (!vmName) {
return callback($('vm-name is required'));
}
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var result = getVMRole(deployments, vmName);
if (result.error) {
return callback(result.error);
}
if (result.vmRole) {
var persistentVMRole = result.vmRole;
var networkConfigIndex = findConfigurationSet(persistentVMRole.configurationSets, 'NetworkConfiguration');
if (networkConfigIndex === -1) {
return callback(new Error($('Network configuration not found on the VM.')));
}
var networkConfiguration = persistentVMRole.configurationSets[networkConfigIndex];
self.cli.interaction.formatOutput(networkConfiguration.publicIPs, function(outputData) {
if (outputData.length === 0) {
logger.info($('No public IP addresses found'));
} else {
logger.table(outputData, function(row, publicIP) {
row.cell($('Name'), publicIP.name);
row.cell($('IdleTimeoutInMinutes'), publicIP.IdleTimeoutInMinutes ? publicIP.IdleTimeoutInMinutes : '');
});
}
});
} else {
logger.warn($('No VMs found'));
}
return callback();
}
});
},
setPublicIP: function(vmName, publicipName, options, callback, logger) {
if (!vmName) {
return callback($('vm-name is required'));
}
if (!publicipName) {
return callback($('publicip-name is required'));
}
if (options.idleTimeoutInMinutes) {
if ((options.idleTimeoutInMinutes != parseInt(options.idleTimeoutInMinutes, 10)) || (options.idleTimeoutInMinutes < 4) || (options.idleTimeoutInMinutes > 30)) {
return callback(new Error($('--idle-timeoutInMinutes must be an integer in the range [4 - 30]')));
}
}
var self = this;
var computeManagementClient = self.createComputeManagementClient();
self.getDeployments(options, function(error, deployments) {
if (error) {
return callback(error);
} else {
var result = getVMDeployment(deployments, vmName);
if (result.error)