%PDF- %PDF-
Direktori : /bin/ |
Current File : //bin/ec2metadata |
#!/usr/bin/python3 # # Query and display EC2 metadata related to the AMI instance # Copyright (c) 2009 Canonical Ltd. (Canonical Contributor Agreement 2.5) # # Author: Alon Swartz <alon@turnkeylinux.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys import time import getopt import socket import os try: from urllib import request as urllib_request from urllib import error as urllib_error from urllib import parse as urllib_parse except ImportError: # python2 import urllib2 as urllib_request import urllib2 as urllib_error import urlparse as urllib_parse instdata_host = "169.254.169.254" instdata_ver = "2009-04-04" instdata_url = "http://%s/%s" % (instdata_host, instdata_ver) TOKEN_TTL_SECONDS = 21600 TOKEN_HEADER = "X-aws-ec2-metadata-token" TOKEN_HEADER_TTL = "X-aws-ec2-metadata-token-ttl-seconds" session_token_url = "http://%s/%s/%s" % (instdata_host, 'latest', 'api/token') __doc__ = """ Query and display EC2 metadata. If no options are provided, all options will be displayed Options: -h --help show this help --kernel-id display the kernel id --ramdisk-id display the ramdisk id --reservation-id display the reservation id --ami-id display the ami id --ami-launch-index display the ami launch index --ami-manifest-path display the ami manifest path --ancestor-ami-ids display the ami ancestor id --product-codes display the ami associated product codes --availability-zone display the ami placement zone name --availability-zone-id display the ami placement zone id --region display the ami placement region name --group-name display the ami placement group name --host-id display the dedicated host id --partition-number display the partition instance was launched from --instance-id display the instance id --instance-type display the instance type --local-hostname display the local hostname --public-hostname display the public hostname --local-ipv4 display the local ipv4 ip address --public-ipv4 display the public ipv4 ip address --block-device-mapping display the block device id --security-groups display the security groups --mac display the instance mac address --profile display the instance profile --instance-action display the instance-action --public-keys display the openssh public keys --user-data display the user data (not actually metadata) -u | --url URL use URL (default: %s) """ % instdata_url METAOPTS = ['ami-id', 'ami-launch-index', 'ami-manifest-path', 'ancestor-ami-ids', 'availability-zone', 'block-device-mapping', 'instance-action', 'instance-id', 'instance-type', 'local-hostname', 'local-ipv4', 'kernel-id', 'mac', 'profile', 'product-codes', 'public-hostname', 'public-ipv4', 'public-keys', 'ramdisk-id', 'reservation-id', 'security-groups', 'user-data', 'availability-zone-id', 'region', 'host-id', 'group-name', 'partition-number'] binstdout = os.fdopen(sys.stdout.fileno(), 'wb') def print_binary(data): if not isinstance(data, bytes): data = data.encode() binstdout.write(data) binstdout.flush() class Error(Exception): pass class EC2Metadata: # pylint: disable=R0903 """Class for querying metadata from EC2""" def __init__(self, burl=instdata_url): self.burl = burl s = urllib_parse.urlsplit(burl) addr = s.netloc.split(":")[0] port = s.port if s.port is None: port = 80 if not self._test_connectivity(addr, port): raise Error("could not establish connection to: %s:%s" % (addr, port)) self._imdsv2_ensure_token() @staticmethod def _test_connectivity(addr, port): for _ in range(6): s = socket.socket() try: s.connect((addr, port)) s.close() return True except socket.error: time.sleep(1) return False def _imdsv2_ensure_token(self): # Get IMDSv2 session token request = urllib_request.Request( session_token_url, method='PUT', headers={TOKEN_HEADER_TTL: TOKEN_TTL_SECONDS}) resp = urllib_request.urlopen(request) self.session_token = resp.read() def _get(self, uri, decode=True): url = "%s/%s" % (self.burl, uri) try: resp = urllib_request.urlopen( urllib_request.Request( url, headers={TOKEN_HEADER: self.session_token})) value = resp.read() if decode: value = value.decode() except urllib_error.HTTPError as e: if e.code == 404: return None # Eucalyptus may raise a 500 (Internal Server Error) if e.code == 500: return None raise return value def get(self, metaopt): """return value of metaopt""" if metaopt not in METAOPTS: raise Error('unknown metaopt', metaopt, METAOPTS) if metaopt in [ 'availability-zone', 'availability-zone-id', 'region', 'host-id', 'group-name', 'partition-number', ]: return self._get('meta-data/placement/' + metaopt) if metaopt == 'public-keys': data = self._get('meta-data/public-keys') if data is None: return None keyids = [line.split('=')[0] for line in data.splitlines()] public_keys = [] for keyid in keyids: uri = 'meta-data/public-keys/%d/openssh-key' % int(keyid) public_keys.append(self._get(uri).rstrip()) return public_keys if metaopt == 'user-data': return self._get('user-data', decode=False) return self._get('meta-data/' + metaopt) def get(metaopt): """primitive: return value of metaopt""" m = EC2Metadata() return m.get(metaopt) def display(metaopts, burl, prefix=False): """primitive: display metaopts (list) values with optional prefix""" m = EC2Metadata(burl) for metaopt in metaopts: value = m.get(metaopt) if not value: value = "unavailable" if prefix: print("%s: %s" % (metaopt, value)) elif metaopt == "user-data": # We want to avoid binary blob corruption while printing as string print_binary(value) else: print(value) def usage(s=None): """display usage and exit""" msg = "" if s: msg = "Error: %s\n" % s msg += "Syntax: %s [options]\n" % sys.argv[0] msg += __doc__ sys.stderr.write(msg + "\n") sys.exit(1) def main(): """handle cli options""" try: getopt_metaopts = METAOPTS[:] getopt_metaopts.append('help') getopt_metaopts.append('url=') opts, _ = getopt.gnu_getopt(sys.argv[1:], "hu:", getopt_metaopts) except getopt.GetoptError as e: usage(e) burl = instdata_url metaopts = [] prefix = False for opt, val in opts: if opt in ('-h', '--help'): usage() if opt in ('-u', '--url'): burl = val continue metaopts.append(opt.replace('--', '')) if len(metaopts) == 0: prefix = True metaopts = METAOPTS display(metaopts, burl, prefix) if __name__ == "__main__": main() # vi: ts=4 expandtab