1 package org.kuali.common.devops.logic;
2
3 import static com.google.common.base.Optional.absent;
4 import static com.google.common.base.Optional.fromNullable;
5 import static com.google.common.base.Preconditions.checkArgument;
6 import static com.google.common.collect.Lists.newArrayList;
7 import static com.google.common.collect.Sets.newTreeSet;
8 import static java.lang.String.format;
9 import static org.apache.commons.io.FileUtils.writeLines;
10 import static org.apache.commons.lang.StringUtils.trimToNull;
11 import static org.kuali.common.util.Encodings.UTF8;
12 import static org.kuali.common.util.base.Exceptions.illegalState;
13 import static org.kuali.common.util.base.Precondition.checkNotBlank;
14 import static org.kuali.common.util.base.Precondition.checkNotNull;
15 import static org.kuali.common.util.log.Loggers.newLogger;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.Properties;
22 import java.util.Set;
23 import java.util.SortedMap;
24 import java.util.SortedSet;
25
26 import org.apache.commons.io.FileUtils;
27 import org.kuali.common.aws.ec2.api.EC2Service;
28 import org.kuali.common.aws.ec2.impl.DefaultEC2Service;
29 import org.kuali.common.aws.ec2.model.EC2ServiceContext;
30 import org.kuali.common.devops.logic.function.ToCsvFunction;
31 import org.kuali.common.devops.logic.function.ToListFunction;
32 import org.kuali.common.devops.metadata.model.EC2Instance;
33 import org.kuali.common.devops.metadata.model.EC2Tag;
34 import org.kuali.common.devops.table.TableCellDescriptor;
35 import org.kuali.common.devops.table.Tables;
36 import org.kuali.common.util.Encodings;
37 import org.kuali.common.util.LocationUtils;
38 import org.kuali.common.util.PropertyUtils;
39 import org.kuali.common.util.Str;
40 import org.kuali.common.util.base.Exceptions;
41 import org.kuali.common.util.file.CanonicalFile;
42 import org.kuali.common.util.wait.DefaultWaitService;
43 import org.kuali.common.util.wait.WaitService;
44 import org.slf4j.Logger;
45
46 import com.amazonaws.auth.AWSCredentials;
47 import com.amazonaws.services.ec2.model.Instance;
48 import com.amazonaws.services.ec2.model.Tag;
49 import com.google.common.base.Optional;
50 import com.google.common.collect.ImmutableList;
51 import com.google.common.collect.Maps;
52 import com.google.common.collect.Table;
53
54 public class Instances {
55
56 private static final File CACHE_DIR = new CanonicalFile("./target/cache/servers/aws/ec2");
57 private static final Logger logger = newLogger();
58 private static final String EC2_NAME_TAG_KEY = "Name";
59
60
61
62
63 public static SortedMap<String, List<EC2Instance>> getInstances(boolean refresh) {
64 SortedMap<String, List<EC2Instance>> map = Maps.newTreeMap();
65 Set<String> accounts = Auth.getAwsAccountNames();
66 for (String account : accounts) {
67 map.put(account, getInstances(account, refresh));
68 }
69 return map;
70 }
71
72 public static void updateTags(String account) {
73 String location = "classpath:org/kuali/" + account + "/tags.properties";
74 checkArgument(LocationUtils.exists(location), "Location [%s] does not exist", location);
75 Properties props = PropertyUtils.load(location);
76 AWSCredentials creds = Auth.getAwsCredentials(account);
77 WaitService ws = new DefaultWaitService();
78 EC2ServiceContext context = EC2ServiceContext.create(creds);
79 EC2Service service = new DefaultEC2Service(context, ws);
80 SortedSet<String> keys = newTreeSet(props.stringPropertyNames());
81 for (String key : keys) {
82 updateTag(account, key, props.getProperty(key), service);
83 }
84 }
85
86 public static void updateTag(String account, String key, String value, EC2Service service) {
87 String location = "classpath:org/kuali/" + account + "/" + value;
88 checkArgument(LocationUtils.exists(location), "Location [%s] does not exist", location);
89 Properties props = PropertyUtils.load(location);
90 List<Instance> instances = service.getInstances();
91 for (Instance instance : instances) {
92 Optional<String> name = getTagValue(instance, EC2_NAME_TAG_KEY);
93 if (name.isPresent()) {
94 Optional<String> tagValue = fromNullable(props.getProperty(name.get()));
95 if (tagValue.isPresent()) {
96 logger.info(format("tagging -> %s [%s]", key, Str.flatten(tagValue.get())));
97 Tag tag = new Tag(key, tagValue.get());
98 service.tag(instance.getInstanceId(), ImmutableList.of(tag));
99 }
100 }
101 }
102 }
103
104 protected static List<EC2Instance> getInstances(String account, boolean refresh) {
105 File cache = getCacheFile(account);
106 if (refresh || !cache.exists()) {
107 AWSCredentials creds = Auth.getAwsCredentials(account);
108 List<EC2Instance> instances = queryAmazon(creds);
109 store(cache, instances);
110 return instances;
111 } else {
112 return load(cache);
113 }
114 }
115
116 protected static File getCacheFile(String account) {
117 return new CanonicalFile(CACHE_DIR, account + ".txt");
118 }
119
120 protected static List<EC2Instance> load(File file) {
121 try {
122 logger.info(format("loading -> [%s]", file));
123 List<String> lines = FileUtils.readLines(file, Encodings.UTF8);
124 Table<Integer, String, TableCellDescriptor<String>> table = Tables.getTableFromCSV(lines, EC2Instance.class);
125 ToListFunction<Integer, String, EC2Instance> function = new ToListFunction.Builder<Integer, String, EC2Instance>().targetType(EC2Instance.class).build();
126 return function.apply(table);
127 } catch (IOException e) {
128 throw Exceptions.illegalState(e, "unexpected io error -> [%s]", file);
129 }
130 }
131
132 public static List<EC2Instance> queryAmazon(AWSCredentials creds) {
133 WaitService ws = new DefaultWaitService();
134 EC2ServiceContext context = EC2ServiceContext.create(creds);
135 EC2Service service = new DefaultEC2Service(context, ws);
136 List<EC2Instance> instances = convert(service.getInstances());
137 Collections.sort(instances);
138 return instances;
139 }
140
141 protected static void store(File file, List<EC2Instance> instances) {
142 Table<Integer, String, TableCellDescriptor<Object>> table = Tables.getTable(instances, EC2Instance.class);
143 ToCsvFunction<Integer, String> function = new ToCsvFunction<Integer, String>();
144 List<String> lines = function.apply(table);
145 store(file, lines, UTF8);
146 }
147
148 protected static void store(File file, List<String> lines, String encoding) {
149 checkNotNull(file, "file");
150 checkNotNull(lines, "lines");
151 checkNotBlank(encoding, "encoding");
152 try {
153 logger.info(format("creating -> [%s]", file));
154 writeLines(file, encoding, lines);
155 } catch (IOException e) {
156 throw illegalState(e, "unexpected io error -> [%s]", file);
157 }
158 }
159
160 protected static List<EC2Instance> convert(List<Instance> instances) {
161 List<EC2Instance> list = newArrayList();
162 for (Instance instance : instances) {
163 list.add(convert(instance));
164 }
165 return list;
166 }
167
168 protected static EC2Instance convert(Instance instance) {
169 String id = instance.getInstanceId();
170 Optional<String> name = getTagValue(instance, EC2_NAME_TAG_KEY);
171 Optional<String> publicDnsName = fromNullable(trimToNull(instance.getPublicDnsName()));
172 String type = instance.getInstanceType();
173 long launchTime = instance.getLaunchTime().getTime();
174 String ami = instance.getImageId();
175 String state = instance.getState().getName();
176 List<EC2Tag> tags = getTags(instance);
177 return EC2Instance.builder().id(id).name(name).publicDnsName(publicDnsName).type(type).launchTime(launchTime).ami(ami).state(state).tags(tags).build();
178 }
179
180 protected static List<EC2Tag> getTags(Instance instance) {
181 if (instance.getTags() == null || instance.getTags().isEmpty()) {
182 return newArrayList();
183 }
184 List<EC2Tag> tags = newArrayList();
185 for (Tag tag : instance.getTags()) {
186 EC2Tag newTag = EC2Tag.create(tag.getKey(), tag.getValue());
187 tags.add(newTag);
188 }
189 return tags;
190 }
191
192 protected static Optional<String> getTagValue(Instance instance, String tagName) {
193 Optional<Tag> tag = getTag(instance, tagName);
194 if (tag.isPresent()) {
195 return Optional.of(tag.get().getValue());
196 } else {
197 return absent();
198 }
199 }
200
201 protected static Optional<Tag> getTag(Instance instance, String key) {
202 List<Tag> tags = instance.getTags();
203 for (Tag tag : tags) {
204 if (key.equals(tag.getKey())) {
205 return Optional.of(tag);
206 }
207 }
208 return absent();
209 }
210
211 }