1 /* This file is part of Eclat.
2    Copyright (C) 2013-2018 Sergey Poznyakoff.
3 
4    Eclat is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    Eclat is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with Eclat.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include "eclat.h"
18 #include "mkinst-cl.h"
19 
20 /* format:
21 
22    dev-index:subnet:description:priv-ip:sgs:DOT:sip-count:sips
23       0        1         2         3     4   5      6      7
24 
25    where:
26 
27    dev-index   device index
28    subnet      subnet ID
29    sgs         a comma-separated list of security group IDs
30    DOT         delete the interface on termination: true or false
31    sip-count   number of secondary IP addresses to assign
32    sips        a comma-separated list of secondary IP addresses
33 
34    only dev-index and subnet are mandatory;
35    either sip-count or sips can be specified, but not both.
36 */
37 void
add_iface(struct ec2_request * q,int ifno,char * ifspec)38 add_iface(struct ec2_request *q, int ifno, char *ifspec)
39 {
40 	struct wordsplit ws;
41 	char *bufptr = NULL;
42 	size_t bufsize = 0;
43 	char *p;
44 	int i;
45 
46 	ws.ws_delim = ":";
47 	if (wordsplit(ifspec, &ws, WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM))
48 		die(EX_SOFTWARE,
49 		    "failed to split string %s: %s",
50 		    ifspec,
51 		    wordsplit_strerror(&ws));
52 
53 	switch (ws.ws_wordc) {
54 	default:
55 		die(EX_USAGE,
56 		    "bad number of parts in interface specification \"%s\": %d",
57 		    ifspec, ws.ws_wordc);
58 	case 8:
59 		for (i = 1, p = strtok(ws.ws_wordv[7], ","); p;
60 		     i++, p = strtok(NULL, ",")) {
61 			grecs_asprintf(&bufptr, &bufsize,
62 				       "NetworkInterface.%d."
63 				       "PrivateIpAddresses.%d.PrivateIpAddress",
64 				       ifno, i);
65 			eclat_request_add_param(q, bufptr, p);
66 		}
67 		/* fall through */
68 	case 7:
69 		if (ws.ws_wordv[6][0]) {
70 			grecs_asprintf(&bufptr, &bufsize,
71 				       "NetworkInterface.%d."
72 				       "SecondaryPrivateIpAddressCount",
73 				       ifno);
74 			eclat_request_add_param(q, bufptr, ws.ws_wordv[6]);
75 		}
76 	case 6:
77 		if (ws.ws_wordv[5][0]) {
78 			grecs_asprintf(&bufptr, &bufsize,
79 				       "NetworkInterface.%d."
80 				       "DeleteOnTermination",
81 				       ifno);
82 			eclat_request_add_param(q, bufptr, ws.ws_wordv[5]);
83 		}
84 	case 5:
85 		if (ws.ws_wordv[4])
86 			for (i = 1, p = strtok(ws.ws_wordv[4], ","); p;
87 			     i++, p = strtok(NULL, ",")) {
88 				grecs_asprintf(&bufptr, &bufsize,
89 					       "NetworkInterface.%d."
90 					       "SecurityGroupId.%d",
91 					       ifno, i);
92 				eclat_request_add_param(q, bufptr, p);
93 			}
94 	case 4:
95 		if (ws.ws_wordv[3]) {
96 			grecs_asprintf(&bufptr, &bufsize,
97 				       "NetworkInterface.%d.PrivateIpAddress",
98 				       ifno);
99 			eclat_request_add_param(q, bufptr, ws.ws_wordv[3]);
100 		}
101 	case 3:
102 		if (ws.ws_wordv[2]) {
103 			grecs_asprintf(&bufptr, &bufsize,
104 				       "NetworkInterface.%d.Description",
105 				       ifno);
106 			eclat_request_add_param(q, bufptr, ws.ws_wordv[2]);
107 		}
108 	case 2:
109 		if (!ws.ws_wordv[1][0])
110 			die(EX_USAGE,
111 			    "no subnet ID in interface specification \"%s\"",
112 			    ifspec);
113 	}
114 	grecs_asprintf(&bufptr, &bufsize, "NetworkInterface.%d.SubnetId",
115 		       ifno);
116 	eclat_request_add_param(q, bufptr,
117 			      ws.ws_wordv[1][0] ? ws.ws_wordv[1] : "0");
118 
119 	if (ws.ws_wordv[0][0]) {
120 		grecs_asprintf(&bufptr, &bufsize,
121 			       "NetworkInterface.%d.DeviceIndex",
122 			       ifno);
123 		eclat_request_add_param(q, bufptr, ws.ws_wordv[0]);
124 	}
125 
126 	wordsplit_free(&ws);
127 	free(bufptr);
128 }
129 
130 int
eclat_run_instances(eclat_command_env_t * env,int argc,char ** argv)131 eclat_run_instances(eclat_command_env_t *env, int argc, char **argv)
132 {
133 	int i;
134 	char *bufptr = NULL;
135 	size_t bufsize = 0;
136 	struct grecs_list_entry *ep;
137 	struct ec2_request *q = env->request;
138 	char *p;
139 	int iface_no = 1;
140 
141 	parse_options(env, argc, argv);
142 
143 	eclat_request_add_param(q, "ImageId", ami);
144 	p = strchr(instance_count, '-');
145 	eclat_request_add_param(q, "MinCount", instance_count);
146 	if (p) {
147 		*p++ = 0;
148 		eclat_request_add_param(q, "MaxCount", p);
149 	} else
150 		eclat_request_add_param(q, "MaxCount", instance_count);
151 
152 	if (keypair)
153 		eclat_request_add_param(q, "KeyName", keypair);
154 
155 	if (secgrp) {
156 		for (i = 1, ep = secgrp->head; ep; ep = ep->next, i++) {
157 			grecs_asprintf(&bufptr, &bufsize,
158 				       "SecurityGroup.%d", i);
159 			eclat_request_add_param(q, bufptr, ep->data);
160 		}
161 	}
162 
163 	if (type)
164 		eclat_request_add_param(q, "InstanceType", type);
165 
166 	if (zone)
167 		eclat_request_add_param(q, "Placement.AvailabilityZone", zone);
168 
169 	if (kernel)
170 		eclat_request_add_param(q, "KernelId", kernel);
171 
172 	if (ramdisk)
173 		eclat_request_add_param(q, "RamdiskId", ramdisk);
174 
175 	if (devmap)
176 		eclat_encode_devmap(q, devmap);
177 
178 	if (monitor)
179 		eclat_request_add_param(q, "Monitoring.Enabled", "true");
180 
181 	if (disable_term)
182 		eclat_request_add_param(q, "DisableApiTermination", "true");
183 
184 	if (shutdown_behavior)
185 		eclat_request_add_param(q, "InstanceInitiatedShutdownBehavior",
186 				      "true");
187 	if (placement_group)
188 		eclat_request_add_param(q, "Placement.GroupName",
189 				      placement_group);
190 
191 	if (tenancy)
192 		eclat_request_add_param(q, "Placement.Tenancy", tenancy);
193 
194 	if (subnet)
195 		eclat_request_add_param(q, "SubnetId", subnet);
196 
197 	if (private_ip)
198 		eclat_request_add_param(q, "PrivateIpAddress", private_ip);
199 
200 	/* FIXME: I'm not at all sure whether this is the right way of
201 	   doing it. */
202 	if (privip) {
203 		for (i = 1, ep = privip->head; ep; i++, ep = ep->next) {
204 			grecs_asprintf(&bufptr, &bufsize,
205 				       "NetworkInterface.1."
206 				       "PrivateIpAddresses.%d.PrivateIpAddress",
207 				       i);
208 			eclat_request_add_param(q, bufptr, ep->data);
209 		}
210 		iface_no++;
211 	} else if (secipcount) {
212 		eclat_request_add_param(q,
213 				      "NetworkInterface.1."
214 				      "SecondaryPrivateIpAddressCount",
215 				      secipcount);
216 		iface_no++;
217 	}
218 
219 	if (iface)
220 		for (ep = iface->head; ep; ep = ep->next)
221 			add_iface(q, iface_no++, ep->data);
222 
223 	if (profile_name)
224 		eclat_request_add_param(q,
225 				      strncmp(profile_name, "arn:", 4) == 0 ?
226 				      "IamInstanceProfile.Arn" :
227 				      "IamInstanceProfile.Name",
228 				      profile_name);
229 
230 	if (ebs_opt)
231 		eclat_request_add_param(q, "EbsOptimized", "true");
232 
233 	if (user_data) {
234 		size_t enclen;
235 
236 		eclat_base64_encode((unsigned char *)user_data,
237 				    strlen(user_data),
238 				    (unsigned char**) &p, &enclen);
239 
240 		eclat_request_add_param(q, "UserData", p);
241 		free(p);
242 	}
243 
244 	return 0;
245 }
246