/src/main/java/com/amazonaws/util/EC2MetadataUtils.java

https://github.com/DWB-eHealth/aws-sdk-java · Java · 510 lines · 275 code · 70 blank · 165 comment · 23 complexity · fd0d3063af10dd6099cd77ae6a8bc60e MD5 · raw file

  1. /*
  2. * Copyright 2013-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License").
  5. * You may not use this file except in compliance with the License.
  6. * A copy of the License is located at
  7. *
  8. * http://aws.amazon.com/apache2.0
  9. *
  10. * or in the "license" file accompanying this file. This file is distributed
  11. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. * express or implied. See the License for the specific language governing
  13. * permissions and limitations under the License.
  14. */
  15. package com.amazonaws.util;
  16. import java.util.Arrays;
  17. import java.util.Collections;
  18. import java.util.HashMap;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import java.util.Map;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import com.amazonaws.AmazonClientException;
  25. import com.amazonaws.internal.EC2MetadataClient;
  26. import com.fasterxml.jackson.databind.DeserializationFeature;
  27. import com.fasterxml.jackson.databind.ObjectMapper;
  28. import com.fasterxml.jackson.databind.PropertyNamingStrategy;
  29. /**
  30. * Utility class for retrieving Amazon EC2 instance metadata.<br>
  31. * You can use the data to build more generic AMIs that can be modified by
  32. * configuration files supplied at launch time. For example, if you run web
  33. * servers for various small businesses, they can all use the same AMI and
  34. * retrieve their content from the Amazon S3 bucket you specify at launch. To
  35. * add a new customer at any time, simply create a bucket for the customer, add
  36. * their content, and launch your AMI.<br>
  37. *
  38. * More information about Amazon EC2 Metadata
  39. * @see <a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html">Amazon EC2 User Guide >> Instance Metadata</a>
  40. */
  41. public class EC2MetadataUtils {
  42. private static final String EC2_METADATA_ROOT = "/latest/meta-data";
  43. private static final String EC2_USERDATA_ROOT = "/latest/user-data/";
  44. private static final int DEFAULT_QUERY_RETRIES = 3;
  45. private static final int MINIMUM_RETRY_WAIT_TIME_MILLISECONDS = 250;
  46. private static Map<String, String> cache = new HashMap<String, String>();
  47. private static EC2MetadataClient ec2MetadataClient = new EC2MetadataClient();
  48. private static final ObjectMapper mapper = new ObjectMapper();
  49. static {
  50. mapper.configure(
  51. DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  52. mapper.setPropertyNamingStrategy(
  53. PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE);
  54. }
  55. private static final Log log = LogFactory.getLog(EC2MetadataUtils.class);
  56. /**
  57. * Get the AMI ID used to launch the instance.
  58. */
  59. public static String getAmiId() {
  60. return fetchData(EC2_METADATA_ROOT + "/ami-id");
  61. }
  62. /**
  63. * Get the index of this instance in the reservation.
  64. */
  65. public static String getAmiLaunchIndex() {
  66. return fetchData(EC2_METADATA_ROOT + "/ami-launch-index");
  67. }
  68. /**
  69. * Get the manifest path of the AMI with which the instance was launched.
  70. */
  71. public static String getAmiManifestPath() {
  72. return fetchData(EC2_METADATA_ROOT + "/ami-manifest-path");
  73. }
  74. /**
  75. * Get the list of AMI IDs of any instances that were rebundled to created this AMI.
  76. * Will only exist if the AMI manifest file contained an ancestor-amis key.
  77. */
  78. public static List<String> getAncestorAmiIds() {
  79. return getItems(EC2_METADATA_ROOT + "/ancestor-ami-ids");
  80. }
  81. /**
  82. * Notifies the instance that it should reboot in preparation for bundling.
  83. * Valid values: none | shutdown | bundle-pending.
  84. */
  85. public static String getInstanceAction() {
  86. return fetchData(EC2_METADATA_ROOT + "/instance-action");
  87. }
  88. /**
  89. * Get the ID of this instance.
  90. */
  91. public static String getInstanceId() {
  92. return fetchData(EC2_METADATA_ROOT + "/instance-id");
  93. }
  94. /**
  95. * Get the type of the instance.
  96. */
  97. public static String getInstanceType() {
  98. return fetchData(EC2_METADATA_ROOT + "/instance-type");
  99. }
  100. /**
  101. * Get the local hostname of the instance. In cases where multiple network interfaces are present,
  102. * this refers to the eth0 device (the device for which device-number is 0).
  103. */
  104. public static String getLocalHostName() {
  105. return fetchData(EC2_METADATA_ROOT + "/local-hostname");
  106. }
  107. /**
  108. * Get the MAC address of the instance. In cases where multiple network interfaces are present,
  109. * this refers to the eth0 device (the device for which device-number is 0).
  110. */
  111. public static String getMacAddress() {
  112. return fetchData(EC2_METADATA_ROOT + "/mac");
  113. }
  114. /**
  115. * Get the private IP address of the instance. In cases where multiple network interfaces are present,
  116. * this refers to the eth0 device (the device for which device-number is 0).
  117. */
  118. public static String getPrivateIpAddress() {
  119. return fetchData(EC2_METADATA_ROOT + "/local-ipv4");
  120. }
  121. /**
  122. * Get the Availability Zone in which the instance launched.
  123. */
  124. public static String getAvailabilityZone() {
  125. return fetchData(EC2_METADATA_ROOT + "/placement/availability-zone");
  126. }
  127. /**
  128. * Get the list of product codes associated with the instance, if any.
  129. */
  130. public static List<String> getProductCodes() {
  131. return getItems(EC2_METADATA_ROOT + "/product-codes");
  132. }
  133. /**
  134. * Get the public key. Only available if supplied at instance launch time.
  135. */
  136. public static String getPublicKey() {
  137. return fetchData(EC2_METADATA_ROOT + "/public-keys/0/openssh-key");
  138. }
  139. /**
  140. * Get the ID of the RAM disk specified at launch time, if applicable.
  141. */
  142. public static String getRamdiskId() {
  143. return fetchData(EC2_METADATA_ROOT + "/ramdisk-id");
  144. }
  145. /**
  146. * Get the ID of the reservation.
  147. */
  148. public static String getReservationId() {
  149. return fetchData(EC2_METADATA_ROOT + "/reservation-id");
  150. }
  151. /**
  152. * Get the list of names of the security groups applied to the instance.
  153. */
  154. public static List<String> getSecurityGroups() {
  155. return getItems(EC2_METADATA_ROOT + "/security-groups");
  156. }
  157. /**
  158. * Get information about the last time the instance profile was updated,
  159. * including the instance's LastUpdated date, InstanceProfileArn, and
  160. * InstanceProfileId.
  161. */
  162. public static IAMInfo getIAMInstanceProfileInfo() {
  163. String json = getData(EC2_METADATA_ROOT + "/iam/info");
  164. if (null == json) {
  165. return null;
  166. }
  167. try {
  168. return mapper.readValue(json, IAMInfo.class);
  169. } catch (Exception e) {
  170. log.warn("Unable to parse IAM Instance profile info ("
  171. + json + "): " + e.getMessage(),
  172. e);
  173. return null;
  174. }
  175. }
  176. /**
  177. * Returns the temporary security credentials (AccessKeyId,
  178. * SecretAccessKey, SessionToken, and Expiration) associated with the IAM
  179. * roles on the instance.
  180. */
  181. public static Map<String, IAMSecurityCredential> getIAMSecurityCredentials() {
  182. Map<String, IAMSecurityCredential> credentialsInfoMap =
  183. new HashMap<String, IAMSecurityCredential>();
  184. List<String> credentials =
  185. getItems(EC2_METADATA_ROOT + "/iam/security-credentials");
  186. if (null != credentials) {
  187. for (String credential : credentials) {
  188. String json = getData(
  189. EC2_METADATA_ROOT
  190. + "/iam/security-credentials/"
  191. + credential);
  192. try {
  193. IAMSecurityCredential credentialInfo =
  194. mapper.readValue(json, IAMSecurityCredential.class);
  195. credentialsInfoMap.put(credential, credentialInfo);
  196. } catch (Exception e) {
  197. log.warn("Unable to process the credential ("
  198. + credential + "). " + e.getMessage(),
  199. e);
  200. }
  201. }
  202. }
  203. return credentialsInfoMap;
  204. }
  205. /**
  206. * Get the virtual devices associated with the ami, root, ebs, and swap.
  207. */
  208. public static Map<String, String> getBlockDeviceMapping() {
  209. Map<String, String> blockDeviceMapping = new HashMap<String, String>();
  210. List<String> devices = getItems(EC2_METADATA_ROOT + "/block-device-mapping");
  211. for (String device : devices) {
  212. blockDeviceMapping.put(device, getData(EC2_METADATA_ROOT + "/block-device-mapping/" + device));
  213. }
  214. return blockDeviceMapping;
  215. }
  216. /**
  217. * Get the list of network interfaces on the instance.
  218. */
  219. public static List<NetworkInterface> getNetworkInterfaces() {
  220. List<NetworkInterface> networkInterfaces = new LinkedList<NetworkInterface>();
  221. List<String> macs = getItems(EC2_METADATA_ROOT + "/network/interfaces/macs/");
  222. for (String mac : macs) {
  223. String key = mac.trim();
  224. if (key.endsWith("/"))
  225. key = key.substring(0, key.length() - 1);
  226. networkInterfaces.add(new NetworkInterface(key));
  227. }
  228. return networkInterfaces;
  229. }
  230. /**
  231. * Get the metadata sent to the instance
  232. */
  233. public static String getUserData() {
  234. return getData(EC2_USERDATA_ROOT);
  235. }
  236. public static String getData(String path) {
  237. return getData(path, DEFAULT_QUERY_RETRIES);
  238. }
  239. public static String getData(String path, int tries) {
  240. List<String> items = getItems(path, tries, true);
  241. if (null != items && items.size() > 0)
  242. return items.get(0);
  243. return null;
  244. }
  245. public static List<String> getItems(String path) {
  246. return getItems(path, DEFAULT_QUERY_RETRIES, false);
  247. }
  248. public static List<String> getItems(String path, int tries) {
  249. return getItems(path, tries, false);
  250. }
  251. private static List<String> getItems(String path, int tries, boolean slurp) {
  252. if (tries == 0)
  253. throw new AmazonClientException("Unable to contact EC2 metadata service.");
  254. List<String> items;
  255. try {
  256. String response = ec2MetadataClient.readResource(path);
  257. if (slurp)
  258. items = Collections.singletonList(response);
  259. else
  260. items = Arrays.asList(response.split("\n"));
  261. return items;
  262. } catch (AmazonClientException ace) {
  263. log.warn("Unable to retrieve the requested metadata.");
  264. return null;
  265. } catch (Exception e) {
  266. // Retry on any other exceptions
  267. int pause = (int) (Math.pow(2, DEFAULT_QUERY_RETRIES - tries) * MINIMUM_RETRY_WAIT_TIME_MILLISECONDS);
  268. try {
  269. Thread.sleep(pause < MINIMUM_RETRY_WAIT_TIME_MILLISECONDS ? MINIMUM_RETRY_WAIT_TIME_MILLISECONDS : pause);
  270. } catch (InterruptedException e1) {
  271. Thread.currentThread().interrupt();
  272. }
  273. return getItems(path, tries - 1, slurp);
  274. }
  275. }
  276. private static String fetchData(String path) {
  277. return fetchData(path, false);
  278. }
  279. private static String fetchData(String path, boolean force) {
  280. try {
  281. if (force || !cache.containsKey(path))
  282. cache.put(path, getData(path));
  283. return cache.get(path);
  284. } catch (Exception e) {
  285. return null;
  286. }
  287. }
  288. /**
  289. * Information about the last time the instance profile was updated,
  290. * including the instance's LastUpdated date, InstanceProfileArn, and
  291. * InstanceProfileId.
  292. */
  293. public static class IAMInfo {
  294. public String code;
  295. public String message;
  296. public String lastUpdated;
  297. public String instanceProfileArn;
  298. public String instanceProfileId;
  299. }
  300. /**
  301. * The temporary security credentials (AccessKeyId, SecretAccessKey,
  302. * SessionToken, and Expiration)
  303. * associated with the IAM role.
  304. */
  305. public static class IAMSecurityCredential {
  306. public String code;
  307. public String message;
  308. public String lastUpdated;
  309. public String type;
  310. public String accessKeyId;
  311. public String secretAccessKey;
  312. public String token;
  313. public String expiration;
  314. /**
  315. * @deprecated because it is spelled incorrectly
  316. * @see #accessKeyId
  317. */
  318. @Deprecated
  319. public String secretAcessKey;
  320. }
  321. /**
  322. * All of the metada associated with a network interface on the instance.
  323. */
  324. public static class NetworkInterface {
  325. private String path;
  326. private String mac;
  327. private List<String> availableKeys;
  328. private Map<String, String> data = new HashMap<String, String>();
  329. private NetworkInterface() {}
  330. public NetworkInterface(String macAddress) {
  331. mac = macAddress;
  332. path = "/network/interfaces/macs/" + mac + "/";
  333. }
  334. /**
  335. * The interface's Media Acess Control (mac) address
  336. */
  337. public String getMacAddress() {
  338. return mac;
  339. }
  340. /**
  341. * The ID of the owner of the network interface.<br>
  342. * In multiple-interface environments, an interface can be attached by a third party,
  343. * such as Elastic Load Balancing. Traffic on an interface is always billed to the interface owner.
  344. */
  345. public String getOwnerId() {
  346. return getData("owner-id");
  347. }
  348. /**
  349. * The interface's profile.
  350. */
  351. public String getProfile() {
  352. return getData("profile");
  353. }
  354. /**
  355. * The interface's local hostname.
  356. */
  357. public String getHostname() {
  358. return getData("local-hostname");
  359. }
  360. /**
  361. * The private IP addresses associated with the interface.
  362. */
  363. public List<String> getLocalIPv4s() {
  364. return getItems("local-ipv4s");
  365. }
  366. /**
  367. * The interface's public hostname.
  368. */
  369. public String getPublicHostname() {
  370. return getData("public-hostname");
  371. }
  372. /**
  373. * The elastic IP addresses associated with the interface.<br>
  374. * There may be multiple IP addresses on an instance.
  375. */
  376. public List<String> getPublicIPv4s() {
  377. return getItems("public-ipv4s");
  378. }
  379. /**
  380. * Security groups to which the network interface belongs.
  381. */
  382. public List<String> getSecurityGroups() {
  383. return getItems("security-groups");
  384. }
  385. /**
  386. * IDs of the security groups to which the network interface belongs. Returned only for
  387. * Amazon EC2 instances launched into a VPC.
  388. */
  389. public List<String> getSecurityGroupIds() {
  390. return getItems("security-group-ids");
  391. }
  392. /**
  393. * The CIDR block of the Amazon EC2-VPC subnet in which the interface resides.<br>
  394. * Returned only for Amazon EC2 instances launched into a VPC.
  395. */
  396. public String getSubnetIPv4CidrBlock() {
  397. return getData("subnet-ipv4-cidr-block");
  398. }
  399. /**
  400. * The CIDR block of the Amazon EC2-VPC subnet in which the interface resides.<br>
  401. * Returned only for Amazon EC2 instances launched into a VPC.
  402. */
  403. public String getVpcId() {
  404. return getData("vpc-id");
  405. }
  406. /**
  407. * Get the private IPv4 address(es) that are associated with the public-ip address
  408. * and assigned to that interface.
  409. * @param publicIp
  410. * The public IP address
  411. * @return
  412. * Private IPv4 address(es) associated with the public IP address.
  413. */
  414. public List<String> getIPv4Association(String publicIp) {
  415. return EC2MetadataUtils.getItems(EC2_METADATA_ROOT + path + "ipv4-associations/" + publicIp);
  416. }
  417. private String getData(String key) {
  418. if (data.containsKey(key))
  419. return data.get(key);
  420. // Since the keys are variable, cache a list of which ones are available
  421. // to prevent unnecessary trips to the service.
  422. if (null == availableKeys)
  423. availableKeys = EC2MetadataUtils.getItems(EC2_METADATA_ROOT + path);
  424. if (availableKeys.contains(key)) {
  425. data.put(key, EC2MetadataUtils.getData(EC2_METADATA_ROOT + path + key));
  426. return data.get(key);
  427. }
  428. else
  429. return null;
  430. }
  431. private List<String> getItems(String key) {
  432. if (null == availableKeys)
  433. availableKeys = EC2MetadataUtils.getItems(EC2_METADATA_ROOT + path);
  434. if (availableKeys.contains(key))
  435. return EC2MetadataUtils.getItems(EC2_METADATA_ROOT + path + key);
  436. else
  437. return new LinkedList<String>();
  438. }
  439. }
  440. }