Thomas Vachuska
Committed by Gerrit Code Review

Adding ability to balance load between different cell servers.

Adding ability to specify structure/size of the cell.

Change-Id: I5e87c99fe8812ba0a974d7815ab8ddc64193a608
...@@ -113,7 +113,10 @@ function cell { ...@@ -113,7 +113,10 @@ function cell {
113 case "$cell" in 113 case "$cell" in
114 "borrow") 114 "borrow")
115 aux="/tmp/cell-$$" 115 aux="/tmp/cell-$$"
116 - curl -sS -X POST "http://$CELL_WARDEN:4321/?duration=${2:-0}&user=${3:-$(id -un)}" \ 116 + spec=${3:-3+1}
117 + spec=${spec/+/%2B}
118 + user=${4:-$(id -un)}
119 + curl -sS -X POST "http://$CELL_WARDEN:4321/?duration=${2:-0}&spec=${spec}&user=${user}" \
117 -d "$(cat ~/.ssh/id_rsa.pub)" > $aux 120 -d "$(cat ~/.ssh/id_rsa.pub)" > $aux
118 . $aux 121 . $aux
119 rm -f $aux 122 rm -f $aux
......
...@@ -3,15 +3,23 @@ ...@@ -3,15 +3,23 @@
3 3
4 name="$1" 4 name="$1"
5 ipx="$2" 5 ipx="$2"
6 -shift 2 6 +spec="$3"
7 +shift 3
7 key="$@" 8 key="$@"
8 9
9 cd $(dirname $0) 10 cd $(dirname $0)
10 11
12 +nodes=${spec%+*}
13 +mininet=${spec#*+}
14 +
11 sudo lxc-attach -n bit-proxy -- bash -c "grep -qF \"$key\" /home/sdn/.ssh/authorized_keys || echo $key >> /home/sdn/.ssh/authorized_keys" 15 sudo lxc-attach -n bit-proxy -- bash -c "grep -qF \"$key\" /home/sdn/.ssh/authorized_keys || echo $key >> /home/sdn/.ssh/authorized_keys"
12 16
13 -./clone-node base-mininet ${ipx/x/0} $name-n "$key" 17 +if [ $mininet -ge 1 ]; then
18 + ./clone-node base-mininet ${ipx/x/0} $name-n "$key"
19 +fi
14 20
15 -for n in {1..3}; do 21 +let n=1
22 +while [ $n -le $nodes ]; do
16 ./clone-node base-onos ${ipx/x/$n} $name-$n "$key" 23 ./clone-node base-onos ${ipx/x/$n} $name-$n "$key"
24 + let n=n+1
17 done 25 done
......
...@@ -2,11 +2,19 @@ ...@@ -2,11 +2,19 @@
2 # Destroys an LXC cell with the specified name. 2 # Destroys an LXC cell with the specified name.
3 3
4 name=$1 4 name=$1
5 +spec=$2
6 +
7 +nodes=${spec%+*}
8 +mininet=${spec#*+}
5 9
6 cd $(dirname $0) 10 cd $(dirname $0)
7 11
8 -./destroy-node $name-n 12 +if [ $mininet -ge 1 ]; then
13 + ./destroy-node $name-n
14 +fi
9 15
10 -for n in {1..3}; do 16 +let n=1
17 +while [ $n -le $nodes ]; do
11 ./destroy-node $name-$n 18 ./destroy-node $name-$n
19 + let n=n+1
12 done 20 done
......
...@@ -42,6 +42,23 @@ ...@@ -42,6 +42,23 @@
42 <artifactId>jetty-servlet</artifactId> 42 <artifactId>jetty-servlet</artifactId>
43 <version>8.1.18.v20150929</version> 43 <version>8.1.18.v20150929</version>
44 </dependency> 44 </dependency>
45 + <dependency>
46 + <groupId>junit</groupId>
47 + <artifactId>junit</artifactId>
48 + <version>4.12</version>
49 + <scope>test</scope>
50 + </dependency>
51 + <dependency>
52 + <groupId>org.onosproject</groupId>
53 + <artifactId>onlab-misc</artifactId>
54 + <version>${project.version}</version>
55 + <scope>test</scope>
56 + </dependency>
57 + <dependency>
58 + <groupId>org.onosproject</groupId>
59 + <artifactId>onlab-junit</artifactId>
60 + <scope>test</scope>
61 + </dependency>
45 </dependencies> 62 </dependencies>
46 63
47 <build> 64 <build>
...@@ -77,6 +94,11 @@ ...@@ -77,6 +94,11 @@
77 </execution> 94 </execution>
78 </executions> 95 </executions>
79 </plugin> 96 </plugin>
97 +
98 + <plugin>
99 + <groupId>org.apache.maven.plugins</groupId>
100 + <artifactId>maven-surefire-plugin</artifactId>
101 + </plugin>
80 </plugins> 102 </plugins>
81 </build> 103 </build>
82 104
......
...@@ -27,13 +27,15 @@ final class Reservation { ...@@ -27,13 +27,15 @@ final class Reservation {
27 final String userName; 27 final String userName;
28 final long time; 28 final long time;
29 final int duration; 29 final int duration;
30 + final String cellSpec;
30 31
31 // Creates a new reservation record 32 // Creates a new reservation record
32 - Reservation(String cellName, String userName, long time, int duration) { 33 + Reservation(String cellName, String userName, long time, int duration, String cellSpec) {
33 this.cellName = cellName; 34 this.cellName = cellName;
34 this.userName = userName; 35 this.userName = userName;
35 this.time = time; 36 this.time = time;
36 this.duration = duration; 37 this.duration = duration;
38 + this.cellSpec = cellSpec;
37 } 39 }
38 40
39 /** 41 /**
...@@ -43,11 +45,12 @@ final class Reservation { ...@@ -43,11 +45,12 @@ final class Reservation {
43 */ 45 */
44 Reservation(String line) { 46 Reservation(String line) {
45 String[] fields = line.trim().split("\t"); 47 String[] fields = line.trim().split("\t");
46 - checkState(fields.length == 4, "Incorrect reservation encoding"); 48 + checkState(fields.length == 5, "Incorrect reservation encoding");
47 this.cellName = fields[0]; 49 this.cellName = fields[0];
48 this.userName = fields[1]; 50 this.userName = fields[1];
49 this.time = Long.parseLong(fields[2]); 51 this.time = Long.parseLong(fields[2]);
50 this.duration = Integer.parseInt(fields[3]); 52 this.duration = Integer.parseInt(fields[3]);
53 + this.cellSpec = fields[4];
51 } 54 }
52 55
53 /** 56 /**
...@@ -56,7 +59,7 @@ final class Reservation { ...@@ -56,7 +59,7 @@ final class Reservation {
56 * @return encoded string 59 * @return encoded string
57 */ 60 */
58 String encode() { 61 String encode() {
59 - return String.format("%s\t%s\t%s\t%s\n", cellName, userName, time, duration); 62 + return String.format("%s\t%s\t%s\t%s\t%s\n", cellName, userName, time, duration, cellSpec);
60 } 63 }
61 64
62 } 65 }
......
...@@ -16,8 +16,9 @@ ...@@ -16,8 +16,9 @@
16 16
17 package org.onlab.warden; 17 package org.onlab.warden;
18 18
19 -import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableSet; 19 import com.google.common.collect.ImmutableSet;
20 +import com.google.common.collect.Lists;
21 +import com.google.common.collect.Maps;
21 import com.google.common.io.ByteStreams; 22 import com.google.common.io.ByteStreams;
22 23
23 import java.io.File; 24 import java.io.File;
...@@ -27,8 +28,11 @@ import java.io.IOException; ...@@ -27,8 +28,11 @@ import java.io.IOException;
27 import java.io.InputStream; 28 import java.io.InputStream;
28 import java.io.PrintWriter; 29 import java.io.PrintWriter;
29 import java.text.SimpleDateFormat; 30 import java.text.SimpleDateFormat;
31 +import java.util.ArrayList;
30 import java.util.Date; 32 import java.util.Date;
31 import java.util.HashSet; 33 import java.util.HashSet;
34 +import java.util.List;
35 +import java.util.Map;
32 import java.util.Random; 36 import java.util.Random;
33 import java.util.Set; 37 import java.util.Set;
34 import java.util.Timer; 38 import java.util.Timer;
...@@ -52,9 +56,15 @@ class Warden { ...@@ -52,9 +56,15 @@ class Warden {
52 private static final int MINUTE = 60_000; // 1 minute 56 private static final int MINUTE = 60_000; // 1 minute
53 private static final int DEFAULT_MINUTES = 60; 57 private static final int DEFAULT_MINUTES = 60;
54 58
59 + private static final String DEFAULT_SPEC = "3+1";
60 +
55 private final File log = new File("warden.log"); 61 private final File log = new File("warden.log");
56 62
57 - private final File cells = new File("cells"); 63 + // Allow overriding these for unit tests.
64 + static String cmdPrefix = "";
65 + static File root = new File(".");
66 +
67 + private final File cells = new File(root, "cells");
58 private final File supported = new File(cells, "supported"); 68 private final File supported = new File(cells, "supported");
59 private final File reserved = new File(cells, "reserved"); 69 private final File reserved = new File(cells, "reserved");
60 70
...@@ -64,6 +74,7 @@ class Warden { ...@@ -64,6 +74,7 @@ class Warden {
64 * Creates a new cell warden. 74 * Creates a new cell warden.
65 */ 75 */
66 Warden() { 76 Warden() {
77 + reserved.mkdirs();
67 random.setSeed(System.currentTimeMillis()); 78 random.setSeed(System.currentTimeMillis());
68 Timer timer = new Timer("cell-pruner", true); 79 Timer timer = new Timer("cell-pruner", true);
69 timer.schedule(new Reposessor(), MINUTE / 4, MINUTE / 2); 80 timer.schedule(new Reposessor(), MINUTE / 4, MINUTE / 2);
...@@ -84,7 +95,7 @@ class Warden { ...@@ -84,7 +95,7 @@ class Warden {
84 * 95 *
85 * @return list of cell names 96 * @return list of cell names
86 */ 97 */
87 - private Set<String> getAvailableCells() { 98 + Set<String> getAvailableCells() {
88 Set<String> available = new HashSet<>(getCells()); 99 Set<String> available = new HashSet<>(getCells());
89 available.removeAll(getReservedCells()); 100 available.removeAll(getReservedCells());
90 return ImmutableSet.copyOf(available); 101 return ImmutableSet.copyOf(available);
...@@ -95,18 +106,28 @@ class Warden { ...@@ -95,18 +106,28 @@ class Warden {
95 * 106 *
96 * @return list of cell names 107 * @return list of cell names
97 */ 108 */
98 - private Set<String> getReservedCells() { 109 + Set<String> getReservedCells() {
99 String[] list = reserved.list(); 110 String[] list = reserved.list();
100 return list != null ? ImmutableSet.copyOf(list) : ImmutableSet.of(); 111 return list != null ? ImmutableSet.copyOf(list) : ImmutableSet.of();
101 } 112 }
102 113
103 /** 114 /**
115 + * Returns the host name on which the specified cell is hosted.
116 + *
117 + * @param cellName cell name
118 + * @return host name where the cell runs
119 + */
120 + String getCellHost(String cellName) {
121 + return getCellInfo(cellName).hostName;
122 + }
123 +
124 + /**
104 * Returns reservation for the specified user. 125 * Returns reservation for the specified user.
105 * 126 *
106 * @param userName user name 127 * @param userName user name
107 * @return cell reservation record or null if user does not have one 128 * @return cell reservation record or null if user does not have one
108 */ 129 */
109 - private Reservation currentUserReservation(String userName) { 130 + Reservation currentUserReservation(String userName) {
110 checkNotNull(userName, USER_NOT_NULL); 131 checkNotNull(userName, USER_NOT_NULL);
111 for (String cellName : getReservedCells()) { 132 for (String cellName : getReservedCells()) {
112 Reservation reservation = currentCellReservation(cellName); 133 Reservation reservation = currentCellReservation(cellName);
...@@ -141,35 +162,66 @@ class Warden { ...@@ -141,35 +162,66 @@ class Warden {
141 * 162 *
142 * @param userName user name 163 * @param userName user name
143 * @param sshKey user ssh public key 164 * @param sshKey user ssh public key
144 - * @param minutes number of minutes for reservation 165 + * @param minutes optional number of minutes for reservation
166 + * @param cellSpec optional cell specification string
145 * @return reserved cell definition 167 * @return reserved cell definition
146 */ 168 */
147 - synchronized String borrowCell(String userName, String sshKey, int minutes) { 169 + synchronized String borrowCell(String userName, String sshKey, int minutes,
170 + String cellSpec) {
148 checkNotNull(userName, USER_NOT_NULL); 171 checkNotNull(userName, USER_NOT_NULL);
172 + checkArgument(userName.matches("[\\w]+"), "Invalid user name %s", userName);
149 checkNotNull(sshKey, KEY_NOT_NULL); 173 checkNotNull(sshKey, KEY_NOT_NULL);
150 checkArgument(minutes < MAX_MINUTES, "Number of minutes must be less than %d", MAX_MINUTES); 174 checkArgument(minutes < MAX_MINUTES, "Number of minutes must be less than %d", MAX_MINUTES);
151 - long now = System.currentTimeMillis(); 175 + checkArgument(minutes >= 0, "Number of minutes must be non-negative");
176 + checkArgument(cellSpec == null || cellSpec.matches("[\\d]+\\+[0-1]"),
177 + "Invalid cell spec string %s", cellSpec);
152 Reservation reservation = currentUserReservation(userName); 178 Reservation reservation = currentUserReservation(userName);
153 if (reservation == null) { 179 if (reservation == null) {
154 - checkArgument(minutes >= 0, "Number of minutes must be non-negative"); 180 + // If there is no reservation for the user, create one
155 - Set<String> cells = getAvailableCells(); 181 + String cellName = findAvailableCell();
156 - checkState(!cells.isEmpty(), "No cells are presently available"); 182 + reservation = new Reservation(cellName, userName, System.currentTimeMillis(),
157 - String cellName = ImmutableList.copyOf(cells).get(random.nextInt(cells.size())); 183 + minutes == 0 ? DEFAULT_MINUTES : minutes,
158 - reservation = new Reservation(cellName, userName, now, minutes == 0 ? DEFAULT_MINUTES : minutes); 184 + cellSpec == null ? DEFAULT_SPEC : cellSpec);
159 } else if (minutes == 0) { 185 } else if (minutes == 0) {
160 // If minutes are 0, simply return the cell definition 186 // If minutes are 0, simply return the cell definition
161 return getCellDefinition(reservation.cellName); 187 return getCellDefinition(reservation.cellName);
162 } else { 188 } else {
163 - reservation = new Reservation(reservation.cellName, userName, now, minutes); 189 + // If minutes are > 0, update the existing cell reservation
190 + reservation = new Reservation(reservation.cellName, userName,
191 + System.currentTimeMillis(), minutes,
192 + reservation.cellSpec);
164 } 193 }
165 194
166 reserveCell(reservation); 195 reserveCell(reservation);
167 createCell(reservation, sshKey); 196 createCell(reservation, sshKey);
168 - log(userName, reservation.cellName, "borrowed for " + reservation.duration + " minutes"); 197 + log(userName, reservation.cellName, reservation.cellSpec,
198 + "borrowed for " + reservation.duration + " minutes");
169 return getCellDefinition(reservation.cellName); 199 return getCellDefinition(reservation.cellName);
170 } 200 }
171 201
172 /** 202 /**
203 + * Returns name of an available cell. Cell is chosen based on the load
204 + * of its hosting server; a random one will be chosen from the set of
205 + * cells hosted by the least loaded server.
206 + *
207 + * @return name of an available cell
208 + */
209 + private String findAvailableCell() {
210 + Set<String> cells = getAvailableCells();
211 + checkState(!cells.isEmpty(), "No cells are presently available");
212 + Map<String, ServerInfo> load = Maps.newHashMap();
213 +
214 + cells.stream().map(this::getCellInfo)
215 + .forEach(info -> load.compute(info.hostName, (k, v) -> v == null ?
216 + new ServerInfo(info.hostName) : v.bumpLoad(info)));
217 +
218 + List<ServerInfo> servers = new ArrayList<>(load.values());
219 + servers.sort((a, b) -> a.load - b.load);
220 + ServerInfo server = servers.get(0);
221 + return server.cells.get(random.nextInt(server.cells.size())).cellName;
222 + }
223 +
224 + /**
173 * Returns the specified cell for the specified user and their public access key. 225 * Returns the specified cell for the specified user and their public access key.
174 * 226 *
175 * @param userName user name 227 * @param userName user name
...@@ -181,7 +233,7 @@ class Warden { ...@@ -181,7 +233,7 @@ class Warden {
181 233
182 unreserveCell(reservation); 234 unreserveCell(reservation);
183 destroyCell(reservation); 235 destroyCell(reservation);
184 - log(userName, reservation.cellName, "returned"); 236 + log(userName, reservation.cellName, reservation.cellSpec, "returned");
185 } 237 }
186 238
187 /** 239 /**
...@@ -229,9 +281,9 @@ class Warden { ...@@ -229,9 +281,9 @@ class Warden {
229 */ 281 */
230 private void createCell(Reservation reservation, String sshKey) { 282 private void createCell(Reservation reservation, String sshKey) {
231 CellInfo cellInfo = getCellInfo(reservation.cellName); 283 CellInfo cellInfo = getCellInfo(reservation.cellName);
232 - String cmd = String.format("ssh %s warden/bin/create-cell %s %s %s", 284 + String cmd = String.format("ssh %s warden/bin/create-cell %s %s %s %s",
233 cellInfo.hostName, cellInfo.cellName, 285 cellInfo.hostName, cellInfo.cellName,
234 - cellInfo.ipPrefix, sshKey); 286 + cellInfo.ipPrefix, reservation.cellSpec, sshKey);
235 exec(cmd); 287 exec(cmd);
236 } 288 }
237 289
...@@ -242,8 +294,8 @@ class Warden { ...@@ -242,8 +294,8 @@ class Warden {
242 */ 294 */
243 private void destroyCell(Reservation reservation) { 295 private void destroyCell(Reservation reservation) {
244 CellInfo cellInfo = getCellInfo(reservation.cellName); 296 CellInfo cellInfo = getCellInfo(reservation.cellName);
245 - exec(String.format("ssh %s warden/bin/destroy-cell %s", 297 + exec(String.format("ssh %s warden/bin/destroy-cell %s %s",
246 - cellInfo.hostName, cellInfo.cellName)); 298 + cellInfo.hostName, cellInfo.cellName, reservation.cellSpec));
247 } 299 }
248 300
249 /** 301 /**
...@@ -265,7 +317,7 @@ class Warden { ...@@ -265,7 +317,7 @@ class Warden {
265 // Executes the specified command. 317 // Executes the specified command.
266 private String exec(String command) { 318 private String exec(String command) {
267 try { 319 try {
268 - Process process = Runtime.getRuntime().exec(command); 320 + Process process = Runtime.getRuntime().exec(cmdPrefix + command);
269 String output = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); 321 String output = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8);
270 process.waitFor(TIMEOUT, TimeUnit.SECONDS); 322 process.waitFor(TIMEOUT, TimeUnit.SECONDS);
271 return process.exitValue() == 0 ? output : null; 323 return process.exitValue() == 0 ? output : null;
...@@ -275,12 +327,12 @@ class Warden { ...@@ -275,12 +327,12 @@ class Warden {
275 } 327 }
276 328
277 // Creates an audit log entry. 329 // Creates an audit log entry.
278 - private void log(String userName, String cellName, String action) { 330 + private void log(String userName, String cellName, String cellSpec, String action) {
279 try (FileOutputStream fos = new FileOutputStream(log, true); 331 try (FileOutputStream fos = new FileOutputStream(log, true);
280 PrintWriter pw = new PrintWriter(fos)) { 332 PrintWriter pw = new PrintWriter(fos)) {
281 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 333 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
282 - pw.println(String.format("%s\t%s\t%s\t%s", format.format(new Date()), 334 + pw.println(String.format("%s\t%s\t%s-%s\t%s", format.format(new Date()),
283 - userName, cellName, action)); 335 + userName, cellName, cellSpec, action));
284 pw.flush(); 336 pw.flush();
285 } catch (IOException e) { 337 } catch (IOException e) {
286 throw new IllegalStateException("Unable to log reservation action", e); 338 throw new IllegalStateException("Unable to log reservation action", e);
...@@ -300,6 +352,23 @@ class Warden { ...@@ -300,6 +352,23 @@ class Warden {
300 } 352 }
301 } 353 }
302 354
355 + // Carrier of cell server information
356 + private final class ServerInfo {
357 + final String hostName;
358 + int load = 0;
359 + List<CellInfo> cells = Lists.newArrayList();
360 +
361 + private ServerInfo(String hostName) {
362 + this.hostName = hostName;
363 + }
364 +
365 + private ServerInfo bumpLoad(CellInfo info) {
366 + cells.add(info);
367 + load++; // TODO: bump by cell size later
368 + return this;
369 + }
370 + }
371 +
303 // Task for re-possessing overdue cells 372 // Task for re-possessing overdue cells
304 private final class Reposessor extends TimerTask { 373 private final class Reposessor extends TimerTask {
305 @Override 374 @Override
......
...@@ -49,8 +49,8 @@ public class WardenServlet extends HttpServlet { ...@@ -49,8 +49,8 @@ public class WardenServlet extends HttpServlet {
49 if (reservation != null) { 49 if (reservation != null) {
50 long expiration = reservation.time + reservation.duration * 60_000; 50 long expiration = reservation.time + reservation.duration * 60_000;
51 long remaining = (expiration - System.currentTimeMillis()) / 60_000; 51 long remaining = (expiration - System.currentTimeMillis()) / 60_000;
52 - out.println(String.format("%-10s\t%-10s\t%s\t%s\t%s mins (%s remaining)", 52 + out.println(String.format("%-14s\t%-10s\t%s\t%s\t%s mins (%s remaining)",
53 - cellName, 53 + cellName + "-" + reservation.cellSpec,
54 reservation.userName, 54 reservation.userName,
55 fmt.format(new Date(reservation.time)), 55 fmt.format(new Date(reservation.time)),
56 fmt.format(new Date(expiration)), 56 fmt.format(new Date(expiration)),
...@@ -72,8 +72,9 @@ public class WardenServlet extends HttpServlet { ...@@ -72,8 +72,9 @@ public class WardenServlet extends HttpServlet {
72 String sshKey = new String(ByteStreams.toByteArray(req.getInputStream()), "UTF-8"); 72 String sshKey = new String(ByteStreams.toByteArray(req.getInputStream()), "UTF-8");
73 String userName = req.getParameter("user"); 73 String userName = req.getParameter("user");
74 String sd = req.getParameter("duration"); 74 String sd = req.getParameter("duration");
75 + String spec = req.getParameter("spec");
75 int duration = isNullOrEmpty(sd) ? 0 : Integer.parseInt(sd); 76 int duration = isNullOrEmpty(sd) ? 0 : Integer.parseInt(sd);
76 - String cellDefinition = warden.borrowCell(userName, sshKey, duration); 77 + String cellDefinition = warden.borrowCell(userName, sshKey, duration, spec);
77 out.println(cellDefinition); 78 out.println(cellDefinition);
78 } catch (Exception e) { 79 } catch (Exception e) {
79 resp.setStatus(Response.SC_INTERNAL_SERVER_ERROR); 80 resp.setStatus(Response.SC_INTERNAL_SERVER_ERROR);
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
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 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onlab.warden;
18 +
19 +import com.google.common.io.Files;
20 +import org.junit.After;
21 +import org.junit.Before;
22 +import org.junit.Test;
23 +import org.onlab.util.Tools;
24 +
25 +import java.io.File;
26 +import java.io.IOException;
27 +import java.util.Objects;
28 +
29 +import static org.junit.Assert.*;
30 +
31 +/**
32 + * Suite of tests for the cell warden.
33 + */
34 +public class WardenTest {
35 +
36 + private Warden warden;
37 + private File cells;
38 + private File supportedCells;
39 +
40 + @Before
41 + public void setUp() throws IOException {
42 + // Setup warden to be tested
43 + Warden.root = Files.createTempDir();
44 + Warden.cmdPrefix = "echo ";
45 + cells = new File(Warden.root, "cells");
46 + supportedCells = new File(cells, "supported");
47 + warden = new Warden();
48 +
49 + // Setup test cell information
50 + createCell("alpha", "foo");
51 + createCell("bravo", "foo");
52 + createCell("charlie", "foo");
53 + createCell("delta", "bar");
54 + createCell("echo", "bar");
55 + createCell("foxtrot", "bar");
56 +
57 + new File("warden.log").deleteOnExit();
58 + }
59 +
60 + private void createCell(String cellName, String hostName) throws IOException {
61 + File cellFile = new File(supportedCells, cellName);
62 + Files.createParentDirs(cellFile);
63 + Files.write((hostName + " " + cellName).getBytes(), cellFile);
64 + }
65 +
66 + @After
67 + public void tearDown() throws IOException {
68 + Tools.removeDirectory(Warden.root);
69 + }
70 +
71 + @Test
72 + public void basics() {
73 + assertEquals("incorrect number of cells", 6, warden.getCells().size());
74 + validateSizes(6, 0);
75 +
76 + String cellDefinition = warden.borrowCell("dude", "the-key", 0, null);
77 + assertTrue("incorrect definition", cellDefinition.contains("cell-def"));
78 + validateSizes(5, 1);
79 +
80 + Reservation dudeCell = warden.currentUserReservation("dude");
81 + validateCellState(dudeCell);
82 +
83 + warden.borrowCell("dolt", "a-key", 0, "4+1");
84 + Reservation doltCell = warden.currentUserReservation("dolt");
85 + validateCellState(doltCell);
86 + validateSizes(4, 2);
87 +
88 + assertTrue("cells should not be on the same host",
89 + Objects.equals(warden.getCellHost(dudeCell.cellName),
90 + warden.getCellHost(doltCell.cellName)));
91 +
92 + warden.returnCell("dude");
93 + validateSizes(5, 1);
94 +
95 + warden.borrowCell("dolt", "a-key", 30, null);
96 + validateSizes(5, 1);
97 +
98 + warden.returnCell("dolt");
99 + validateSizes(6, 0);
100 + }
101 +
102 + private void validateSizes(int available, int reserved) {
103 + assertEquals("incorrect number of available cells", available,
104 + warden.getAvailableCells().size());
105 + assertEquals("incorrect number of reserved cells", reserved,
106 + warden.getReservedCells().size());
107 + }
108 +
109 + private void validateCellState(Reservation reservation) {
110 + assertFalse("cell should not be available",
111 + warden.getAvailableCells().contains(reservation.cellName));
112 + assertTrue("cell should be reserved",
113 + warden.getReservedCells().contains(reservation.cellName));
114 + }
115 +
116 +}
...\ No newline at end of file ...\ No newline at end of file