50 * stop(uuid, options={[force=true]}, callback)
51 * sysrq(uuid, req=[nmi|screenshot], options={}, callback)
52 * update(uuid, properties, callback)
53 *
54 * Exported variables:
55 *
56 * logname - you can set this to a string [a-zA-Z_] to use as log name
57 * logger - you can set this to a node-bunyan log stream to capture the logs
58 * INFO_TYPES - list of supported types for the info command
59 * SYSRQ_TYPES - list of supported requests for sysrq
60 */
61
62 // Ensure we're using the platform's node
63 require('/usr/node/node_modules/platform_node_version').assert();
64
65 var assert = require('assert');
66 var async = require('/usr/node/node_modules/async');
67 var bunyan = require('/usr/node/node_modules/bunyan');
68 var cp = require('child_process');
69 var dladm = require('/usr/vm/node_modules/dladm');
70 var EventEmitter = require('events').EventEmitter;
71 var exec = cp.exec;
72 var execFile = cp.execFile;
73 var expat = require('/usr/node/node_modules/node-expat');
74 var fs = require('fs');
75 var fw = require('/usr/fw/lib/fw');
76 var http = require('http');
77 var net = require('net');
78 var path = require('path');
79 var Qmp = require('/usr/vm/node_modules/qmp').Qmp;
80 var spawn = cp.spawn;
81 var sprintf = require('/usr/node/node_modules/sprintf').sprintf;
82 var tty = require('tty');
83 var util = require('util');
84
85 var log_to_file = false;
86
87 // keep the last 512 messages just in case we end up wanting them.
88 var ringbuffer = new bunyan.RingBuffer({ limit: 512 });
89
4208 }
4209 createme.push(d);
4210 }
4211 }
4212 }
4213
4214 function loggedCreateVolume(volume, cb) {
4215 return createVolume(volume, log, cb);
4216 }
4217
4218 // create all the volumes we found that we need.
4219 async.forEachSeries(createme, loggedCreateVolume, function (err) {
4220 if (err) {
4221 callback(err);
4222 } else {
4223 callback();
4224 }
4225 });
4226 }
4227
4228 // writes a Zone's metadata JSON to /zones/<uuid>/config/metadata.json
4229 // and /zones/<uuid>/config/tags.json.
4230 function updateMetadata(vmobj, payload, log, callback)
4231 {
4232 var cmdata = {};
4233 var imdata = {};
4234 var key;
4235 var mdata = {};
4236 var mdata_filename;
4237 var tags = {};
4238 var tags_filename;
4239 var zonepath;
4240
4241 assert(log, 'no logger passed to updateMetadata()');
4242
4243 if (vmobj.hasOwnProperty('zonepath')) {
4244 zonepath = vmobj.zonepath;
4245 } else if (vmobj.hasOwnProperty('zpool')
4246 && vmobj.hasOwnProperty('zonename')) {
4247
4297 // same thing for tags
4298 for (key in vmobj.tags) {
4299 if (vmobj.tags.hasOwnProperty(key)) {
4300 tags[key] = vmobj.tags[key];
4301 if (payload.hasOwnProperty('remove_tags')
4302 && payload.remove_tags.indexOf(key) !== -1) {
4303
4304 // in the remove_* list, don't load it.
4305 delete tags[key];
4306 }
4307 }
4308 }
4309
4310 for (key in payload.set_tags) {
4311 if (payload.set_tags.hasOwnProperty(key)) {
4312 tags[key] = payload.set_tags[key];
4313 }
4314 }
4315
4316 mdata = {'customer_metadata': cmdata, 'internal_metadata': imdata};
4317 fs.writeFile(mdata_filename, JSON.stringify(mdata, null, 2),
4318 function (err) {
4319 if (err) {
4320 callback(err);
4321 } else {
4322 log.debug('wrote metadata to ' + mdata_filename);
4323 fs.writeFile(tags_filename, JSON.stringify(tags, null, 2),
4324 function (e) {
4325 if (e) {
4326 callback(e);
4327 } else {
4328 log.debug('wrote tags to' + tags_filename);
4329 callback();
4330 }
4331 }
4332 );
4333 }
4334 }
4335 );
4336 }
4337
4338 function saveMetadata(payload, log, callback)
4339 {
4340 var protovm = {};
4341
4342 assert(log, 'no logger passed to saveMetadata()');
4343
4344 if (!payload.hasOwnProperty('zonepath')
4345 || !payload.hasOwnProperty('zpool')
4346 || !payload.hasOwnProperty('zonename')) {
4347
4348 callback(new Error('saveMetadata payload is missing zone '
4349 + 'properties.'));
4350 return;
4351 }
4352
4353 protovm.zonepath = payload.zonepath;
4354 protovm.zpool = payload.zpool;
4355 protovm.zonename = payload.zonename;
11988 // the actual updates above already did happen.
11989 cb();
11990 }
11991 );
11992 } else {
11993 cb();
11994 }
11995 }
11996
11997 ], function (err, res) {
11998 log.debug('done applying updates to ' + oldobj.uuid);
11999 callback(err);
12000 });
12001 }
12002
12003 exports.update = function (uuid, payload, options, callback)
12004 {
12005 var log;
12006 var new_vmobj;
12007 var vmobj;
12008
12009 // options parameter is optional
12010 if (arguments.length === 3) {
12011 callback = arguments[2];
12012 options = {};
12013 }
12014
12015 ensureLogging(true);
12016 if (options.hasOwnProperty('log')) {
12017 log = options.log;
12018 } else {
12019 log = VM.log.child({action: 'update', vm: uuid});
12020 }
12021
12022 log.info('Updating VM ' + uuid + ' with initial payload:\n'
12023 + JSON.stringify(payload, null, 2));
12024
12025 async.series([
12026 function (cb) {
12027 // for update we currently always load the whole vmobj since the
12028 // update functions may need to look at bits from the existing VM.
12029 VM.load(uuid, {log: log}, function (err, obj) {
12030 if (err) {
12031 cb(err);
12032 return;
12033 }
12034 vmobj = obj;
12035 cb();
12036 });
12037 }, function (cb) {
12038 normalizePayload(payload, vmobj, log, function (e) {
12039 log.debug('Used payload:\n'
12040 + JSON.stringify(payload, null, 2));
12041 cb(e);
12042 });
12043 }, function (cb) {
12044 var deletables = [];
12045 var to_remove = [];
12046 var n;
12144 }, function (cb) {
12145 // Update the firewall data
12146 updateFirewallData(payload, vmobj, log, cb);
12147 }, function (cb) {
12148 // Do another full reload (all fields) so we can compare in
12149 // applyUpdates() and decide what's changed that we need to apply.
12150 VM.load(uuid, {log: log}, function (e, newobj) {
12151 if (e) {
12152 cb(e);
12153 } else {
12154 new_vmobj = newobj;
12155 cb();
12156 }
12157 });
12158 }, function (cb) {
12159 applyUpdates(vmobj, new_vmobj, payload, log, function () {
12160 cb();
12161 });
12162 }
12163 ], function (e) {
12164 callback(e);
12165 });
12166 };
12167
12168 function kill(uuid, log, callback)
12169 {
12170 var load_fields;
12171 var unset_autoboot = 'set autoboot=false';
12172
12173 assert(log, 'no logger passed to kill()');
12174
12175 log.info('Killing VM ' + uuid);
12176
12177 load_fields = [
12178 'brand',
12179 'state',
12180 'transition_to',
12181 'uuid'
12182 ];
12183
12184 /* We load here to ensure this vm exists. */
|
50 * stop(uuid, options={[force=true]}, callback)
51 * sysrq(uuid, req=[nmi|screenshot], options={}, callback)
52 * update(uuid, properties, callback)
53 *
54 * Exported variables:
55 *
56 * logname - you can set this to a string [a-zA-Z_] to use as log name
57 * logger - you can set this to a node-bunyan log stream to capture the logs
58 * INFO_TYPES - list of supported types for the info command
59 * SYSRQ_TYPES - list of supported requests for sysrq
60 */
61
62 // Ensure we're using the platform's node
63 require('/usr/node/node_modules/platform_node_version').assert();
64
65 var assert = require('assert');
66 var async = require('/usr/node/node_modules/async');
67 var bunyan = require('/usr/node/node_modules/bunyan');
68 var cp = require('child_process');
69 var dladm = require('/usr/vm/node_modules/dladm');
70 var lock = require('/usr/vm/node_modules/locker').lock;
71 var EventEmitter = require('events').EventEmitter;
72 var exec = cp.exec;
73 var execFile = cp.execFile;
74 var expat = require('/usr/node/node_modules/node-expat');
75 var fs = require('fs');
76 var fw = require('/usr/fw/lib/fw');
77 var http = require('http');
78 var net = require('net');
79 var path = require('path');
80 var Qmp = require('/usr/vm/node_modules/qmp').Qmp;
81 var spawn = cp.spawn;
82 var sprintf = require('/usr/node/node_modules/sprintf').sprintf;
83 var tty = require('tty');
84 var util = require('util');
85
86 var log_to_file = false;
87
88 // keep the last 512 messages just in case we end up wanting them.
89 var ringbuffer = new bunyan.RingBuffer({ limit: 512 });
90
4209 }
4210 createme.push(d);
4211 }
4212 }
4213 }
4214
4215 function loggedCreateVolume(volume, cb) {
4216 return createVolume(volume, log, cb);
4217 }
4218
4219 // create all the volumes we found that we need.
4220 async.forEachSeries(createme, loggedCreateVolume, function (err) {
4221 if (err) {
4222 callback(err);
4223 } else {
4224 callback();
4225 }
4226 });
4227 }
4228
4229 function writeAndRename(log, name, destfile, file_data, callback)
4230 {
4231 var tempfile = destfile + '.new';
4232
4233 log.debug('writing ' + name + ' to ' + tempfile);
4234
4235 fs.writeFile(tempfile, file_data, function (err) {
4236 if (err) {
4237 callback(err);
4238 return;
4239 }
4240
4241 log.debug('wrote ' + name + ' to ' + tempfile);
4242 log.debug('renaming from ' + tempfile + ' to ' + destfile);
4243
4244 fs.rename(tempfile, destfile, function (_err) {
4245 if (_err) {
4246 callback(_err);
4247 return;
4248 }
4249
4250 log.debug('renamed from ' + tempfile + ' to ' + destfile);
4251 callback();
4252 });
4253 });
4254 }
4255
4256 // writes a Zone's metadata JSON to /zones/<uuid>/config/metadata.json
4257 // and /zones/<uuid>/config/tags.json.
4258 function updateMetadata(vmobj, payload, log, callback)
4259 {
4260 var cmdata = {};
4261 var imdata = {};
4262 var key;
4263 var mdata = {};
4264 var mdata_filename;
4265 var tags = {};
4266 var tags_filename;
4267 var zonepath;
4268
4269 assert(log, 'no logger passed to updateMetadata()');
4270
4271 if (vmobj.hasOwnProperty('zonepath')) {
4272 zonepath = vmobj.zonepath;
4273 } else if (vmobj.hasOwnProperty('zpool')
4274 && vmobj.hasOwnProperty('zonename')) {
4275
4325 // same thing for tags
4326 for (key in vmobj.tags) {
4327 if (vmobj.tags.hasOwnProperty(key)) {
4328 tags[key] = vmobj.tags[key];
4329 if (payload.hasOwnProperty('remove_tags')
4330 && payload.remove_tags.indexOf(key) !== -1) {
4331
4332 // in the remove_* list, don't load it.
4333 delete tags[key];
4334 }
4335 }
4336 }
4337
4338 for (key in payload.set_tags) {
4339 if (payload.set_tags.hasOwnProperty(key)) {
4340 tags[key] = payload.set_tags[key];
4341 }
4342 }
4343
4344 mdata = {'customer_metadata': cmdata, 'internal_metadata': imdata};
4345
4346 async.series([
4347 function (next) {
4348 writeAndRename(log, 'metadata', mdata_filename,
4349 JSON.stringify(mdata, null, 2), next);
4350 },
4351 function (next) {
4352 writeAndRename(log, 'tags', tags_filename,
4353 JSON.stringify(tags, null, 2), next);
4354 }
4355 ], callback);
4356 }
4357
4358 function saveMetadata(payload, log, callback)
4359 {
4360 var protovm = {};
4361
4362 assert(log, 'no logger passed to saveMetadata()');
4363
4364 if (!payload.hasOwnProperty('zonepath')
4365 || !payload.hasOwnProperty('zpool')
4366 || !payload.hasOwnProperty('zonename')) {
4367
4368 callback(new Error('saveMetadata payload is missing zone '
4369 + 'properties.'));
4370 return;
4371 }
4372
4373 protovm.zonepath = payload.zonepath;
4374 protovm.zpool = payload.zpool;
4375 protovm.zonename = payload.zonename;
12008 // the actual updates above already did happen.
12009 cb();
12010 }
12011 );
12012 } else {
12013 cb();
12014 }
12015 }
12016
12017 ], function (err, res) {
12018 log.debug('done applying updates to ' + oldobj.uuid);
12019 callback(err);
12020 });
12021 }
12022
12023 exports.update = function (uuid, payload, options, callback)
12024 {
12025 var log;
12026 var new_vmobj;
12027 var vmobj;
12028 var unlock;
12029 var lockpath;
12030
12031 // options parameter is optional
12032 if (arguments.length === 3) {
12033 callback = arguments[2];
12034 options = {};
12035 }
12036
12037 ensureLogging(true);
12038 if (options.hasOwnProperty('log')) {
12039 log = options.log;
12040 } else {
12041 log = VM.log.child({action: 'update', vm: uuid});
12042 }
12043
12044 log.info('Updating VM ' + uuid + ' with initial payload:\n'
12045 + JSON.stringify(payload, null, 2));
12046
12047 async.series([
12048 function (cb) {
12049 lockpath = '/var/run/vm.' + uuid + '.config.lockfile';
12050 log.debug('acquiring lock on ' + lockpath);
12051 lock(lockpath, function (err, _unlock) {
12052 log.debug('acquired lock on ' + lockpath);
12053 if (err) {
12054 cb(err);
12055 return;
12056 }
12057 unlock = _unlock;
12058 cb();
12059 });
12060 },
12061 function (cb) {
12062 // for update we currently always load the whole vmobj since the
12063 // update functions may need to look at bits from the existing VM.
12064 VM.load(uuid, {log: log}, function (err, obj) {
12065 if (err) {
12066 cb(err);
12067 return;
12068 }
12069 vmobj = obj;
12070 cb();
12071 });
12072 }, function (cb) {
12073 normalizePayload(payload, vmobj, log, function (e) {
12074 log.debug('Used payload:\n'
12075 + JSON.stringify(payload, null, 2));
12076 cb(e);
12077 });
12078 }, function (cb) {
12079 var deletables = [];
12080 var to_remove = [];
12081 var n;
12179 }, function (cb) {
12180 // Update the firewall data
12181 updateFirewallData(payload, vmobj, log, cb);
12182 }, function (cb) {
12183 // Do another full reload (all fields) so we can compare in
12184 // applyUpdates() and decide what's changed that we need to apply.
12185 VM.load(uuid, {log: log}, function (e, newobj) {
12186 if (e) {
12187 cb(e);
12188 } else {
12189 new_vmobj = newobj;
12190 cb();
12191 }
12192 });
12193 }, function (cb) {
12194 applyUpdates(vmobj, new_vmobj, payload, log, function () {
12195 cb();
12196 });
12197 }
12198 ], function (e) {
12199 // If we were able to hold the lockfile, and thus have an unlock
12200 // callback, we must call it before returning, whether or not
12201 // there was an error.
12202 if (unlock) {
12203 log.debug('releasing lock on ' + lockpath);
12204 unlock(function (unlock_err) {
12205 if (unlock_err) {
12206 log.error(err, 'unlock error! (path ' + lockpath + ')');
12207 } else {
12208 log.debug('released lock on ' + lockpath);
12209 }
12210 callback(e);
12211 });
12212 } else {
12213 callback(e);
12214 }
12215 });
12216 };
12217
12218 function kill(uuid, log, callback)
12219 {
12220 var load_fields;
12221 var unset_autoboot = 'set autoboot=false';
12222
12223 assert(log, 'no logger passed to kill()');
12224
12225 log.info('Killing VM ' + uuid);
12226
12227 load_fields = [
12228 'brand',
12229 'state',
12230 'transition_to',
12231 'uuid'
12232 ];
12233
12234 /* We load here to ensure this vm exists. */
|