Print this page
OS-2573 metadata agent support for PUT, DELETE and V2 protocol

Split Close
Expand all
Collapse all
          --- old/src/vm/lib/metadata/agent.js
          +++ new/src/vm/lib/metadata/agent.js
        1 +// vim: set ts=4 sts=4 sw=4 et:
   1    2  var VM  = require('/usr/vm/node_modules/VM');
   2    3  var ZWatch = require('./zwatch');
   3    4  var common = require('./common');
        5 +var crc32 = require('./crc32');
   4    6  var async = require('/usr/node/node_modules/async');
   5    7  var execFile = require('child_process').execFile;
   6    8  var fs = require('fs');
   7    9  var net = require('net');
   8   10  var path = require('path');
   9   11  var util = require('util');
  10   12  var zsock = require('/usr/node/node_modules/zsock');
  11   13  var zutil = require('/usr/node/node_modules/zutil');
  12   14  
  13   15  var sdc_fields = [
↓ open down ↓ 462 lines elided ↑ open up ↑
 476  478                  + 'anything.');
 477  479          }
 478  480      };
 479  481  
 480  482      return function (data) {
 481  483          var cmd;
 482  484          var parts;
 483  485          var val;
 484  486          var vmobj;
 485  487          var want;
      488 +        var reqid;
      489 +        var req_is_v2 = false;
 486  490  
 487  491          parts = rtrim(data.toString()).replace(/\n$/, '')
 488  492              .match(/^([^\s]+)\s?(.*)/);
 489  493  
 490  494          if (!parts) {
 491  495              write('invalid command\n');
 492  496              return;
 493  497          }
 494  498  
 495  499          cmd = parts[1];
 496  500          want = parts[2];
 497  501  
 498      -        if (cmd === 'GET' && !want) {
      502 +        if ((cmd === 'NEGOTIATE' || cmd === 'GET') && !want) {
 499  503              write('invalid command\n');
 500  504              return;
 501  505          }
 502  506  
 503  507          vmobj = self.zones[zone];
 504  508  
      509 +        // Unbox V2 protocol frames:
      510 +        if (cmd === 'V2') {
      511 +            if (!parse_v2_request(want))
      512 +                return;
      513 +        }
      514 +
 505  515          if (cmd === 'GET') {
 506  516              zlog.info('Serving ' + want);
 507  517              if (want.slice(0, 4) === 'sdc:') {
 508  518                  want = want.slice(4);
 509  519  
 510  520                  // NOTE: sdc:nics, sdc:resolvers and sdc:routes are not a
 511  521                  // committed interface, do not rely on it. At this point it
 512  522                  // should only be used by mdata-fetch, if you add a consumer
 513  523                  // that depends on it, please add a note about that here
 514  524                  // otherwise expect it will be removed on you sometime.
↓ open down ↓ 96 lines elided ↑ open up ↑
 611  621                      if (vmobj.hasOwnProperty(which_mdata)) {
 612  622                          returnit(null, vmobj[which_mdata][want]);
 613  623                          return;
 614  624                      } else {
 615  625                          returnit(new Error('Zone did not contain '
 616  626                              + which_mdata));
 617  627                          return;
 618  628                      }
 619  629                  });
 620  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;
 621  692          } else if (cmd === 'KEYS') {
 622  693              addMetadata(function (err) {
 623  694                  var ckeys = [];
 624  695                  var ikeys = [];
 625  696  
 626  697                  if (err) {
 627  698                      returnit(new Error('Unable to load metadata: '
 628  699                          + err.message));
 629  700                      return;
 630  701                  }
↓ open down ↓ 94 lines elided ↑ open up ↑
 725  796                  } catch (e) {
 726  797                      zlog.error({err: e}, 'unable to load metadata.json for '
 727  798                          + zone + ': ' + e.message);
 728  799                      cb(e);
 729  800                  }
 730  801  
 731  802                  return;
 732  803              });
 733  804          }
 734  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 +
 735  888          function returnit(error, retval) {
 736  889              var towrite;
 737  890  
 738  891              if (error) {
 739  892                  zlog.error(error.message);
 740      -                write('FAILURE\n');
      893 +                if (req_is_v2)
      894 +                    write(format_v2_response('FAILURE', error.message));
      895 +                else
      896 +                    write('FAILURE\n');
 741  897                  return;
 742  898              }
 743  899  
 744  900              // String value
 745  901              if (common.isString(retval)) {
 746      -                towrite = retval.replace(/^\./mg, '..');
 747      -                write('SUCCESS\n' + towrite + '\n.\n');
      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 +                }
 748  908                  return;
 749  909              } else if (!isNaN(retval)) {
 750      -                towrite = retval.toString().replace(/^\./mg, '..');
 751      -                write('SUCCESS\n' + towrite + '\n.\n');
      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 +                }
 752  916                  return;
 753  917              } else if (retval) {
 754  918                  // Non-string value
 755      -                write('FAILURE\n');
      919 +                if (req_is_v2)
      920 +                    write(format_v2_response('FAILURE'));
      921 +                else
      922 +                    write('FAILURE\n');
 756  923                  return;
 757  924              } else {
 758  925                  // Nothing to return
 759      -                write('NOTFOUND\n');
      926 +                if (req_is_v2)
      927 +                    write(format_v2_response('NOTFOUND'));
      928 +                else
      929 +                    write('NOTFOUND\n');
 760  930                  return;
 761  931              }
 762  932          }
 763  933      };
 764  934  };
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX