TRITON-58 node-triton should support nic operations
Reviewed by: Marsell Kukuljevic <marsell@joyent.com> Approved by: Marsell Kukuljevic <marsell@joyent.com>
This commit is contained in:
parent
39635cd0a2
commit
bf64899685
152
lib/cloudapi2.js
152
lib/cloudapi2.js
@ -154,6 +154,12 @@ function CloudApi(options) {
|
|||||||
this.client = new SaferJsonClient(options);
|
this.client = new SaferJsonClient(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <Network Object Key> -> <expected typeof>
|
||||||
|
CloudApi.prototype.NETWORK_OBJECT_FIELDS = {
|
||||||
|
ipv4_uuid: 'string',
|
||||||
|
ipv4_ips: 'string'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
CloudApi.prototype.close = function close(callback) {
|
CloudApi.prototype.close = function close(callback) {
|
||||||
this.log.trace({host: this.client.url && this.client.url.host},
|
this.log.trace({host: this.client.url && this.client.url.host},
|
||||||
@ -1150,7 +1156,7 @@ CloudApi.prototype.createMachine = function createMachine(options, callback) {
|
|||||||
assert.optionalString(options.name, 'options.name');
|
assert.optionalString(options.name, 'options.name');
|
||||||
assert.uuid(options.image, 'options.image');
|
assert.uuid(options.image, 'options.image');
|
||||||
assert.uuid(options.package, 'options.package');
|
assert.uuid(options.package, 'options.package');
|
||||||
assert.optionalArrayOfUuid(options.networks, 'options.networks');
|
assert.optionalArray(options.networks, 'options.networks');
|
||||||
// TODO: assert the other fields
|
// TODO: assert the other fields
|
||||||
assert.func(callback, 'callback');
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
@ -1536,6 +1542,150 @@ function deleteMachineSnapshot(opts, cb) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// --- NICs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a NIC on a network to an instance.
|
||||||
|
*
|
||||||
|
* @param {Object} options object containing:
|
||||||
|
* - {String} id (required) the instance id.
|
||||||
|
* - {String|Object} (required) network uuid or network object.
|
||||||
|
* @param {Function} callback of the form f(err, nic, res).
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.addNic =
|
||||||
|
function addNic(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.uuid(opts.id, 'opts.id');
|
||||||
|
assert.ok(opts.network, 'opts.network');
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
network: opts.network
|
||||||
|
};
|
||||||
|
|
||||||
|
this._request({
|
||||||
|
method: 'POST',
|
||||||
|
path: format('/%s/machines/%s/nics', this.account, opts.id),
|
||||||
|
data: data
|
||||||
|
}, function (err, req, res, body) {
|
||||||
|
cb(err, body, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all NICs on an instance.
|
||||||
|
*
|
||||||
|
* Returns an array of objects.
|
||||||
|
*
|
||||||
|
* @param opts {Object} Options
|
||||||
|
* - {String} id (required) the instance id.
|
||||||
|
* @param {Function} callback of the form f(err, nics, res).
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.listNics =
|
||||||
|
function listNics(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.uuid(opts.id, 'opts.id');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var endpoint = format('/%s/machines/%s/nics', this.account, opts.id);
|
||||||
|
this._passThrough(endpoint, opts, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a NIC on an instance.
|
||||||
|
*
|
||||||
|
* @param {Object} options object containing:
|
||||||
|
* - {UUID} id: The instance id. Required.
|
||||||
|
* - {String} mac: The NIC's MAC. Required.
|
||||||
|
* @param {Function} callback of the form `function (err, nic, res)`
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.getNic =
|
||||||
|
function getNic(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.uuid(opts.id, 'opts.id');
|
||||||
|
assert.string(opts.mac, 'opts.mac');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var mac = opts.mac.replace(/:/g, '');
|
||||||
|
var endpoint = format('/%s/machines/%s/nics/%s', this.account, opts.id,
|
||||||
|
mac);
|
||||||
|
this._request(endpoint, function (err, req, res, body) {
|
||||||
|
cb(err, body, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a NIC off an instance.
|
||||||
|
*
|
||||||
|
* @param {Object} opts (object)
|
||||||
|
* - {UUID} id: The instance id. Required.
|
||||||
|
* - {String} mac: The NIC's MAC. Required.
|
||||||
|
* @param {Function} cb of the form `function (err, res)`
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.removeNic =
|
||||||
|
function removeNic(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.uuid(opts.id, 'opts.id');
|
||||||
|
assert.string(opts.mac, 'opts.mac');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var mac = opts.mac.replace(/:/g, '');
|
||||||
|
|
||||||
|
this._request({
|
||||||
|
method: 'DELETE',
|
||||||
|
path: format('/%s/machines/%s/nics/%s', this.account, opts.id, mac)
|
||||||
|
}, function (err, req, res) {
|
||||||
|
cb(err, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for a machine's nic to go one of a set of specfic states.
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* - {String} id {required} machine id
|
||||||
|
* - {String} mac {required} mac for new nic
|
||||||
|
* - {Array of String} states - desired state
|
||||||
|
* - {Number} interval (optional) - time in ms to poll
|
||||||
|
* @param {Function} callback of the form f(err, nic, res).
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.waitForNicStates =
|
||||||
|
function waitForNicStates(opts, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.uuid(opts.id, 'opts.id');
|
||||||
|
assert.string(opts.mac, 'opts.mac');
|
||||||
|
assert.arrayOfString(opts.states, 'opts.states');
|
||||||
|
assert.optionalNumber(opts.interval, 'opts.interval');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var interval = opts.interval || 1000;
|
||||||
|
assert.ok(interval > 0, 'interval must be a positive number');
|
||||||
|
|
||||||
|
poll();
|
||||||
|
|
||||||
|
function poll() {
|
||||||
|
self.getNic({
|
||||||
|
id: opts.id,
|
||||||
|
mac: opts.mac
|
||||||
|
}, function onPoll(err, nic, res) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, null, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (opts.states.indexOf(nic.state) !== -1) {
|
||||||
|
cb(null, nic, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(poll, interval);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// --- firewall rules
|
// --- firewall rules
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Joyent, Inc.
|
* Copyright (c) 2018, Joyent, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var assert = require('assert-plus');
|
var assert = require('assert-plus');
|
||||||
@ -24,7 +24,8 @@ var wordwrap = require('wordwrap');
|
|||||||
|
|
||||||
var errors = require('./errors'),
|
var errors = require('./errors'),
|
||||||
InternalError = errors.InternalError;
|
InternalError = errors.InternalError;
|
||||||
|
var NETWORK_OBJECT_FIELDS =
|
||||||
|
require('./cloudapi2').CloudApi.prototype.NETWORK_OBJECT_FIELDS;
|
||||||
|
|
||||||
|
|
||||||
// ---- support stuff
|
// ---- support stuff
|
||||||
@ -1412,6 +1413,55 @@ function ipv4ToLong(ip) {
|
|||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the input from the `--nics <nic>` CLI argument.
|
||||||
|
*
|
||||||
|
* @param a {Array} The array of strings formatted as key=value
|
||||||
|
* ex: ['ipv4_uuid=1234', 'ipv4_ips=1.2.3.4|5.6.7.8']
|
||||||
|
* @return {Object} A network object. From the example above:
|
||||||
|
* {
|
||||||
|
* "ipv4_uuid": 1234,
|
||||||
|
* "ipv4_ips": [
|
||||||
|
* "1.2.3.4",
|
||||||
|
* "5.6.7.8"
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* Note: "1234" is used as the UUID for this example, but would actually cause
|
||||||
|
* `parseNicsCLI` to throw as it is not a valid UUID.
|
||||||
|
*/
|
||||||
|
function parseNicsCLI(nic) {
|
||||||
|
assert.arrayOfString(nic);
|
||||||
|
|
||||||
|
var obj = objFromKeyValueArgs(nic, {
|
||||||
|
disableDotted: true,
|
||||||
|
typeHintFromKey: NETWORK_OBJECT_FIELDS,
|
||||||
|
validKeys: Object.keys(NETWORK_OBJECT_FIELDS)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!obj.ipv4_uuid) {
|
||||||
|
throw new errors.UsageError(
|
||||||
|
'ipv4_uuid must be specified in network object');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.ipv4_ips) {
|
||||||
|
obj.ipv4_ips = obj.ipv4_ips.split('|');
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.uuid(obj.ipv4_uuid, 'obj.ipv4_uuid');
|
||||||
|
assert.optionalArrayOfString(obj.ipv4_ips, 'obj.ipv4_ips');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only 1 IP address may be specified at this time. In the future, this
|
||||||
|
* limitation should be removed.
|
||||||
|
*/
|
||||||
|
if (obj.ipv4_ips && obj.ipv4_ips.length !== 1) {
|
||||||
|
throw new errors.UsageError('only 1 ipv4_ip may be specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//---- exports
|
//---- exports
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -1451,6 +1501,7 @@ module.exports = {
|
|||||||
monotonicTimeDiffMs: monotonicTimeDiffMs,
|
monotonicTimeDiffMs: monotonicTimeDiffMs,
|
||||||
readStdin: readStdin,
|
readStdin: readStdin,
|
||||||
validateObject: validateObject,
|
validateObject: validateObject,
|
||||||
ipv4ToLong: ipv4ToLong
|
ipv4ToLong: ipv4ToLong,
|
||||||
|
parseNicsCLI: parseNicsCLI
|
||||||
};
|
};
|
||||||
// vim: set softtabstop=4 shiftwidth=4:
|
// vim: set softtabstop=4 shiftwidth=4:
|
||||||
|
211
lib/do_instance/do_nic/do_create.js
Normal file
211
lib/do_instance/do_nic/do_create.js
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton instance nic create ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
|
||||||
|
var common = require('../../common');
|
||||||
|
var errors = require('../../errors');
|
||||||
|
|
||||||
|
function do_create(subcmd, opts, args, cb) {
|
||||||
|
assert.optionalBool(opts.wait, 'opts.wait');
|
||||||
|
assert.optionalBool(opts.json, 'opts.json');
|
||||||
|
assert.optionalBool(opts.help, 'opts.help');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
cb(new errors.UsageError('missing INST and NETWORK or INST and' +
|
||||||
|
' NICOPT=VALUE arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
var netObj;
|
||||||
|
var netObjArgs = [];
|
||||||
|
var regularArgs = [];
|
||||||
|
var createOpts = {};
|
||||||
|
|
||||||
|
args.forEach(function forEachArg(arg) {
|
||||||
|
if (arg.indexOf('=') !== -1) {
|
||||||
|
netObjArgs.push(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
regularArgs.push(arg);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (netObjArgs.length > 0) {
|
||||||
|
if (regularArgs.length > 1) {
|
||||||
|
cb(new errors.UsageError('cannot specify INST and NETWORK when'
|
||||||
|
+ ' passing in ipv4 arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (regularArgs.length !== 1) {
|
||||||
|
cb(new errors.UsageError('missing INST argument'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
netObj = common.parseNicsCLI(netObjArgs);
|
||||||
|
} catch (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netObj) {
|
||||||
|
assert.array(regularArgs, 'regularArgs');
|
||||||
|
assert.equal(regularArgs.length, 1, 'instance uuid');
|
||||||
|
|
||||||
|
createOpts.id = regularArgs[0];
|
||||||
|
createOpts.network = netObj;
|
||||||
|
} else {
|
||||||
|
assert.array(args, 'args');
|
||||||
|
assert.equal(args.length, 2, 'INST and NETWORK');
|
||||||
|
|
||||||
|
createOpts.id = args[0];
|
||||||
|
createOpts.network = args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait(instId, mac, next) {
|
||||||
|
assert.string(instId, 'instId');
|
||||||
|
assert.string(mac, 'mac');
|
||||||
|
assert.func(next, 'next');
|
||||||
|
|
||||||
|
var waiter = cli.tritonapi.waitForNicStates.bind(cli.tritonapi);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We request state running|stopped because net-agent is doing work to
|
||||||
|
* keep a NICs state in sync with the VMs state. If a user adds a NIC
|
||||||
|
* to a stopped instance the final state of the NIC should also be
|
||||||
|
* stopped.
|
||||||
|
*/
|
||||||
|
waiter({
|
||||||
|
id: instId,
|
||||||
|
mac: mac,
|
||||||
|
states: ['running', 'stopped']
|
||||||
|
}, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// same signature as wait(), but is a nop
|
||||||
|
function waitNop(instId, mac, next) {
|
||||||
|
assert.string(instId, 'instId');
|
||||||
|
assert.string(mac, 'mac');
|
||||||
|
assert.func(next, 'next');
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
common.cliSetupTritonApi({cli: cli}, function onSetup(setupErr) {
|
||||||
|
if (setupErr) {
|
||||||
|
cb(setupErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.tritonapi.addNic(createOpts, function onAddNic(err, nic) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a NIC exists on the network already we will receive a 302
|
||||||
|
if (!nic) {
|
||||||
|
var errMsg = 'Instance already has a NIC on that network';
|
||||||
|
cb(new errors.TritonError(errMsg));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// either wait or invoke a nop stub
|
||||||
|
var func = opts.wait ? wait : waitNop;
|
||||||
|
|
||||||
|
if (opts.wait && !opts.json) {
|
||||||
|
console.log('Creating NIC %s', nic.mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
func(createOpts.id, nic.mac, function onWait(err2, createdNic) {
|
||||||
|
if (err2) {
|
||||||
|
cb(err2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nicInfo = createdNic || nic;
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
console.log(JSON.stringify(nicInfo));
|
||||||
|
} else {
|
||||||
|
console.log('Created NIC %s', nic.mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_create.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON stream output.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['wait', 'w'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Wait for the creation to complete.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
do_create.synopses = [
|
||||||
|
'{{name}} {{cmd}} [OPTIONS] INST NETWORK',
|
||||||
|
'{{name}} {{cmd}} [OPTIONS] INST NICOPT=VALUE [NICOPT=VALUE ...]'
|
||||||
|
];
|
||||||
|
|
||||||
|
do_create.help = [
|
||||||
|
'Create a NIC.',
|
||||||
|
'',
|
||||||
|
'{{usage}}',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'INST is an instance id (full UUID), name, or short id,',
|
||||||
|
'and NETWORK is a network id (full UUID), name, or short id.',
|
||||||
|
'',
|
||||||
|
'NICOPTs are NIC options. The following NIC options are supported:',
|
||||||
|
'ipv4_uuid=<full network uuid> (required),' +
|
||||||
|
' and ipv4_ips=<a single IP string>.',
|
||||||
|
'',
|
||||||
|
'Be aware that adding NICs to an instance will cause that instance to',
|
||||||
|
'reboot.',
|
||||||
|
'',
|
||||||
|
'Example:',
|
||||||
|
' triton instance nic create --wait 22b75576 ca8aefb9',
|
||||||
|
' triton instance nic create 22b75576' +
|
||||||
|
' ipv4_uuid=651446a8-dab0-439e-a2c4-2c841ab07c51' +
|
||||||
|
' ipv4_ips=192.168.128.13'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
do_create.helpOpts = {
|
||||||
|
helpCol: 25
|
||||||
|
};
|
||||||
|
|
||||||
|
do_create.completionArgtypes = ['tritoninstance', 'tritonnic', 'none'];
|
||||||
|
|
||||||
|
module.exports = do_create;
|
126
lib/do_instance/do_nic/do_delete.js
Normal file
126
lib/do_instance/do_nic/do_delete.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton instance nic delete ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var vasync = require('vasync');
|
||||||
|
|
||||||
|
var common = require('../../common');
|
||||||
|
var errors = require('../../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_delete(subcmd, opts, args, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.optionalBool(opts.force, 'opts.force');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
cb(new errors.UsageError('missing INST and MAC argument(s)'));
|
||||||
|
return;
|
||||||
|
} else if (args.length > 2) {
|
||||||
|
cb(new errors.UsageError('incorrect number of arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inst = args[0];
|
||||||
|
var mac = args[1];
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
common.cliSetupTritonApi({cli: cli}, function onSetup(setupErr) {
|
||||||
|
if (setupErr) {
|
||||||
|
cb(setupErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm({mac: mac, force: opts.force}, function onConfirm(confirmErr) {
|
||||||
|
if (confirmErr) {
|
||||||
|
console.error('Aborting');
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.tritonapi.removeNic({
|
||||||
|
id: inst,
|
||||||
|
mac: mac
|
||||||
|
}, function onRemove(err) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Deleted NIC %s', mac);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Request confirmation before deleting, unless --force flag given.
|
||||||
|
// If user declines, terminate early.
|
||||||
|
function confirm(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.force) {
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
common.promptYesNo({
|
||||||
|
msg: 'Delete NIC "' + opts.mac + '"? [y/n] '
|
||||||
|
}, function (answer) {
|
||||||
|
if (answer !== 'y') {
|
||||||
|
cb(new Error('Aborted NIC deletion'));
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_delete.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['force', 'f'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Force removal.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
do_delete.synopses = ['{{name}} {{cmd}} INST MAC'];
|
||||||
|
|
||||||
|
do_delete.help = [
|
||||||
|
'Remove a NIC from an instance.',
|
||||||
|
'',
|
||||||
|
'{{usage}}',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'Where INST is an instance id (full UUID), name, or short id.',
|
||||||
|
'',
|
||||||
|
'Be aware that removing NICs from an instance will cause that instance to',
|
||||||
|
'reboot.'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
do_delete.aliases = ['rm'];
|
||||||
|
|
||||||
|
do_delete.completionArgtypes = ['tritoninstance', 'none'];
|
||||||
|
|
||||||
|
module.exports = do_delete;
|
89
lib/do_instance/do_nic/do_get.js
Normal file
89
lib/do_instance/do_nic/do_get.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton instance nic get ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
|
||||||
|
var common = require('../../common');
|
||||||
|
var errors = require('../../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_get(subcmd, opts, args, cb) {
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
cb(new errors.UsageError('missing INST and MAC arguments'));
|
||||||
|
return;
|
||||||
|
} else if (args.length > 2) {
|
||||||
|
cb(new errors.UsageError('incorrect number of arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inst = args[0];
|
||||||
|
var mac = args[1];
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
common.cliSetupTritonApi({cli: cli}, function onSetup(setupErr) {
|
||||||
|
if (setupErr) {
|
||||||
|
cb(setupErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.tritonapi.getNic({id: inst, mac: mac}, function onNic(err, nic) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
console.log(JSON.stringify(nic));
|
||||||
|
} else {
|
||||||
|
console.log(JSON.stringify(nic, null, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_get.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON stream output.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
do_get.synopses = ['{{name}} {{cmd}} INST MAC'];
|
||||||
|
|
||||||
|
do_get.help = [
|
||||||
|
'Show a specific NIC.',
|
||||||
|
'',
|
||||||
|
'{{usage}}',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'Where INST is an instance id (full UUID), name, or short id.'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
do_get.completionArgtypes = ['tritoninstance', 'none'];
|
||||||
|
|
||||||
|
module.exports = do_get;
|
154
lib/do_instance/do_nic/do_list.js
Normal file
154
lib/do_instance/do_nic/do_list.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton instance nic list ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var tabula = require('tabula');
|
||||||
|
|
||||||
|
var common = require('../../common');
|
||||||
|
var errors = require('../../errors');
|
||||||
|
|
||||||
|
|
||||||
|
var VALID_FILTERS = ['ip', 'mac', 'state', 'network', 'primary', 'gateway'];
|
||||||
|
var COLUMNS_DEFAULT = 'ip,mac,state,network';
|
||||||
|
var COLUMNS_DEFAULT_LONG = 'ip,mac,state,network,primary,gateway';
|
||||||
|
var SORT_DEFAULT = 'ip';
|
||||||
|
|
||||||
|
|
||||||
|
function do_list(subcmd, opts, args, cb) {
|
||||||
|
assert.array(args, 'args');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 1) {
|
||||||
|
cb(new errors.UsageError('missing INST argument'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inst = args.shift();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var filters = common.objFromKeyValueArgs(args, {
|
||||||
|
validKeys: VALID_FILTERS,
|
||||||
|
disableDotted: true
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
common.cliSetupTritonApi({cli: cli}, function onSetup(setupErr) {
|
||||||
|
if (setupErr) {
|
||||||
|
cb(setupErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.tritonapi.listNics({id: inst}, function onNics(err, nics) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do filtering
|
||||||
|
Object.keys(filters).forEach(function filterByKey(key) {
|
||||||
|
var val = filters[key];
|
||||||
|
nics = nics.filter(function filterByNic(nic) {
|
||||||
|
return nic[key] === val;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
common.jsonStream(nics);
|
||||||
|
} else {
|
||||||
|
nics.forEach(function onNic(nic) {
|
||||||
|
nic.network = nic.network.split('-')[0];
|
||||||
|
nic.ip = nic.ip + '/' + convertCidrSuffix(nic.netmask);
|
||||||
|
});
|
||||||
|
|
||||||
|
var columns = COLUMNS_DEFAULT;
|
||||||
|
|
||||||
|
if (opts.o) {
|
||||||
|
columns = opts.o;
|
||||||
|
} else if (opts.long) {
|
||||||
|
columns = COLUMNS_DEFAULT_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
columns = columns.split(',');
|
||||||
|
var sort = opts.s.split(',');
|
||||||
|
|
||||||
|
tabula(nics, {
|
||||||
|
skipHeader: opts.H,
|
||||||
|
columns: columns,
|
||||||
|
sort: sort
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function convertCidrSuffix(netmask) {
|
||||||
|
var bitmask = netmask.split('.').map(function (octet) {
|
||||||
|
return (+octet).toString(2);
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (i = 0; i < bitmask.length; i++) {
|
||||||
|
if (bitmask[i] === '0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_list.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
}
|
||||||
|
].concat(common.getCliTableOptions({
|
||||||
|
includeLong: true,
|
||||||
|
sortDefault: SORT_DEFAULT
|
||||||
|
}));
|
||||||
|
|
||||||
|
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS] [FILTERS]'];
|
||||||
|
|
||||||
|
do_list.help = [
|
||||||
|
'Show all NICs on an instance.',
|
||||||
|
'',
|
||||||
|
'{{usage}}',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'',
|
||||||
|
'Where INST is an instance id (full UUID), name, or short id.',
|
||||||
|
'',
|
||||||
|
'Filters:',
|
||||||
|
' FIELD=<string> String filter. Supported fields: ip, mac, state,',
|
||||||
|
' network, netmask',
|
||||||
|
'',
|
||||||
|
'Filters are applied client-side (i.e. done by the triton command itself).'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
do_list.completionArgtypes = ['tritoninstance', 'none'];
|
||||||
|
|
||||||
|
do_list.aliases = ['ls'];
|
||||||
|
|
||||||
|
module.exports = do_list;
|
50
lib/do_instance/do_nic/index.js
Normal file
50
lib/do_instance/do_nic/index.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton inst nic ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Cmdln = require('cmdln').Cmdln;
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---- CLI class
|
||||||
|
|
||||||
|
function NicCLI(top) {
|
||||||
|
this.top = top.top;
|
||||||
|
|
||||||
|
Cmdln.call(this, {
|
||||||
|
name: top.name + ' nic',
|
||||||
|
desc: 'List and manage instance NICs.',
|
||||||
|
helpSubcmds: [
|
||||||
|
'help',
|
||||||
|
'list',
|
||||||
|
'get',
|
||||||
|
'create',
|
||||||
|
'delete'
|
||||||
|
],
|
||||||
|
helpOpts: {
|
||||||
|
minHelpCol: 23
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(NicCLI, Cmdln);
|
||||||
|
|
||||||
|
NicCLI.prototype.init = function init(opts, args, cb) {
|
||||||
|
this.log = this.top.log;
|
||||||
|
Cmdln.prototype.init.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
NicCLI.prototype.do_list = require('./do_list');
|
||||||
|
NicCLI.prototype.do_create = require('./do_create');
|
||||||
|
NicCLI.prototype.do_get = require('./do_get');
|
||||||
|
NicCLI.prototype.do_delete = require('./do_delete');
|
||||||
|
|
||||||
|
module.exports = NicCLI;
|
@ -49,6 +49,7 @@ function InstanceCLI(top) {
|
|||||||
'ip',
|
'ip',
|
||||||
'wait',
|
'wait',
|
||||||
'audit',
|
'audit',
|
||||||
|
'nic',
|
||||||
'snapshot',
|
'snapshot',
|
||||||
'tag'
|
'tag'
|
||||||
]
|
]
|
||||||
@ -81,6 +82,7 @@ InstanceCLI.prototype.do_ssh = require('./do_ssh');
|
|||||||
InstanceCLI.prototype.do_ip = require('./do_ip');
|
InstanceCLI.prototype.do_ip = require('./do_ip');
|
||||||
InstanceCLI.prototype.do_wait = require('./do_wait');
|
InstanceCLI.prototype.do_wait = require('./do_wait');
|
||||||
InstanceCLI.prototype.do_audit = require('./do_audit');
|
InstanceCLI.prototype.do_audit = require('./do_audit');
|
||||||
|
InstanceCLI.prototype.do_nic = require('./do_nic');
|
||||||
InstanceCLI.prototype.do_snapshot = require('./do_snapshot');
|
InstanceCLI.prototype.do_snapshot = require('./do_snapshot');
|
||||||
InstanceCLI.prototype.do_snapshots = require('./do_snapshots');
|
InstanceCLI.prototype.do_snapshots = require('./do_snapshots');
|
||||||
InstanceCLI.prototype.do_tag = require('./do_tag');
|
InstanceCLI.prototype.do_tag = require('./do_tag');
|
||||||
|
224
lib/tritonapi.js
224
lib/tritonapi.js
@ -2029,6 +2029,230 @@ function deleteAllInstanceTags(opts, cb) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---- nics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a NIC on a network to an instance.
|
||||||
|
*
|
||||||
|
* @param {Object} opts
|
||||||
|
* - {String} id: The instance ID, name, or short ID. Required.
|
||||||
|
* - {Object|String} network: The network object or ID, name, or short ID.
|
||||||
|
* Required.
|
||||||
|
* @param {Function} callback `function (err, nic, res)`
|
||||||
|
*/
|
||||||
|
TritonApi.prototype.addNic =
|
||||||
|
function addNic(opts, cb) {
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
|
assert.ok(opts.network, 'opts.network');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var pipeline = [];
|
||||||
|
var res;
|
||||||
|
var nic;
|
||||||
|
|
||||||
|
switch (typeof (opts.network)) {
|
||||||
|
case 'string':
|
||||||
|
pipeline.push(_stepNetId);
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('unexpected opts.network: ' + opts.network);
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.push(_stepInstId);
|
||||||
|
pipeline.push(function createNic(arg, next) {
|
||||||
|
self.cloudapi.addNic({
|
||||||
|
id: arg.instId,
|
||||||
|
network: arg.netId || arg.network
|
||||||
|
}, function onCreateNic(err, _nic, _res) {
|
||||||
|
res = _res;
|
||||||
|
res.instId = arg.instId; // gross hack, in case caller needs it
|
||||||
|
res.netId = arg.netId; // ditto
|
||||||
|
nic = _nic;
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var pipelineArg = {
|
||||||
|
client: self,
|
||||||
|
id: opts.id,
|
||||||
|
network: opts.network
|
||||||
|
};
|
||||||
|
|
||||||
|
vasync.pipeline({
|
||||||
|
arg: pipelineArg,
|
||||||
|
funcs: pipeline
|
||||||
|
}, function (err) {
|
||||||
|
cb(err, nic, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List an instance's NICs.
|
||||||
|
*
|
||||||
|
* @param {Object} opts
|
||||||
|
* - {String} id: The instance ID, name, or short ID. Required.
|
||||||
|
* @param {Function} callback `function (err, nics, res)`
|
||||||
|
*/
|
||||||
|
TritonApi.prototype.listNics =
|
||||||
|
function listNics(opts, cb) {
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var res;
|
||||||
|
var nics;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {client: self, id: opts.id}, funcs: [
|
||||||
|
_stepInstId,
|
||||||
|
|
||||||
|
function list(arg, next) {
|
||||||
|
self.cloudapi.listNics({
|
||||||
|
id: arg.instId
|
||||||
|
}, function (err, _nics, _res) {
|
||||||
|
res = _res;
|
||||||
|
res.instId = arg.instId; // gross hack, in case caller needs it
|
||||||
|
nics = _nics;
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
cb(err, nics, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a NIC belonging to an instance.
|
||||||
|
*
|
||||||
|
* @param {Object} opts
|
||||||
|
* - {String} id: The instance ID, name, or short ID. Required.
|
||||||
|
* - {String} mac: The NIC's MAC address. Required.
|
||||||
|
* @param {Function} callback `function (err, nic, res)`
|
||||||
|
*/
|
||||||
|
TritonApi.prototype.getNic =
|
||||||
|
function getNic(opts, cb) {
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
|
assert.string(opts.mac, 'opts.mac');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var res;
|
||||||
|
var nic;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {client: self, id: opts.id, mac: opts.mac}, funcs: [
|
||||||
|
_stepInstId,
|
||||||
|
|
||||||
|
function get(arg, next) {
|
||||||
|
self.cloudapi.getNic({
|
||||||
|
id: arg.instId,
|
||||||
|
mac: arg.mac
|
||||||
|
}, function (err, _nic, _res) {
|
||||||
|
res = _res;
|
||||||
|
res.instId = arg.instId; // gross hack, in case caller needs it
|
||||||
|
nic = _nic;
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
cb(err, nic, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a NIC from an instance.
|
||||||
|
*
|
||||||
|
* @param {Object} opts
|
||||||
|
* - {String} id: The instance ID, name, or short ID. Required.
|
||||||
|
* - {String} mac: The NIC's MAC address. Required.
|
||||||
|
* @param {Function} callback `function (err, res)`
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TritonApi.prototype.removeNic =
|
||||||
|
function removeNic(opts, cb) {
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
|
assert.string(opts.mac, 'opts.mac');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var res;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {client: self, id: opts.id, mac: opts.mac}, funcs: [
|
||||||
|
_stepInstId,
|
||||||
|
|
||||||
|
function deleteNic(arg, next) {
|
||||||
|
self.cloudapi.removeNic({
|
||||||
|
id: arg.instId,
|
||||||
|
mac: arg.mac
|
||||||
|
}, function (err, _res) {
|
||||||
|
res = _res;
|
||||||
|
res.instId = arg.instId; // gross hack, in case caller needs it
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
cb(err, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for cloudapi2's waitForNicStates that will first translate
|
||||||
|
* opts.id into the proper uuid from shortid/name.
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* - {String} id {required} machine id
|
||||||
|
* - {String} mac {required} mac for new nic
|
||||||
|
* - {Array of String} states - desired state
|
||||||
|
* @param {Function} callback of the form f(err, nic, res).
|
||||||
|
*/
|
||||||
|
TritonApi.prototype.waitForNicStates = function waitForNicStates(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.string(opts.id, 'opts.id');
|
||||||
|
assert.string(opts.mac, 'opts.mac');
|
||||||
|
assert.arrayOfString(opts.states, 'opts.states');
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var nic, res;
|
||||||
|
|
||||||
|
function waitForNic(arg, next) {
|
||||||
|
var _opts = {
|
||||||
|
id: arg.instId,
|
||||||
|
mac: arg.mac,
|
||||||
|
states: arg.states
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cloudapi.waitForNicStates(_opts,
|
||||||
|
function onWaitForNicState(err, _nic, _res) {
|
||||||
|
res = _res;
|
||||||
|
nic = _nic;
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipelineArgs = {
|
||||||
|
client: self,
|
||||||
|
id: opts.id,
|
||||||
|
mac: opts.mac,
|
||||||
|
states: opts.states
|
||||||
|
};
|
||||||
|
|
||||||
|
vasync.pipeline({
|
||||||
|
arg: pipelineArgs,
|
||||||
|
funcs: [
|
||||||
|
_stepInstId,
|
||||||
|
waitForNic
|
||||||
|
]
|
||||||
|
}, function onWaitForNicPipeline(err) {
|
||||||
|
cb(err, nic, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// ---- Firewall Rules
|
// ---- Firewall Rules
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
110
test/integration/api-nics.test.js
Normal file
110
test/integration/api-nics.test.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Joyent, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Integration tests for using NIC-related APIs as a module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var h = require('./helpers');
|
||||||
|
var test = require('tape');
|
||||||
|
|
||||||
|
|
||||||
|
// --- Globals
|
||||||
|
|
||||||
|
var CLIENT;
|
||||||
|
var INST;
|
||||||
|
var NIC;
|
||||||
|
|
||||||
|
|
||||||
|
// --- Tests
|
||||||
|
|
||||||
|
test('TritonApi networks', function (tt) {
|
||||||
|
tt.test(' setup', function (t) {
|
||||||
|
h.createClient(function (err, client_) {
|
||||||
|
t.error(err);
|
||||||
|
CLIENT = client_;
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
tt.test(' setup: inst', function (t) {
|
||||||
|
CLIENT.cloudapi.listMachines({}, function (err, vms) {
|
||||||
|
if (vms.length === 0)
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(Array.isArray(vms), 'vms array');
|
||||||
|
INST = vms[0];
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
tt.test(' TritonApi listNics', function (t) {
|
||||||
|
if (!INST)
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
function check(val, valName, next) {
|
||||||
|
CLIENT.listNics({id: val}, function (err, nics) {
|
||||||
|
if (h.ifErr(t, err, 'no err ' + valName))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(Array.isArray(nics), 'nics array');
|
||||||
|
NIC = nics[0];
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortId = INST.id.split('-')[0];
|
||||||
|
|
||||||
|
check(INST.id, 'id', function () {
|
||||||
|
check(INST.name, 'name', function () {
|
||||||
|
check(shortId, 'shortId', function () {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
tt.test(' TritonApi getNic', function (t) {
|
||||||
|
if (!NIC)
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
function check(inst, mac, instValName, next) {
|
||||||
|
CLIENT.getNic({id: inst, mac: mac}, function (err, nic) {
|
||||||
|
if (h.ifErr(t, err, 'no err for ' + instValName))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.deepEqual(nic, NIC, instValName);
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortId = INST.id.split('-')[0];
|
||||||
|
|
||||||
|
check(INST.id, NIC.mac, 'id', function () {
|
||||||
|
check(INST.name, NIC.mac, 'name', function () {
|
||||||
|
check(shortId, NIC.mac, 'shortId', function () {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
tt.test(' teardown: client', function (t) {
|
||||||
|
CLIENT.close();
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
252
test/integration/cli-nics.test.js
Normal file
252
test/integration/cli-nics.test.js
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Joyent, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Integration tests for `triton instance nics ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var h = require('./helpers');
|
||||||
|
var f = require('util').format;
|
||||||
|
var os = require('os');
|
||||||
|
var test = require('tape');
|
||||||
|
|
||||||
|
// --- Globals
|
||||||
|
|
||||||
|
var INST_ALIAS = f('nodetritontest-nics-%s', os.hostname());
|
||||||
|
var NETWORK;
|
||||||
|
var INST;
|
||||||
|
var NIC;
|
||||||
|
var NIC2;
|
||||||
|
|
||||||
|
var OPTS = {
|
||||||
|
skip: !h.CONFIG.allowWriteActions
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// --- Tests
|
||||||
|
|
||||||
|
if (OPTS.skip) {
|
||||||
|
console.error('** skipping %s tests', __filename);
|
||||||
|
console.error('** set "allowWriteActions" in test config to enable');
|
||||||
|
}
|
||||||
|
|
||||||
|
test('triton instance nics', OPTS, function (tt) {
|
||||||
|
h.printConfig(tt);
|
||||||
|
|
||||||
|
tt.test(' cleanup existing inst with alias ' + INST_ALIAS, function (t) {
|
||||||
|
h.deleteTestInst(t, INST_ALIAS, function (err) {
|
||||||
|
t.ifErr(err);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' setup: triton instance create', function (t) {
|
||||||
|
h.createTestInst(t, INST_ALIAS, function onInst(err, instId) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance create'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
INST = instId;
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' setup: find network for tests', function (t) {
|
||||||
|
h.triton('network list -j', function onNetworks(err, stdout) {
|
||||||
|
if (h.ifErr(t, err, 'triton network list'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
NETWORK = JSON.parse(stdout.trim().split('\n')[0]);
|
||||||
|
t.ok(NETWORK, 'NETWORK');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic create', function (t) {
|
||||||
|
var cmd = 'instance nic create -j -w ' + INST + ' ' + NETWORK.id;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic create'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
NIC = JSON.parse(stdout);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic get', function (t) {
|
||||||
|
var cmd = 'instance nic get ' + INST + ' ' + NIC.mac;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic get'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
var obj = JSON.parse(stdout);
|
||||||
|
t.equal(obj.mac, NIC.mac, 'nic MAC is correct');
|
||||||
|
t.equal(obj.ip, NIC.ip, 'nic IP is correct');
|
||||||
|
t.equal(obj.network, NIC.network, 'nic network is correct');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic list', function (t) {
|
||||||
|
var cmd = 'instance nic list ' + INST;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic list'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
var nics = stdout.trim().split('\n');
|
||||||
|
t.ok(nics[0].match(/IP\s+MAC\s+STATE\s+NETWORK/), 'nic list' +
|
||||||
|
' header correct');
|
||||||
|
nics.shift();
|
||||||
|
|
||||||
|
t.ok(nics.length >= 1, 'triton nic list expected nic num');
|
||||||
|
|
||||||
|
var testNics = nics.filter(function (nic) {
|
||||||
|
return nic.match(NIC.mac);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.equal(testNics.length, 1, 'triton nic list test nic found');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic list -j', function (t) {
|
||||||
|
var cmd = 'instance nic list -j ' + INST;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic list'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
var nics = stdout.trim().split('\n').map(function (line) {
|
||||||
|
return JSON.parse(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.ok(nics.length >= 1, 'triton nic list expected nic num');
|
||||||
|
|
||||||
|
var testNics = nics.filter(function (nic) {
|
||||||
|
return nic.mac === NIC.mac;
|
||||||
|
});
|
||||||
|
|
||||||
|
t.equal(testNics.length, 1, 'triton nic list test nic found');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic list mac=<...>', function (t) {
|
||||||
|
var cmd = 'instance nic list -j ' + INST + ' mac=' + NIC.mac;
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
var nics = stdout.trim().split('\n').map(function (str) {
|
||||||
|
return JSON.parse(str);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.equal(nics.length, 1);
|
||||||
|
t.equal(nics[0].ip, NIC.ip);
|
||||||
|
t.equal(nics[0].network, NIC.network);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton nic list mac=<...>', function (t) {
|
||||||
|
var cmd = 'instance nic list -j ' + INST + ' mac=' + NIC.mac;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
var nics = stdout.trim().split('\n').map(function (str) {
|
||||||
|
return JSON.parse(str);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.equal(nics.length, 1);
|
||||||
|
t.equal(nics[0].ip, NIC.ip);
|
||||||
|
t.equal(nics[0].network, NIC.network);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic delete', function (t) {
|
||||||
|
var cmd = 'instance nic delete --force ' + INST + ' ' + NIC.mac;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic delete'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(stdout.match('Deleted NIC ' + NIC.mac, 'deleted nic'));
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic create (with NICOPTS)', function (t) {
|
||||||
|
var cmd = 'instance nic create -j -w ' + INST + ' ipv4_uuid=' +
|
||||||
|
NETWORK.id;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic create'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
NIC2 = JSON.parse(stdout);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic with ip get', function (t) {
|
||||||
|
var cmd = 'instance nic get ' + INST + ' ' + NIC2.mac;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic get'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
var obj = JSON.parse(stdout);
|
||||||
|
t.equal(obj.mac, NIC2.mac, 'nic MAC is correct');
|
||||||
|
t.equal(obj.ip, NIC2.ip, 'nic IP is correct');
|
||||||
|
t.equal(obj.network, NIC2.network, 'nic network is correct');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton instance nic with ip delete', function (t) {
|
||||||
|
var cmd = 'instance nic delete --force ' + INST + ' ' + NIC2.mac;
|
||||||
|
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton instance nic with ip delete'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(stdout.match('Deleted NIC ' + NIC2.mac, 'deleted nic'));
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use a timeout, because '-w' on delete doesn't have a way to know if the
|
||||||
|
* attempt failed or if it is just taking a really long time.
|
||||||
|
*/
|
||||||
|
tt.test(' cleanup: triton instance rm INST', {timeout: 10 * 60 * 1000},
|
||||||
|
function (t) {
|
||||||
|
h.deleteTestInst(t, INST_ALIAS, function () {
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -56,6 +56,10 @@ var subs = [
|
|||||||
['instance snapshot list', 'instance snapshot ls', 'instance snapshots'],
|
['instance snapshot list', 'instance snapshot ls', 'instance snapshots'],
|
||||||
['instance snapshot get'],
|
['instance snapshot get'],
|
||||||
['instance snapshot delete', 'instance snapshot rm'],
|
['instance snapshot delete', 'instance snapshot rm'],
|
||||||
|
['instance nic create'],
|
||||||
|
['instance nic list', 'instance nic ls'],
|
||||||
|
['instance nic get'],
|
||||||
|
['instance nic delete', 'instance nic rm'],
|
||||||
['ip'],
|
['ip'],
|
||||||
['ssh'],
|
['ssh'],
|
||||||
['network'],
|
['network'],
|
||||||
|
Reference in New Issue
Block a user