diff --git a/lib/cli.js b/lib/cli.js index 9c2ab71..32caadf 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,34 +1,75 @@ +var DatasetManifest = require('./dataset_manifest'); var DiskImage = require('./disk_image'); var async = require('async'); +var crypto = require('crypto'); var execFile = require('child_process').execFile; var fs = require('fs'); +var fs = require('fs'); var optparse = require('optparse'); var path = require('path'); var util = require('util'); -var xml2js = require('xml2js'); -var crypto = require('crypto'); -var fs = require('fs'); var uuid = require('node-uuid'); +var xml2js = require('xml2js'); -var DatasetManifest = require('./dataset_manifest'); +function sha1file (filename, callback) { + var shasum = crypto.createHash('sha1'); + var s = fs.ReadStream(filename); + s.on('data', function(d) { + shasum.update(d); + }); + + s.on('end', function() { + var d = shasum.digest('hex'); + return callback(null, d); + }); +} + +function replaceFilenameExtension (filename, newExt) { + return ( path.join + ( path.dirname(filename) + , path.basename + ( filename + , path.extname(filename) + ) + newExt + ) + ); +} var CLI = module.exports = function () {} CLI.prototype.parseOptions = function () { var self = this; var switches - = [ ['-h', '--help', 'This help message.'] - , ['-n', '--ds-name VALUE', 'Short name for the dataset.'] - , ['-v', '--ds-version VALUE', 'Semantic version of dataset.'] - , ['-d', '--ds-description VALUE', 'Short description of dataset (to max. of 255 bytes).'] - , ['-u', '--assets-url VALUE', 'Assets location url'] + = [ ['-h', '--help', 'This help message'] + , ['-n', '--ds-name VALUE', 'Short name for the dataset'] + , ['-v', '--ds-version VALUE', 'Semantic version of dataset'] + , ['-d', '--ds-description VALUE', + 'Short description of dataset (to max. of 255 bytes)'] + , ['-o', '--ds-os VALUE', + 'The dataset operating system. (ie. linux, smartos, etc)'] + , ['-a', '--assets-url VALUE', 'Assets location url'] + , ['-D', '--ds-disk-driver VALUE', 'Set the VM\'s disk driver (default: virtio)'] + , ['-N', '--ds-nic-driver VALUE', 'Set the VM\'s NIC driver (default: virtio)'] ]; + var examples + = [ + { description: + "bleh" + , example: + "$0 --assets-url http://10.99.99.6/datasets/ --dataset-os linux" + + "--name mylinux ./vms/mylinux.ovf /usbkey/datasets" + } + ] + var options = this.options = {}; var parser = new optparse.OptionParser(switches); parser.banner = [ "Usage:" - , " " + [process.argv[0], process.argv[1], "[options] [output-directory]"].join(' ') + , " " + [ process.argv[0] + , process.argv[1] + , "[options] [output-directory]" + ].join(' ') ].join("\n"); parser.on(2, function (value) { @@ -43,18 +84,22 @@ CLI.prototype.parseOptions = function () { self.displayHelp(parser.toString()); }); - parser.on('ds-name', function (ds_name) { + parser.on('ds-name', function (name, ds_name) { options.ds_name = ds_name; }); - parser.on('ds-version', function (ds_version) { + parser.on('ds-version', function (name, ds_version) { options.ds_version = ds_version; }); - parser.on('ds-uuid', function (ds_uuid) { + parser.on('ds-uuid', function (name, ds_uuid) { options.ds_uuid = ds_uuid; }); + parser.on('ds-os', function (name, ds_os) { + options.ds_os = ds_os; + }); + parser.on('assets-url', function (name, assets_url) { options.assets_url = assets_url; }); @@ -68,6 +113,12 @@ CLI.prototype.parseOptions = function () { process.exit(1); } + if (!options.ds_os) { + console.error("Error: --ds-os must be specified\n"); + self.displayHelp(parser.toString()); + process.exit(1); + } + self.ovfFilename = options.input; if (!options.outputDir) { options.outputDir = '.'; @@ -95,7 +146,6 @@ CLI.prototype.start = function () { , self.writeDatasetManifest.bind(self) ] , function (error) { - console.dir(error); console.log("All done!"); } ); @@ -123,7 +173,7 @@ CLI.prototype.mkdir = function (callback) { callback(); } else { - fs.mkdir(self.options.outputDir, 0755, function (error) { + fs.mkdir(self.options.outputDir, parseInt('0755', 8), function (error) { if (error) { throw new Error(error.toString); } @@ -164,7 +214,7 @@ CLI.prototype.verifyFiles = function (callback) { console.error("Digest mismatch for file " + filename); process.exit(1); } - fileDigests[filename] = computedDigest; + self.fileDigests[filename] = computedDigest; callback(); }); } @@ -196,14 +246,17 @@ CLI.prototype.createDatasetManifest = function (callback) { var VirtualSystem = ovf.VirtualSystem; self.parseDisks(ovf); - self.parseVirtualSytemSection(ovf); +// self.parseVirtualSytemSection(ovf); self.parseNetworkSection(ovf); manifest.name = this.options.ds_name || VirtualSystem['@']['ovf:id']; manifest.requirements = {}; - manifest.type = 'vmimage'; + manifest.type = 'zvol'; manifest.uuid = this.options.ds_uuid || uuid().toLowerCase(); manifest.version = this.options.ds_version || '1.0.0'; + manifest.os = this.options.ds_os || manifest.name; + manifest.disk_driver = this.options.disk_driver || 'virtio'; + manifest.nic_driver = this.options.nic_driver || 'virtio'; return callback(); } @@ -211,8 +264,6 @@ CLI.prototype.createDatasetManifest = function (callback) { CLI.prototype.populateFiles = function (callback) { var self = this; - console.dir(self.files); - async.forEach ( Object.keys(self.files) , function (file, callback) { @@ -234,7 +285,7 @@ CLI.prototype.populateFiles = function (callback) { , function (callback) { console.log("Verifying file " + outputFile); sha1file(outputFile, function (error, sha) { - self.fileDigests[file] = record.sha = sha; + self.fileDigests[file] = record.sha1 = sha; callback(); }); } @@ -251,19 +302,8 @@ CLI.prototype.populateFiles = function (callback) { ); } -function sha1file (filename, callback) { - var shasum = crypto.createHash('sha1'); - var s = fs.ReadStream(filename); - s.on('data', function(d) { - shasum.update(d); - }); - - s.on('end', function() { - var d = shasum.digest('hex'); - return callback(null, d); - }); -} +/* CLI.prototype.parseVirtualSytemSection = function (obj, callback) { var self = this; var VirtualHardwareSection = obj.VirtualSystem.VirtualHardwareSection; @@ -277,12 +317,13 @@ CLI.prototype.parseVirtualSytemSection = function (obj, callback) { break; } }); -} +} */ CLI.prototype.parseNetworkSection = function (obj) { var self = this; var NetworkSection = obj.NetworkSection; var Network; + var nets = []; if (NetworkSection.Network) { if (Array.isArray(NetworkSection.Network)) { @@ -292,12 +333,11 @@ CLI.prototype.parseNetworkSection = function (obj) { Network = [ NetworkSection.Network ]; } - var nets = []; - var count = 0; + var count = 0; - Network.forEach(function (n) { - nets.push({ name: 'net'+count++, description: n.Description }); - }); + Network.forEach(function (n) { + nets.push({ name: 'net'+count++, description: n.Description }); + }); } // Scan Networks @@ -326,6 +366,11 @@ CLI.prototype.parseDisks = function () { var Files = self.getAsArray(ovf.References, 'File'); var Disks = self.getAsArray(ovf.DiskSection, 'Disk'); + if (Disks.length > 1) { + throw new Error( + "sdc-convertm does not support .ovf's describing multiple disks"); + } + Files.forEach(function (File) { var file = files[File['@']['ovf:id']] @@ -335,11 +380,16 @@ CLI.prototype.parseDisks = function () { var href = File['@']['ovf:href']; var m = href.match(/^(\w+):\/\//); if (m) { - throw new Error ("OVF disk referenced file with unsupported href type: " + m[1]); + throw new Error( + "OVF disk referenced file with unsupported href type: " + m[1]); } file.path = path.join(dirname, href); file.href = href; - file.outputFile = path.join(self.options.outputDir, replaceFilenameExtension(href, '.zfs.bz2')); + file.outputFile + = path.join + ( self.options.outputDir + , replaceFilenameExtension(href, '.zfs.bz2') + ); }); Disks.forEach(function (Disk) { @@ -365,6 +415,8 @@ CLI.prototype.parseDisks = function () { + allocUnits); } } + + self.manifest.image_size = disk.capacityBytes / (1024*1024); }); } @@ -387,10 +439,8 @@ CLI.prototype.createDiskImages = function (callback) { ( Object.keys(self.disks) , function (diskId, callback) { var disk = self.disks[diskId]; - console.dir(self.disks[disk]); var diskImage = new DiskImage(); - console.dir(disk); var opts = { inputFile: disk.file.path , outputFile: disk.file.outputFile @@ -398,10 +448,8 @@ CLI.prototype.createDiskImages = function (callback) { , zpool: self.options.zpool , format: disk.format }; - console.dir(opts); diskImage.convertToZfsStream(opts, function (error) { - console.dir(arguments); console.log("Done converting " + disk); callback(); }); @@ -412,17 +460,6 @@ CLI.prototype.createDiskImages = function (callback) { ); } -function replaceFilenameExtension (filename, newExt) { - return ( path.join - ( path.dirname(filename) - , path.basename - ( filename - , path.extname(filename) - ) + newExt - ) - ); -} - var OVF = function () {} OVF.prototype.parse = function (opts) { diff --git a/lib/dataset_manifest.js b/lib/dataset_manifest.js index a40f791..e7f9dac 100644 --- a/lib/dataset_manifest.js +++ b/lib/dataset_manifest.js @@ -1,6 +1,8 @@ var DatasetManifest = module.exports = function () { var self = this; - var simpleKeys = 'name type uuid version files'.split(' '); + var simpleKeys = + [ 'name', 'type', 'uuid', 'version', 'image_size', 'files', 'os' + , 'disk_driver', 'nic_driver']; this.manifest = { files: [], requirements: { networks : [] }}; var requirements = this.manifest.requirements; diff --git a/lib/disk_image.js b/lib/disk_image.js index aeacbc8..2c9ebc9 100644 --- a/lib/disk_image.js +++ b/lib/disk_image.js @@ -15,6 +15,10 @@ var async = require('async'); * 4. zfs destroy */ +function randstr () { + return Math.floor(Math.random() * 0xffffffff).toString(16); +} + var DiskImage = module.exports = function () {} @@ -56,10 +60,15 @@ DiskImage.prototype.convertToZfsStream = function (opts, callback) { console.error(error); } console.log("All done!"); - zfs.destroyAll - ( self.zvolName + zfs.destroy + ( this.zvolSnapshotName , function (error, stdout, stderr) { - if (callback) return callback(); + zfs.destroyAll + ( self.zvolName + , function (error, stdout, stderr) { + if (callback) return callback(); + } + ); } ); } @@ -96,11 +105,15 @@ DiskImage.prototype._vmdkToZvol = function (callback) { console.log(data.toString()); }); + var stdout = ''; + var stderr = ''; child.stderr.on('data', function (data) { console.error(data.toString()); }); child.on('exit', function (code) { + console.log("qemu-img exited"); + console.dir(arguments); if (code) { return callback(new Error(stderr.toString())); } @@ -114,7 +127,7 @@ DiskImage.prototype._snapshotZvol = function (callback) { zfs.snapshot(self.zvolName + '@qemu-img-convert', function (error) { if (error) { - return callback(new Error(stderr.toString())); + return callback(error); } return callback(); }); @@ -123,7 +136,8 @@ DiskImage.prototype._snapshotZvol = function (callback) { DiskImage.prototype._zfsSendSnapshot = function (callback) { var self = this; exec - ( '/usr/sbin/zfs send ' + self.zvolSnapshotName + ' | bzip2 > ' + self.outputFile + ( '/usr/sbin/zfs send ' + self.zvolSnapshotName + + ' | bzip2 > ' + self.outputFile , {} , function (error, stdout, stderr) { if (error) { @@ -133,7 +147,3 @@ DiskImage.prototype._zfsSendSnapshot = function (callback) { } ); } - -function randstr () { - return Math.floor(Math.random() * 0xffffffff).toString(16); -}; diff --git a/package.json b/package.json index 247291b..011cfe7 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,7 @@ "main": "bin/sdc-convertvm", "directories": { "lib": "lib", - "bin": "bin", - "node_modules": "node_modules" + "bin": "bin" }, "bin": { "sdc-convertvm": "./bin/sdc-convertvm.js" } }