1 var VM = require('/usr/vm/node_modules/VM');
2 var ZWatch = require('./zwatch');
3 var common = require('./common');
4 var async = require('/usr/node/node_modules/async');
5 var execFile = require('child_process').execFile;
6 var fs = require('fs');
7 var net = require('net');
8 var path = require('path');
9 var util = require('util');
10 var zsock = require('/usr/node/node_modules/zsock');
11 var zutil = require('/usr/node/node_modules/zutil');
12
13 var sdc_fields = [
14 'alias',
15 'billing_id',
16 'brand',
17 'cpu_cap',
18 'cpu_shares',
19 'create_timestamp',
20 'server_uuid',
21 'image_uuid',
22 'datacenter_name',
23 'do_not_inventory',
466 };
467
468 MetadataAgent.prototype.makeMetadataHandler = function (zone, socket) {
469 var self = this;
470 var zlog = self.zlog[zone];
471 var write = function (str) {
472 if (socket.writable) {
473 socket.write(str);
474 } else {
475 zlog.error('Socket for ' + zone + ' closed before we could write '
476 + 'anything.');
477 }
478 };
479
480 return function (data) {
481 var cmd;
482 var parts;
483 var val;
484 var vmobj;
485 var want;
486
487 parts = rtrim(data.toString()).replace(/\n$/, '')
488 .match(/^([^\s]+)\s?(.*)/);
489
490 if (!parts) {
491 write('invalid command\n');
492 return;
493 }
494
495 cmd = parts[1];
496 want = parts[2];
497
498 if (cmd === 'GET' && !want) {
499 write('invalid command\n');
500 return;
501 }
502
503 vmobj = self.zones[zone];
504
505 if (cmd === 'GET') {
506 zlog.info('Serving ' + want);
507 if (want.slice(0, 4) === 'sdc:') {
508 want = want.slice(4);
509
510 // NOTE: sdc:nics, sdc:resolvers and sdc:routes are not a
511 // committed interface, do not rely on it. At this point it
512 // should only be used by mdata-fetch, if you add a consumer
513 // that depends on it, please add a note about that here
514 // otherwise expect it will be removed on you sometime.
515 if (want === 'nics' && vmobj.hasOwnProperty('nics')) {
516 val = JSON.stringify(vmobj.nics);
517 returnit(null, val);
518 return;
519 } else if (want === 'resolvers'
520 && vmobj.hasOwnProperty('resolvers')) {
521
522 // resolvers and routes are special because we might reload
523 // metadata trying to get the new ones w/o zone reboot. To
524 // ensure these are fresh we always run updateZone which
601 if (err) {
602 returnit(new Error('Unable to load metadata: '
603 + err.message));
604 return;
605 }
606
607 if (want.match(/_pw$/)) {
608 which_mdata = 'internal_metadata';
609 }
610
611 if (vmobj.hasOwnProperty(which_mdata)) {
612 returnit(null, vmobj[which_mdata][want]);
613 return;
614 } else {
615 returnit(new Error('Zone did not contain '
616 + which_mdata));
617 return;
618 }
619 });
620 }
621 } else if (cmd === 'KEYS') {
622 addMetadata(function (err) {
623 var ckeys = [];
624 var ikeys = [];
625
626 if (err) {
627 returnit(new Error('Unable to load metadata: '
628 + err.message));
629 return;
630 }
631
632 // *_pw$ keys come from internal_metadata, everything else comes
633 // from customer_metadata
634 ckeys = Object.keys(vmobj.customer_metadata)
635 .filter(function (k) {
636
637 return (!k.match(/_pw$/));
638 });
639 ikeys = Object.keys(vmobj.internal_metadata)
640 .filter(function (k) {
715 }
716
717 try {
718 json = JSON.parse(file_data.toString());
719 mdata_types.forEach(function (mdata) {
720 if (json.hasOwnProperty(mdata)) {
721 vmobj[mdata] = json[mdata];
722 }
723 });
724 cb();
725 } catch (e) {
726 zlog.error({err: e}, 'unable to load metadata.json for '
727 + zone + ': ' + e.message);
728 cb(e);
729 }
730
731 return;
732 });
733 }
734
735 function returnit(error, retval) {
736 var towrite;
737
738 if (error) {
739 zlog.error(error.message);
740 write('FAILURE\n');
741 return;
742 }
743
744 // String value
745 if (common.isString(retval)) {
746 towrite = retval.replace(/^\./mg, '..');
747 write('SUCCESS\n' + towrite + '\n.\n');
748 return;
749 } else if (!isNaN(retval)) {
750 towrite = retval.toString().replace(/^\./mg, '..');
751 write('SUCCESS\n' + towrite + '\n.\n');
752 return;
753 } else if (retval) {
754 // Non-string value
755 write('FAILURE\n');
756 return;
757 } else {
758 // Nothing to return
759 write('NOTFOUND\n');
760 return;
761 }
762 }
763 };
764 };
|
1 // vim: set ts=4 sts=4 sw=4 et:
2 var VM = require('/usr/vm/node_modules/VM');
3 var ZWatch = require('./zwatch');
4 var common = require('./common');
5 var crc32 = require('./crc32');
6 var async = require('/usr/node/node_modules/async');
7 var execFile = require('child_process').execFile;
8 var fs = require('fs');
9 var net = require('net');
10 var path = require('path');
11 var util = require('util');
12 var zsock = require('/usr/node/node_modules/zsock');
13 var zutil = require('/usr/node/node_modules/zutil');
14
15 var sdc_fields = [
16 'alias',
17 'billing_id',
18 'brand',
19 'cpu_cap',
20 'cpu_shares',
21 'create_timestamp',
22 'server_uuid',
23 'image_uuid',
24 'datacenter_name',
25 'do_not_inventory',
468 };
469
470 MetadataAgent.prototype.makeMetadataHandler = function (zone, socket) {
471 var self = this;
472 var zlog = self.zlog[zone];
473 var write = function (str) {
474 if (socket.writable) {
475 socket.write(str);
476 } else {
477 zlog.error('Socket for ' + zone + ' closed before we could write '
478 + 'anything.');
479 }
480 };
481
482 return function (data) {
483 var cmd;
484 var parts;
485 var val;
486 var vmobj;
487 var want;
488 var reqid;
489 var req_is_v2 = false;
490
491 parts = rtrim(data.toString()).replace(/\n$/, '')
492 .match(/^([^\s]+)\s?(.*)/);
493
494 if (!parts) {
495 write('invalid command\n');
496 return;
497 }
498
499 cmd = parts[1];
500 want = parts[2];
501
502 if ((cmd === 'NEGOTIATE' || cmd === 'GET') && !want) {
503 write('invalid command\n');
504 return;
505 }
506
507 vmobj = self.zones[zone];
508
509 // Unbox V2 protocol frames:
510 if (cmd === 'V2') {
511 if (!parse_v2_request(want))
512 return;
513 }
514
515 if (cmd === 'GET') {
516 zlog.info('Serving ' + want);
517 if (want.slice(0, 4) === 'sdc:') {
518 want = want.slice(4);
519
520 // NOTE: sdc:nics, sdc:resolvers and sdc:routes are not a
521 // committed interface, do not rely on it. At this point it
522 // should only be used by mdata-fetch, if you add a consumer
523 // that depends on it, please add a note about that here
524 // otherwise expect it will be removed on you sometime.
525 if (want === 'nics' && vmobj.hasOwnProperty('nics')) {
526 val = JSON.stringify(vmobj.nics);
527 returnit(null, val);
528 return;
529 } else if (want === 'resolvers'
530 && vmobj.hasOwnProperty('resolvers')) {
531
532 // resolvers and routes are special because we might reload
533 // metadata trying to get the new ones w/o zone reboot. To
534 // ensure these are fresh we always run updateZone which
611 if (err) {
612 returnit(new Error('Unable to load metadata: '
613 + err.message));
614 return;
615 }
616
617 if (want.match(/_pw$/)) {
618 which_mdata = 'internal_metadata';
619 }
620
621 if (vmobj.hasOwnProperty(which_mdata)) {
622 returnit(null, vmobj[which_mdata][want]);
623 return;
624 } else {
625 returnit(new Error('Zone did not contain '
626 + which_mdata));
627 return;
628 }
629 });
630 }
631 } else if (cmd === 'NEGOTIATE') {
632 if (want === 'V2') {
633 write('V2_OK\n');
634 } else {
635 write('FAILURE\n');
636 }
637 return;
638 } else if (req_is_v2 && cmd === 'DELETE') {
639 want = want.trim();
640
641 if (!want) {
642 returnit(new Error('Invalid DELETE Request'));
643 return;
644 }
645
646 setMetadata(want, null, function (err) {
647 if (err) {
648 returnit(err);
649 } else {
650 returnit(null, 'OK');
651 }
652 });
653 } else if (req_is_v2 && cmd === 'PUT') {
654 var key;
655 var value;
656 var terms;
657
658 terms = want.trim().split(' ');
659
660 if (terms.length !== 2) {
661 returnit(new Error('Invalid PUT Request'));
662 return;
663 }
664
665 // PUT requests have two space-separated BASE64-encoded
666 // arguments: the Key and then the Value.
667 key = (new Buffer(terms[0], 'base64')).toString().trim();
668 value = (new Buffer(terms[1], 'base64')).toString();
669
670 if (!key) {
671 returnit(new Error('Invalid PUT Request'));
672 return;
673 }
674
675 if (key.slice(0, 4) === 'sdc:') {
676 returnit(new Error('Cannot update the "sdc" Namespace.'));
677 return;
678 }
679
680 zlog.info('PUT of key "' + key + '"');
681 setMetadata(key, value, function (err) {
682 if (err) {
683 zlog.error(err, 'could not set metadata (key "' + key
684 + '")');
685 returnit(err);
686 } else {
687 returnit(null, 'OK');
688 }
689 });
690
691 return;
692 } else if (cmd === 'KEYS') {
693 addMetadata(function (err) {
694 var ckeys = [];
695 var ikeys = [];
696
697 if (err) {
698 returnit(new Error('Unable to load metadata: '
699 + err.message));
700 return;
701 }
702
703 // *_pw$ keys come from internal_metadata, everything else comes
704 // from customer_metadata
705 ckeys = Object.keys(vmobj.customer_metadata)
706 .filter(function (k) {
707
708 return (!k.match(/_pw$/));
709 });
710 ikeys = Object.keys(vmobj.internal_metadata)
711 .filter(function (k) {
786 }
787
788 try {
789 json = JSON.parse(file_data.toString());
790 mdata_types.forEach(function (mdata) {
791 if (json.hasOwnProperty(mdata)) {
792 vmobj[mdata] = json[mdata];
793 }
794 });
795 cb();
796 } catch (e) {
797 zlog.error({err: e}, 'unable to load metadata.json for '
798 + zone + ': ' + e.message);
799 cb(e);
800 }
801
802 return;
803 });
804 }
805
806 function setMetadata(_key, _value, cb) {
807 var payload = {};
808 var which = 'customer_metadata';
809
810 // Some keys come from "internal_metadata":
811 if (_key.match(/_pw$/) || _key === 'operator-script') {
812 which = 'internal_metadata';
813 }
814
815 // Construct payload for VM.update()
816 if (_value) {
817 payload['set_' + which] = {};
818 payload['set_' + which][_key] = _value;
819 } else {
820 payload['remove_' + which] = [ _key ];
821 }
822
823 zlog.trace({ payload: payload }, 'calling VM.update()');
824 VM.update(vmobj.uuid, payload, zlog, cb);
825 }
826
827 function parse_v2_request(inframe) {
828 var m;
829 var m2;
830 var newcrc32;
831 var framecrc32;
832 var clen;
833
834 m = inframe.match(
835 /\s*([0-9]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+(.*)/);
836 if (!m) {
837 zlog.error('V2 frame did not parse: ', inframe);
838 return (false);
839 }
840
841 clen = Number(m[1]);
842 if (!(clen > 0) || clen !== (m[3] + ' ' + m[4]).length) {
843 zlog.error('V2 invalid clen: ' + m[1]);
844 return (false);
845 }
846
847 newcrc32 = crc32.crc32_calc(m[3] + ' ' + m[4]);
848 framecrc32 = m[2];
849 if (framecrc32 !== newcrc32) {
850 zlog.error('V2 crc mismatch (ours ' + newcrc32
851 + ' theirs ' + framecrc32 + '): ' + want);
852 return (false);
853 }
854
855 reqid = m[3];
856
857 m2 = m[4].match(/\s*(\S+)\s*(.*)/);
858 if (!m2) {
859 zlog.error('V2 invalid body: ' + m[4]);
860 return (false);
861 }
862
863 cmd = m2[1];
864 want = (new Buffer(m2[2], 'base64')).toString('utf8');
865
866 req_is_v2 = true;
867
868 return (true);
869 }
870
871
872 function format_v2_response(code, body) {
873 var resp;
874 var fullresp;
875
876 resp = reqid + ' ' + code;
877 if (body)
878 resp += ' ' + (new Buffer(body).toString('base64'));
879
880 fullresp = 'V2 ' + resp.length + ' ' + crc32.crc32_calc(
881 resp) + ' ' + resp + '\n';
882
883 zlog.trace({ response: fullresp }, 'formatted V2 response');
884
885 return (fullresp);
886 }
887
888 function returnit(error, retval) {
889 var towrite;
890
891 if (error) {
892 zlog.error(error.message);
893 if (req_is_v2)
894 write(format_v2_response('FAILURE', error.message));
895 else
896 write('FAILURE\n');
897 return;
898 }
899
900 // String value
901 if (common.isString(retval)) {
902 if (req_is_v2) {
903 write(format_v2_response('SUCCESS', retval));
904 } else {
905 towrite = retval.replace(/^\./mg, '..');
906 write('SUCCESS\n' + towrite + '\n.\n');
907 }
908 return;
909 } else if (!isNaN(retval)) {
910 if (req_is_v2) {
911 write(format_v2_response('SUCCESS', retval.toString()));
912 } else {
913 towrite = retval.toString().replace(/^\./mg, '..');
914 write('SUCCESS\n' + towrite + '\n.\n');
915 }
916 return;
917 } else if (retval) {
918 // Non-string value
919 if (req_is_v2)
920 write(format_v2_response('FAILURE'));
921 else
922 write('FAILURE\n');
923 return;
924 } else {
925 // Nothing to return
926 if (req_is_v2)
927 write(format_v2_response('NOTFOUND'));
928 else
929 write('NOTFOUND\n');
930 return;
931 }
932 }
933 };
934 };
|