Committed by
Ray Milkey
ONOS-785 Adding distributed store for apps & app admin CLIs
Change-Id: Ia7639f3258fca2a18ba513f0c95de0ab8ea7ceee
Showing
16 changed files
with
784 additions
and
32 deletions
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.cli.app; | ||
17 | + | ||
18 | +import org.apache.karaf.shell.commands.Argument; | ||
19 | +import org.apache.karaf.shell.commands.Command; | ||
20 | +import org.onosproject.app.ApplicationAdminService; | ||
21 | +import org.onosproject.cli.AbstractShellCommand; | ||
22 | +import org.onosproject.core.ApplicationId; | ||
23 | + | ||
24 | +/** | ||
25 | + * Activates an installed application. | ||
26 | + */ | ||
27 | +@Command(scope = "onos", name = "app-activate", | ||
28 | + description = "Activates an installed application") | ||
29 | +public class ApplicationActivateCommand extends AbstractShellCommand { | ||
30 | + | ||
31 | + @Argument(index = 0, name = "name", description = "Application name", | ||
32 | + required = true, multiValued = false) | ||
33 | + String name = null; | ||
34 | + | ||
35 | + @Override | ||
36 | + protected void execute() { | ||
37 | + ApplicationAdminService service = get(ApplicationAdminService.class); | ||
38 | + ApplicationId appId = service.getId(name); | ||
39 | + if (appId != null) { | ||
40 | + service.activate(appId); | ||
41 | + } else { | ||
42 | + print("No such application: %s", name); | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.cli.app; | ||
17 | + | ||
18 | +import org.apache.karaf.shell.commands.Argument; | ||
19 | +import org.apache.karaf.shell.commands.Command; | ||
20 | +import org.onosproject.app.ApplicationAdminService; | ||
21 | +import org.onosproject.cli.AbstractShellCommand; | ||
22 | +import org.onosproject.core.ApplicationId; | ||
23 | + | ||
24 | +/** | ||
25 | + * Deactivates an installed application. | ||
26 | + */ | ||
27 | +@Command(scope = "onos", name = "app-deactivate", | ||
28 | + description = "Deactivates an installed application") | ||
29 | +public class ApplicationDeactivateCommand extends AbstractShellCommand { | ||
30 | + | ||
31 | + @Argument(index = 0, name = "name", description = "Application name", | ||
32 | + required = true, multiValued = false) | ||
33 | + String name = null; | ||
34 | + | ||
35 | + @Override | ||
36 | + protected void execute() { | ||
37 | + ApplicationAdminService service = get(ApplicationAdminService.class); | ||
38 | + ApplicationId appId = service.getId(name); | ||
39 | + if (appId != null) { | ||
40 | + service.deactivate(appId); | ||
41 | + } else { | ||
42 | + print("No such application: %s", name); | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | +} |
... | @@ -13,12 +13,14 @@ | ... | @@ -13,12 +13,14 @@ |
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.onosproject.cli; | 16 | +package org.onosproject.cli.app; |
17 | 17 | ||
18 | import com.fasterxml.jackson.databind.JsonNode; | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | import com.fasterxml.jackson.databind.ObjectMapper; | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
20 | import com.fasterxml.jackson.databind.node.ArrayNode; | 20 | import com.fasterxml.jackson.databind.node.ArrayNode; |
21 | import org.apache.karaf.shell.commands.Command; | 21 | import org.apache.karaf.shell.commands.Command; |
22 | +import org.onosproject.cli.AbstractShellCommand; | ||
23 | +import org.onosproject.cli.Comparators; | ||
22 | import org.onosproject.core.ApplicationId; | 24 | import org.onosproject.core.ApplicationId; |
23 | import org.onosproject.core.CoreService; | 25 | import org.onosproject.core.CoreService; |
24 | 26 | ||
... | @@ -30,7 +32,7 @@ import static com.google.common.collect.Lists.newArrayList; | ... | @@ -30,7 +32,7 @@ import static com.google.common.collect.Lists.newArrayList; |
30 | /** | 32 | /** |
31 | * Lists application ID information. | 33 | * Lists application ID information. |
32 | */ | 34 | */ |
33 | -@Command(scope = "onos", name = "apps", | 35 | +@Command(scope = "onos", name = "app-ids", |
34 | description = "Lists application ID information") | 36 | description = "Lists application ID information") |
35 | public class ApplicationIdListCommand extends AbstractShellCommand { | 37 | public class ApplicationIdListCommand extends AbstractShellCommand { |
36 | 38 | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.cli.app; | ||
17 | + | ||
18 | +import org.apache.karaf.shell.console.Completer; | ||
19 | +import org.apache.karaf.shell.console.completer.StringsCompleter; | ||
20 | +import org.onosproject.app.ApplicationService; | ||
21 | +import org.onosproject.cli.AbstractShellCommand; | ||
22 | +import org.onosproject.core.Application; | ||
23 | + | ||
24 | +import java.util.Iterator; | ||
25 | +import java.util.List; | ||
26 | +import java.util.SortedSet; | ||
27 | + | ||
28 | +/** | ||
29 | + * Application name completer. | ||
30 | + */ | ||
31 | +public class ApplicationNameCompleter implements Completer { | ||
32 | + @Override | ||
33 | + public int complete(String buffer, int cursor, List<String> candidates) { | ||
34 | + // Delegate string completer | ||
35 | + StringsCompleter delegate = new StringsCompleter(); | ||
36 | + | ||
37 | + // Fetch our service and feed it's offerings to the string completer | ||
38 | + ApplicationService service = AbstractShellCommand.get(ApplicationService.class); | ||
39 | + Iterator<Application> it = service.getApplications().iterator(); | ||
40 | + SortedSet<String> strings = delegate.getStrings(); | ||
41 | + while (it.hasNext()) { | ||
42 | + strings.add(it.next().id().name()); | ||
43 | + } | ||
44 | + | ||
45 | + // Now let the completer do the work for figuring out what to offer. | ||
46 | + return delegate.complete(buffer, cursor, candidates); | ||
47 | + } | ||
48 | + | ||
49 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.cli.app; | ||
17 | + | ||
18 | +import org.apache.karaf.shell.commands.Argument; | ||
19 | +import org.apache.karaf.shell.commands.Command; | ||
20 | +import org.onosproject.app.ApplicationAdminService; | ||
21 | +import org.onosproject.cli.AbstractShellCommand; | ||
22 | +import org.onosproject.core.ApplicationId; | ||
23 | + | ||
24 | +/** | ||
25 | + * Uninstalls an application. | ||
26 | + */ | ||
27 | +@Command(scope = "onos", name = "app-uninstall", | ||
28 | + description = "Uninstalls an application") | ||
29 | +public class ApplicationUninstallCommand extends AbstractShellCommand { | ||
30 | + | ||
31 | + @Argument(index = 0, name = "name", description = "Application name", | ||
32 | + required = true, multiValued = false) | ||
33 | + String name = null; | ||
34 | + | ||
35 | + @Override | ||
36 | + protected void execute() { | ||
37 | + ApplicationAdminService service = get(ApplicationAdminService.class); | ||
38 | + ApplicationId appId = service.getId(name); | ||
39 | + if (appId != null) { | ||
40 | + service.uninstall(appId); | ||
41 | + } else { | ||
42 | + print("No such application: %s", name); | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | +} |
... | @@ -13,32 +13,33 @@ | ... | @@ -13,32 +13,33 @@ |
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.onosproject.cli; | 16 | +package org.onosproject.cli.app; |
17 | 17 | ||
18 | import org.apache.karaf.shell.commands.Command; | 18 | import org.apache.karaf.shell.commands.Command; |
19 | +import org.onosproject.app.ApplicationService; | ||
20 | +import org.onosproject.cli.AbstractShellCommand; | ||
21 | +import org.onosproject.core.Application; | ||
19 | 22 | ||
20 | -import java.io.BufferedReader; | 23 | +import static org.onosproject.app.ApplicationState.ACTIVE; |
21 | -import java.io.IOException; | ||
22 | -import java.io.InputStreamReader; | ||
23 | 24 | ||
24 | /** | 25 | /** |
25 | - * Lists application ID information. | 26 | + * Lists application information. |
26 | */ | 27 | */ |
27 | -@Command(scope = "onos", name = "app-install", | 28 | +@Command(scope = "onos", name = "apps", |
28 | - description = "Lists application ID information") | 29 | + description = "Lists application information") |
29 | -public class ApplicationInstallCommand extends AbstractShellCommand { | 30 | +public class ApplicationsListCommand extends AbstractShellCommand { |
31 | + | ||
32 | + private static final String FMT = | ||
33 | + "%s id=%d, name=%s, version=%s, origin=%s, description=%s, " + | ||
34 | + "features=%s, featuresRepo=%s, permissions=%s"; | ||
30 | 35 | ||
31 | @Override | 36 | @Override |
32 | protected void execute() { | 37 | protected void execute() { |
33 | - // FIXME: merely an experiment for now | 38 | + ApplicationService service = get(ApplicationService.class); |
34 | - try (InputStreamReader isr = new InputStreamReader(System.in); | 39 | + for (Application app : service.getApplications()) { |
35 | - BufferedReader br = new BufferedReader(isr)) { | 40 | + print(FMT, service.getState(app.id()) == ACTIVE ? "*" : " ", |
36 | - String line; | 41 | + app.id().id(), app.id().name(), app.version(), app.origin(), |
37 | - while ((line = br.readLine()) != null) { | 42 | + app.description(), app.features(), app.featuresRepo(), app.permissions()); |
38 | - print("%s", line.toUpperCase()); | ||
39 | - } | ||
40 | - } catch (IOException e) { | ||
41 | - e.printStackTrace(); | ||
42 | } | 43 | } |
43 | } | 44 | } |
44 | 45 | ... | ... |
... | @@ -21,7 +21,31 @@ | ... | @@ -21,7 +21,31 @@ |
21 | </command> | 21 | </command> |
22 | 22 | ||
23 | <command> | 23 | <command> |
24 | - <action class="org.onosproject.cli.ApplicationInstallCommand"/> | 24 | + <action class="org.onosproject.cli.app.ApplicationsListCommand"/> |
25 | + </command> | ||
26 | + | ||
27 | + <command> | ||
28 | + <action class="org.onosproject.cli.app.ApplicationActivateCommand"/> | ||
29 | + <completers> | ||
30 | + <ref component-id="appNameCompleter"/> | ||
31 | + <null/> | ||
32 | + </completers> | ||
33 | + </command> | ||
34 | + | ||
35 | + <command> | ||
36 | + <action class="org.onosproject.cli.app.ApplicationDeactivateCommand"/> | ||
37 | + <completers> | ||
38 | + <ref component-id="appNameCompleter"/> | ||
39 | + <null/> | ||
40 | + </completers> | ||
41 | + </command> | ||
42 | + | ||
43 | + <command> | ||
44 | + <action class="org.onosproject.cli.app.ApplicationUninstallCommand"/> | ||
45 | + <completers> | ||
46 | + <ref component-id="appNameCompleter"/> | ||
47 | + <null/> | ||
48 | + </completers> | ||
25 | </command> | 49 | </command> |
26 | 50 | ||
27 | <command> | 51 | <command> |
... | @@ -77,7 +101,7 @@ | ... | @@ -77,7 +101,7 @@ |
77 | <action class="org.onosproject.cli.BalanceMastersCommand"/> | 101 | <action class="org.onosproject.cli.BalanceMastersCommand"/> |
78 | </command> | 102 | </command> |
79 | <command> | 103 | <command> |
80 | - <action class="org.onosproject.cli.ApplicationIdListCommand"/> | 104 | + <action class="org.onosproject.cli.app.ApplicationIdListCommand"/> |
81 | </command> | 105 | </command> |
82 | 106 | ||
83 | <command> | 107 | <command> |
... | @@ -275,6 +299,7 @@ | ... | @@ -275,6 +299,7 @@ |
275 | </command> | 299 | </command> |
276 | </command-bundle> | 300 | </command-bundle> |
277 | 301 | ||
302 | + <bean id="appNameCompleter" class="org.onosproject.cli.app.ApplicationNameCompleter"/> | ||
278 | <bean id="nodeIdCompleter" class="org.onosproject.cli.NodeIdCompleter"/> | 303 | <bean id="nodeIdCompleter" class="org.onosproject.cli.NodeIdCompleter"/> |
279 | <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/> | 304 | <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/> |
280 | <bean id="clusterIdCompleter" class="org.onosproject.cli.net.ClusterIdCompleter"/> | 305 | <bean id="clusterIdCompleter" class="org.onosproject.cli.net.ClusterIdCompleter"/> | ... | ... |
... | @@ -39,6 +39,7 @@ import java.io.FileNotFoundException; | ... | @@ -39,6 +39,7 @@ import java.io.FileNotFoundException; |
39 | import java.io.IOException; | 39 | import java.io.IOException; |
40 | import java.io.InputStream; | 40 | import java.io.InputStream; |
41 | import java.net.URI; | 41 | import java.net.URI; |
42 | +import java.nio.file.NoSuchFileException; | ||
42 | import java.util.Set; | 43 | import java.util.Set; |
43 | import java.util.zip.ZipEntry; | 44 | import java.util.zip.ZipEntry; |
44 | import java.util.zip.ZipInputStream; | 45 | import java.util.zip.ZipInputStream; |
... | @@ -63,17 +64,25 @@ public class ApplicationArchive | ... | @@ -63,17 +64,25 @@ public class ApplicationArchive |
63 | 64 | ||
64 | private static Logger log = LoggerFactory.getLogger(ApplicationArchive.class); | 65 | private static Logger log = LoggerFactory.getLogger(ApplicationArchive.class); |
65 | private static final String APP_XML = "app.xml"; | 66 | private static final String APP_XML = "app.xml"; |
67 | + private static final String M2_PREFIX = "m2"; | ||
68 | + | ||
69 | + private static final String KARAF_ROOT = "."; | ||
70 | + private static final String M2_ROOT = "system/"; | ||
66 | private static final String APPS_ROOT = "data/apps/"; | 71 | private static final String APPS_ROOT = "data/apps/"; |
67 | 72 | ||
68 | - private File appsDir = new File(APPS_ROOT); | 73 | + private File karafRoot = new File(KARAF_ROOT); |
74 | + private File m2Dir = new File(karafRoot, M2_ROOT); | ||
75 | + private File appsDir = new File(karafRoot, APPS_ROOT); | ||
69 | 76 | ||
70 | /** | 77 | /** |
71 | * Sets the root directory where application artifacts are kept. | 78 | * Sets the root directory where application artifacts are kept. |
72 | * | 79 | * |
73 | * @param appsRoot top-level applications directory path | 80 | * @param appsRoot top-level applications directory path |
74 | */ | 81 | */ |
75 | - protected void setAppsRoot(String appsRoot) { | 82 | + protected void setRootPath(String appsRoot) { |
76 | - this.appsDir = new File(appsRoot); | 83 | + this.karafRoot = new File(appsRoot); |
84 | + this.appsDir = new File(karafRoot, APPS_ROOT); | ||
85 | + this.m2Dir = new File(karafRoot, M2_ROOT); | ||
77 | } | 86 | } |
78 | 87 | ||
79 | /** | 88 | /** |
... | @@ -81,8 +90,8 @@ public class ApplicationArchive | ... | @@ -81,8 +90,8 @@ public class ApplicationArchive |
81 | * | 90 | * |
82 | * @return top-level applications directory path | 91 | * @return top-level applications directory path |
83 | */ | 92 | */ |
84 | - protected String getAppsRoot() { | 93 | + protected String getRootPath() { |
85 | - return appsDir.getPath(); | 94 | + return karafRoot.getPath(); |
86 | } | 95 | } |
87 | 96 | ||
88 | /** | 97 | /** |
... | @@ -238,10 +247,20 @@ public class ApplicationArchive | ... | @@ -238,10 +247,20 @@ public class ApplicationArchive |
238 | } | 247 | } |
239 | 248 | ||
240 | // Installs application artifacts into M2 repository. | 249 | // Installs application artifacts into M2 repository. |
241 | - private void installArtifacts(ApplicationDescription desc) { | 250 | + private void installArtifacts(ApplicationDescription desc) throws IOException { |
242 | - // FIXME: implement M2 repository copy | 251 | + try { |
252 | + Tools.copyDirectory(appFile(desc.name(), M2_PREFIX), m2Dir); | ||
253 | + } catch (NoSuchFileException e) { | ||
254 | + log.debug("Application {} has no M2 artifacts", desc.name()); | ||
255 | + } | ||
243 | } | 256 | } |
244 | 257 | ||
258 | + /** | ||
259 | + * Marks the app as active by creating token file in the app directory. | ||
260 | + * | ||
261 | + * @param appName application name | ||
262 | + * @return true if file was created | ||
263 | + */ | ||
245 | protected boolean setActive(String appName) { | 264 | protected boolean setActive(String appName) { |
246 | try { | 265 | try { |
247 | return appFile(appName, "active").createNewFile(); | 266 | return appFile(appName, "active").createNewFile(); |
... | @@ -250,10 +269,22 @@ public class ApplicationArchive | ... | @@ -250,10 +269,22 @@ public class ApplicationArchive |
250 | } | 269 | } |
251 | } | 270 | } |
252 | 271 | ||
272 | + /** | ||
273 | + * Clears the app as active by deleting token file in the app directory. | ||
274 | + * | ||
275 | + * @param appName application name | ||
276 | + * @return true if file was deleted | ||
277 | + */ | ||
253 | protected boolean clearActive(String appName) { | 278 | protected boolean clearActive(String appName) { |
254 | return appFile(appName, "active").delete(); | 279 | return appFile(appName, "active").delete(); |
255 | } | 280 | } |
256 | 281 | ||
282 | + /** | ||
283 | + * Indicates whether the app was marked as active by checking for token file. | ||
284 | + * | ||
285 | + * @param appName application name | ||
286 | + * @return true if the app is marked as active | ||
287 | + */ | ||
257 | protected boolean isActive(String appName) { | 288 | protected boolean isActive(String appName) { |
258 | return appFile(appName, "active").exists(); | 289 | return appFile(appName, "active").exists(); |
259 | } | 290 | } | ... | ... |
... | @@ -17,13 +17,17 @@ package org.onosproject.common.app; | ... | @@ -17,13 +17,17 @@ package org.onosproject.common.app; |
17 | 17 | ||
18 | import com.google.common.collect.ImmutableSet; | 18 | import com.google.common.collect.ImmutableSet; |
19 | import com.google.common.io.ByteStreams; | 19 | import com.google.common.io.ByteStreams; |
20 | +import org.junit.After; | ||
20 | import org.junit.Before; | 21 | import org.junit.Before; |
21 | import org.junit.Test; | 22 | import org.junit.Test; |
23 | +import org.onlab.util.Tools; | ||
22 | import org.onosproject.app.ApplicationDescription; | 24 | import org.onosproject.app.ApplicationDescription; |
23 | import org.onosproject.app.ApplicationException; | 25 | import org.onosproject.app.ApplicationException; |
24 | 26 | ||
27 | +import java.io.File; | ||
25 | import java.io.IOException; | 28 | import java.io.IOException; |
26 | import java.io.InputStream; | 29 | import java.io.InputStream; |
30 | +import java.util.Random; | ||
27 | import java.util.Set; | 31 | import java.util.Set; |
28 | 32 | ||
29 | import static org.junit.Assert.assertArrayEquals; | 33 | import static org.junit.Assert.assertArrayEquals; |
... | @@ -32,13 +36,20 @@ import static org.onosproject.app.DefaultApplicationDescriptionTest.*; | ... | @@ -32,13 +36,20 @@ import static org.onosproject.app.DefaultApplicationDescriptionTest.*; |
32 | 36 | ||
33 | public class ApplicationArchiveTest { | 37 | public class ApplicationArchiveTest { |
34 | 38 | ||
35 | - static final String ROOT = "/tmp/app-junit"; | 39 | + static final String ROOT = "/tmp/app-junit/" + new Random().nextInt(); |
36 | 40 | ||
37 | private ApplicationArchive aar = new ApplicationArchive(); | 41 | private ApplicationArchive aar = new ApplicationArchive(); |
38 | 42 | ||
39 | @Before | 43 | @Before |
40 | public void setUp() { | 44 | public void setUp() { |
41 | - aar.setAppsRoot(ROOT); | 45 | + aar.setRootPath(ROOT); |
46 | + } | ||
47 | + | ||
48 | + @After | ||
49 | + public void tearDown() throws IOException { | ||
50 | + if (new File(aar.getRootPath()).exists()) { | ||
51 | + Tools.removeDirectory(aar.getRootPath()); | ||
52 | + } | ||
42 | } | 53 | } |
43 | 54 | ||
44 | private void validate(ApplicationDescription app) { | 55 | private void validate(ApplicationDescription app) { |
... | @@ -77,7 +88,8 @@ public class ApplicationArchiveTest { | ... | @@ -77,7 +88,8 @@ public class ApplicationArchiveTest { |
77 | public void purgeApp() throws IOException { | 88 | public void purgeApp() throws IOException { |
78 | saveApp(); | 89 | saveApp(); |
79 | aar.purgeApplication(APP_NAME); | 90 | aar.purgeApplication(APP_NAME); |
80 | - assertEquals("incorrect names", ImmutableSet.of(), aar.getApplicationNames()); | 91 | + assertEquals("incorrect names", ImmutableSet.<String>of(), |
92 | + aar.getApplicationNames()); | ||
81 | } | 93 | } |
82 | 94 | ||
83 | @Test | 95 | @Test | ... | ... |
... | @@ -21,6 +21,7 @@ import org.apache.felix.scr.annotations.Deactivate; | ... | @@ -21,6 +21,7 @@ import org.apache.felix.scr.annotations.Deactivate; |
21 | import org.apache.felix.scr.annotations.Reference; | 21 | import org.apache.felix.scr.annotations.Reference; |
22 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
23 | import org.apache.felix.scr.annotations.Service; | 23 | import org.apache.felix.scr.annotations.Service; |
24 | +import org.apache.karaf.features.Feature; | ||
24 | import org.apache.karaf.features.FeaturesService; | 25 | import org.apache.karaf.features.FeaturesService; |
25 | import org.onosproject.app.ApplicationAdminService; | 26 | import org.onosproject.app.ApplicationAdminService; |
26 | import org.onosproject.app.ApplicationEvent; | 27 | import org.onosproject.app.ApplicationEvent; |
... | @@ -202,14 +203,20 @@ public class ApplicationManager implements ApplicationService, ApplicationAdminS | ... | @@ -202,14 +203,20 @@ public class ApplicationManager implements ApplicationService, ApplicationAdminS |
202 | 203 | ||
203 | private void installAppFeatures(Application app) throws Exception { | 204 | private void installAppFeatures(Application app) throws Exception { |
204 | for (String name : app.features()) { | 205 | for (String name : app.features()) { |
206 | + Feature feature = featuresService.getFeature(name); | ||
207 | + if (!featuresService.isInstalled(feature)) { | ||
205 | featuresService.installFeature(name); | 208 | featuresService.installFeature(name); |
206 | } | 209 | } |
207 | } | 210 | } |
211 | + } | ||
208 | 212 | ||
209 | private void uninstallAppFeatures(Application app) throws Exception { | 213 | private void uninstallAppFeatures(Application app) throws Exception { |
210 | for (String name : app.features()) { | 214 | for (String name : app.features()) { |
215 | + Feature feature = featuresService.getFeature(name); | ||
216 | + if (featuresService.isInstalled(feature)) { | ||
211 | featuresService.uninstallFeature(name); | 217 | featuresService.uninstallFeature(name); |
212 | } | 218 | } |
213 | } | 219 | } |
220 | + } | ||
214 | 221 | ||
215 | } | 222 | } | ... | ... |
... | @@ -43,6 +43,11 @@ | ... | @@ -43,6 +43,11 @@ |
43 | <artifactId>onlab-netty</artifactId> | 43 | <artifactId>onlab-netty</artifactId> |
44 | <version>${project.version}</version> | 44 | <version>${project.version}</version> |
45 | </dependency> | 45 | </dependency> |
46 | + | ||
47 | + <dependency> | ||
48 | + <groupId>org.onosproject</groupId> | ||
49 | + <artifactId>onos-core-common</artifactId> | ||
50 | + </dependency> | ||
46 | <!-- | 51 | <!-- |
47 | <dependency> | 52 | <dependency> |
48 | <groupId>net.kuujo.copycat</groupId> | 53 | <groupId>net.kuujo.copycat</groupId> | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.store.app; | ||
17 | + | ||
18 | +import com.google.common.collect.ImmutableSet; | ||
19 | +import com.google.common.util.concurrent.ListenableFuture; | ||
20 | +import org.apache.felix.scr.annotations.Activate; | ||
21 | +import org.apache.felix.scr.annotations.Component; | ||
22 | +import org.apache.felix.scr.annotations.Deactivate; | ||
23 | +import org.apache.felix.scr.annotations.Reference; | ||
24 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
25 | +import org.apache.felix.scr.annotations.Service; | ||
26 | +import org.onlab.util.KryoNamespace; | ||
27 | +import org.onosproject.app.ApplicationDescription; | ||
28 | +import org.onosproject.app.ApplicationEvent; | ||
29 | +import org.onosproject.app.ApplicationException; | ||
30 | +import org.onosproject.app.ApplicationState; | ||
31 | +import org.onosproject.app.ApplicationStore; | ||
32 | +import org.onosproject.cluster.ClusterService; | ||
33 | +import org.onosproject.cluster.ControllerNode; | ||
34 | +import org.onosproject.common.app.ApplicationArchive; | ||
35 | +import org.onosproject.core.Application; | ||
36 | +import org.onosproject.core.ApplicationId; | ||
37 | +import org.onosproject.core.ApplicationIdStore; | ||
38 | +import org.onosproject.core.DefaultApplication; | ||
39 | +import org.onosproject.core.Permission; | ||
40 | +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | ||
41 | +import org.onosproject.store.cluster.messaging.ClusterMessage; | ||
42 | +import org.onosproject.store.cluster.messaging.ClusterMessageHandler; | ||
43 | +import org.onosproject.store.cluster.messaging.MessageSubject; | ||
44 | +import org.onosproject.store.impl.EventuallyConsistentMap; | ||
45 | +import org.onosproject.store.impl.EventuallyConsistentMapEvent; | ||
46 | +import org.onosproject.store.impl.EventuallyConsistentMapImpl; | ||
47 | +import org.onosproject.store.impl.EventuallyConsistentMapListener; | ||
48 | +import org.onosproject.store.impl.WallclockClockManager; | ||
49 | +import org.onosproject.store.serializers.KryoNamespaces; | ||
50 | +import org.slf4j.Logger; | ||
51 | + | ||
52 | +import java.io.ByteArrayInputStream; | ||
53 | +import java.io.IOException; | ||
54 | +import java.io.InputStream; | ||
55 | +import java.util.HashMap; | ||
56 | +import java.util.Map; | ||
57 | +import java.util.Set; | ||
58 | +import java.util.concurrent.CountDownLatch; | ||
59 | +import java.util.concurrent.Executors; | ||
60 | +import java.util.concurrent.ScheduledExecutorService; | ||
61 | + | ||
62 | +import static com.google.common.io.ByteStreams.toByteArray; | ||
63 | +import static java.util.concurrent.TimeUnit.MILLISECONDS; | ||
64 | +import static org.onlab.util.Tools.namedThreads; | ||
65 | +import static org.onosproject.app.ApplicationEvent.Type.*; | ||
66 | +import static org.onosproject.store.app.GossipApplicationStore.InternalState.*; | ||
67 | +import static org.onosproject.store.impl.EventuallyConsistentMapEvent.Type.PUT; | ||
68 | +import static org.onosproject.store.impl.EventuallyConsistentMapEvent.Type.REMOVE; | ||
69 | +import static org.slf4j.LoggerFactory.getLogger; | ||
70 | + | ||
71 | +/** | ||
72 | + * Manages inventory of applications in a distributed data store that uses | ||
73 | + * optimistic replication and gossip based anti-entropy techniques. | ||
74 | + */ | ||
75 | +@Component(immediate = true) | ||
76 | +@Service | ||
77 | +public class GossipApplicationStore extends ApplicationArchive | ||
78 | + implements ApplicationStore { | ||
79 | + | ||
80 | + private final Logger log = getLogger(getClass()); | ||
81 | + | ||
82 | + private static final MessageSubject APP_BITS_REQUEST = new MessageSubject("app-bits-request"); | ||
83 | + | ||
84 | + private static final int FETCH_TIMEOUT_MS = 10_000; | ||
85 | + private static final int LOAD_TIMEOUT_MS = 5_000; | ||
86 | + | ||
87 | + public enum InternalState { | ||
88 | + INSTALLED, ACTIVATED, DEACTIVATED | ||
89 | + } | ||
90 | + | ||
91 | + private final ScheduledExecutorService executor = | ||
92 | + Executors.newSingleThreadScheduledExecutor(namedThreads("onos-app-store")); | ||
93 | + | ||
94 | + private EventuallyConsistentMap<ApplicationId, Application> apps; | ||
95 | + private EventuallyConsistentMap<Application, InternalState> states; | ||
96 | + private EventuallyConsistentMap<Application, Set<Permission>> permissions; | ||
97 | + | ||
98 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
99 | + protected ClusterCommunicationService clusterCommunicator; | ||
100 | + | ||
101 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
102 | + protected ClusterService clusterService; | ||
103 | + | ||
104 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
105 | + protected ApplicationIdStore idStore; | ||
106 | + | ||
107 | + @Activate | ||
108 | + public void activate() { | ||
109 | + KryoNamespace.Builder intentSerializer = KryoNamespace.newBuilder() | ||
110 | + .register(KryoNamespaces.API) | ||
111 | + .register(InternalState.class); | ||
112 | + | ||
113 | + clusterCommunicator.addSubscriber(APP_BITS_REQUEST, new InternalBitServer()); | ||
114 | + | ||
115 | + apps = new EventuallyConsistentMapImpl<>("apps", clusterService, | ||
116 | + clusterCommunicator, | ||
117 | + intentSerializer, | ||
118 | + new WallclockClockManager<>()); | ||
119 | + | ||
120 | + states = new EventuallyConsistentMapImpl<>("app-states", | ||
121 | + clusterService, | ||
122 | + clusterCommunicator, | ||
123 | + intentSerializer, | ||
124 | + new WallclockClockManager<>()); | ||
125 | + states.addListener(new InternalAppStatesListener()); | ||
126 | + | ||
127 | + permissions = new EventuallyConsistentMapImpl<>("app-permissions", | ||
128 | + clusterService, | ||
129 | + clusterCommunicator, | ||
130 | + intentSerializer, | ||
131 | + new WallclockClockManager<>()); | ||
132 | + | ||
133 | + // FIXME: figure out load from disk; this will require resolving the dual authority problem | ||
134 | + | ||
135 | + executor.schedule(this::pruneUninstalledApps, LOAD_TIMEOUT_MS, MILLISECONDS); | ||
136 | + | ||
137 | + log.info("Started"); | ||
138 | + } | ||
139 | + | ||
140 | + private void loadFromDisk() { | ||
141 | + for (String name : getApplicationNames()) { | ||
142 | + create(getApplicationDescription(name)); | ||
143 | + // load app permissions | ||
144 | + } | ||
145 | + } | ||
146 | + | ||
147 | + @Deactivate | ||
148 | + public void deactivate() { | ||
149 | + apps.destroy(); | ||
150 | + states.destroy(); | ||
151 | + permissions.destroy(); | ||
152 | + log.info("Stopped"); | ||
153 | + } | ||
154 | + | ||
155 | + @Override | ||
156 | + public Set<Application> getApplications() { | ||
157 | + return ImmutableSet.copyOf(apps.values()); | ||
158 | + } | ||
159 | + | ||
160 | + @Override | ||
161 | + public ApplicationId getId(String name) { | ||
162 | + return idStore.getAppId(name); | ||
163 | + } | ||
164 | + | ||
165 | + @Override | ||
166 | + public Application getApplication(ApplicationId appId) { | ||
167 | + return apps.get(appId); | ||
168 | + } | ||
169 | + | ||
170 | + @Override | ||
171 | + public ApplicationState getState(ApplicationId appId) { | ||
172 | + Application app = apps.get(appId); | ||
173 | + InternalState s = app == null ? null : states.get(app); | ||
174 | + return s == null ? null : s == ACTIVATED ? | ||
175 | + ApplicationState.ACTIVE : ApplicationState.INSTALLED; | ||
176 | + } | ||
177 | + | ||
178 | + @Override | ||
179 | + public Application create(InputStream appDescStream) { | ||
180 | + ApplicationDescription appDesc = saveApplication(appDescStream); | ||
181 | + return create(appDesc); | ||
182 | + } | ||
183 | + | ||
184 | + private Application create(ApplicationDescription appDesc) { | ||
185 | + Application app = registerApp(appDesc); | ||
186 | + apps.put(app.id(), app); | ||
187 | + states.put(app, INSTALLED); | ||
188 | + return app; | ||
189 | + } | ||
190 | + | ||
191 | + @Override | ||
192 | + public void remove(ApplicationId appId) { | ||
193 | + Application app = apps.get(appId); | ||
194 | + if (app != null) { | ||
195 | + apps.remove(appId); | ||
196 | + states.remove(app); | ||
197 | + permissions.remove(app); | ||
198 | + } | ||
199 | + } | ||
200 | + | ||
201 | + @Override | ||
202 | + public void activate(ApplicationId appId) { | ||
203 | + Application app = apps.get(appId); | ||
204 | + if (app != null) { | ||
205 | + states.put(app, ACTIVATED); | ||
206 | + } | ||
207 | + } | ||
208 | + | ||
209 | + @Override | ||
210 | + public void deactivate(ApplicationId appId) { | ||
211 | + Application app = apps.get(appId); | ||
212 | + if (app != null) { | ||
213 | + states.put(app, DEACTIVATED); | ||
214 | + } | ||
215 | + } | ||
216 | + | ||
217 | + @Override | ||
218 | + public Set<Permission> getPermissions(ApplicationId appId) { | ||
219 | + Application app = apps.get(appId); | ||
220 | + return app != null ? permissions.get(app) : null; | ||
221 | + } | ||
222 | + | ||
223 | + @Override | ||
224 | + public void setPermissions(ApplicationId appId, Set<Permission> permissions) { | ||
225 | + Application app = getApplication(appId); | ||
226 | + if (app != null) { | ||
227 | + this.permissions.put(app, permissions); | ||
228 | + delegate.notify(new ApplicationEvent(APP_PERMISSIONS_CHANGED, app)); | ||
229 | + } | ||
230 | + } | ||
231 | + | ||
232 | + /** | ||
233 | + * Listener to application state distributed map changes. | ||
234 | + */ | ||
235 | + private final class InternalAppStatesListener | ||
236 | + implements EventuallyConsistentMapListener<Application, InternalState> { | ||
237 | + @Override | ||
238 | + public void event(EventuallyConsistentMapEvent<Application, InternalState> event) { | ||
239 | + Application app = event.key(); | ||
240 | + InternalState state = event.value(); | ||
241 | + | ||
242 | + if (event.type() == PUT) { | ||
243 | + if (state == INSTALLED) { | ||
244 | + fetchBitsIfNeeded(app); | ||
245 | + delegate.notify(new ApplicationEvent(APP_INSTALLED, app)); | ||
246 | + | ||
247 | + } else if (state == ACTIVATED) { | ||
248 | + installAppIfNeeded(app); | ||
249 | + setActive(app.id().name()); | ||
250 | + delegate.notify(new ApplicationEvent(APP_ACTIVATED, app)); | ||
251 | + | ||
252 | + } else if (state == DEACTIVATED) { | ||
253 | + clearActive(app.id().name()); | ||
254 | + delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app)); | ||
255 | + } | ||
256 | + } else if (event.type() == REMOVE) { | ||
257 | + delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app)); | ||
258 | + purgeApplication(app.id().name()); | ||
259 | + } | ||
260 | + } | ||
261 | + } | ||
262 | + | ||
263 | + /** | ||
264 | + * Determines if the application bits are available locally. | ||
265 | + */ | ||
266 | + private boolean appBitsAvailable(Application app) { | ||
267 | + try { | ||
268 | + ApplicationDescription appDesc = getApplicationDescription(app.id().name()); | ||
269 | + return appDesc.version().equals(app.version()); | ||
270 | + } catch (ApplicationException e) { | ||
271 | + return false; | ||
272 | + } | ||
273 | + } | ||
274 | + | ||
275 | + /** | ||
276 | + * Fetches the bits from the cluster peers if necessary. | ||
277 | + */ | ||
278 | + private void fetchBitsIfNeeded(Application app) { | ||
279 | + if (!appBitsAvailable(app)) { | ||
280 | + fetchBits(app); | ||
281 | + } | ||
282 | + } | ||
283 | + | ||
284 | + /** | ||
285 | + * Installs the application if necessary from the application peers. | ||
286 | + */ | ||
287 | + private void installAppIfNeeded(Application app) { | ||
288 | + if (!appBitsAvailable(app)) { | ||
289 | + fetchBits(app); | ||
290 | + delegate.notify(new ApplicationEvent(APP_INSTALLED, app)); | ||
291 | + } | ||
292 | + } | ||
293 | + | ||
294 | + /** | ||
295 | + * Fetches the bits from the cluster peers. | ||
296 | + */ | ||
297 | + private void fetchBits(Application app) { | ||
298 | + ControllerNode localNode = clusterService.getLocalNode(); | ||
299 | + ClusterMessage message = new ClusterMessage(localNode.id(), APP_BITS_REQUEST, | ||
300 | + app.id().name().getBytes()); | ||
301 | + Map<ControllerNode, ListenableFuture<byte[]>> futures = new HashMap<>(); | ||
302 | + CountDownLatch latch = new CountDownLatch(1); | ||
303 | + | ||
304 | + // FIXME: send message with name & version to make sure we don't get served old bits | ||
305 | + | ||
306 | + log.info("Downloading bits for application {}", app.id().name()); | ||
307 | + for (ControllerNode node : clusterService.getNodes()) { | ||
308 | + try { | ||
309 | + ListenableFuture<byte[]> future = clusterCommunicator.sendAndReceive(message, node.id()); | ||
310 | + future.addListener(new InternalBitListener(app, node, future, latch), executor); | ||
311 | + } catch (IOException e) { | ||
312 | + log.debug("Unable to request bits for application {} from node {}", | ||
313 | + app.id().name(), node.id()); | ||
314 | + } | ||
315 | + } | ||
316 | + | ||
317 | + try { | ||
318 | + if (!latch.await(FETCH_TIMEOUT_MS, MILLISECONDS)) { | ||
319 | + log.warn("Unable to fetch bits for application {}", app.id().name()); | ||
320 | + } | ||
321 | + } catch (InterruptedException e) { | ||
322 | + log.warn("Interrupted while fetching bits for application {}", app.id().name()); | ||
323 | + } | ||
324 | + } | ||
325 | + | ||
326 | + /** | ||
327 | + * Responder to requests for application bits. | ||
328 | + */ | ||
329 | + private class InternalBitServer implements ClusterMessageHandler { | ||
330 | + @Override | ||
331 | + public void handle(ClusterMessage message) { | ||
332 | + String name = new String(message.payload()); | ||
333 | + try { | ||
334 | + message.respond(toByteArray(getApplicationInputStream(name))); | ||
335 | + } catch (Exception e) { | ||
336 | + log.debug("Unable to read bits for application {}", name); | ||
337 | + } | ||
338 | + } | ||
339 | + } | ||
340 | + | ||
341 | + /** | ||
342 | + * Processes completed fetch requests. | ||
343 | + */ | ||
344 | + private class InternalBitListener implements Runnable { | ||
345 | + private final Application app; | ||
346 | + private final ControllerNode node; | ||
347 | + private final ListenableFuture<byte[]> future; | ||
348 | + private final CountDownLatch latch; | ||
349 | + | ||
350 | + public InternalBitListener(Application app, ControllerNode node, | ||
351 | + ListenableFuture<byte[]> future, CountDownLatch latch) { | ||
352 | + this.app = app; | ||
353 | + this.node = node; | ||
354 | + this.future = future; | ||
355 | + this.latch = latch; | ||
356 | + } | ||
357 | + | ||
358 | + @Override | ||
359 | + public void run() { | ||
360 | + if (latch.getCount() > 0 && !future.isCancelled()) { | ||
361 | + try { | ||
362 | + byte[] bits = future.get(1, MILLISECONDS); | ||
363 | + saveApplication(new ByteArrayInputStream(bits)); | ||
364 | + log.info("Downloaded bits for application {} from node {}", | ||
365 | + app.id().name(), node.id()); | ||
366 | + latch.countDown(); | ||
367 | + } catch (Exception e) { | ||
368 | + log.warn("Unable to fetch bits for application {} from node {}", | ||
369 | + app.id().name(), node.id()); | ||
370 | + } | ||
371 | + } | ||
372 | + } | ||
373 | + } | ||
374 | + | ||
375 | + /** | ||
376 | + * Prunes applications which are not in the map, but are on disk. | ||
377 | + */ | ||
378 | + private void pruneUninstalledApps() { | ||
379 | + for (String name : getApplicationNames()) { | ||
380 | + if (getApplication(getId(name)) == null) { | ||
381 | + Application app = registerApp(getApplicationDescription(name)); | ||
382 | + delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app)); | ||
383 | + purgeApplication(app.id().name()); | ||
384 | + } | ||
385 | + } | ||
386 | + } | ||
387 | + | ||
388 | + /** | ||
389 | + * Produces a registered application from the supplied description. | ||
390 | + */ | ||
391 | + private Application registerApp(ApplicationDescription appDesc) { | ||
392 | + ApplicationId appId = idStore.registerApplication(appDesc.name()); | ||
393 | + return new DefaultApplication(appId, appDesc.version(), appDesc.description(), | ||
394 | + appDesc.origin(), appDesc.permissions(), | ||
395 | + appDesc.featuresRepo(), appDesc.features()); | ||
396 | + } | ||
397 | +} | ||
398 | + |
1 | +/* | ||
2 | + * Copyright 2015 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 | +/** | ||
18 | + * Implementation of distributed applications store. | ||
19 | + */ | ||
20 | +package org.onosproject.store.app; |
... | @@ -28,14 +28,17 @@ import org.onlab.packet.IpPrefix; | ... | @@ -28,14 +28,17 @@ import org.onlab.packet.IpPrefix; |
28 | import org.onlab.packet.MacAddress; | 28 | import org.onlab.packet.MacAddress; |
29 | import org.onlab.packet.VlanId; | 29 | import org.onlab.packet.VlanId; |
30 | import org.onlab.util.KryoNamespace; | 30 | import org.onlab.util.KryoNamespace; |
31 | +import org.onosproject.app.ApplicationState; | ||
31 | import org.onosproject.cluster.ControllerNode; | 32 | import org.onosproject.cluster.ControllerNode; |
32 | import org.onosproject.cluster.DefaultControllerNode; | 33 | import org.onosproject.cluster.DefaultControllerNode; |
33 | import org.onosproject.cluster.Leadership; | 34 | import org.onosproject.cluster.Leadership; |
34 | import org.onosproject.cluster.LeadershipEvent; | 35 | import org.onosproject.cluster.LeadershipEvent; |
35 | import org.onosproject.cluster.NodeId; | 36 | import org.onosproject.cluster.NodeId; |
36 | import org.onosproject.cluster.RoleInfo; | 37 | import org.onosproject.cluster.RoleInfo; |
38 | +import org.onosproject.core.DefaultApplication; | ||
37 | import org.onosproject.core.DefaultApplicationId; | 39 | import org.onosproject.core.DefaultApplicationId; |
38 | import org.onosproject.core.DefaultGroupId; | 40 | import org.onosproject.core.DefaultGroupId; |
41 | +import org.onosproject.core.Version; | ||
39 | import org.onosproject.mastership.MastershipTerm; | 42 | import org.onosproject.mastership.MastershipTerm; |
40 | import org.onosproject.net.ConnectPoint; | 43 | import org.onosproject.net.ConnectPoint; |
41 | import org.onosproject.net.DefaultAnnotations; | 44 | import org.onosproject.net.DefaultAnnotations; |
... | @@ -192,7 +195,10 @@ public final class KryoNamespaces { | ... | @@ -192,7 +195,10 @@ public final class KryoNamespaces { |
192 | .register(MISC) | 195 | .register(MISC) |
193 | .nextId(KryoNamespace.INITIAL_ID + 30 + 10) | 196 | .nextId(KryoNamespace.INITIAL_ID + 30 + 10) |
194 | .register( | 197 | .register( |
198 | + Version.class, | ||
195 | ControllerNode.State.class, | 199 | ControllerNode.State.class, |
200 | + ApplicationState.class, | ||
201 | + DefaultApplication.class, | ||
196 | Device.Type.class, | 202 | Device.Type.class, |
197 | Port.Type.class, | 203 | Port.Type.class, |
198 | ChassisId.class, | 204 | ChassisId.class, | ... | ... |
... | @@ -8,4 +8,4 @@ export OCN="10.128.11.4" | ... | @@ -8,4 +8,4 @@ export OCN="10.128.11.4" |
8 | 8 | ||
9 | export OCI="${OC1}" | 9 | export OCI="${OC1}" |
10 | 10 | ||
11 | -export ONOS_FEATURES="webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-gui,onos-rest,onos-app-fwd,onos-app-proxyarp,onos-app-foo" | 11 | +export ONOS_FEATURES="webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-gui,onos-rest,onos-app-fwd,onos-app-proxyarp" | ... | ... |
... | @@ -27,9 +27,11 @@ import java.io.InputStreamReader; | ... | @@ -27,9 +27,11 @@ import java.io.InputStreamReader; |
27 | import java.lang.Thread.UncaughtExceptionHandler; | 27 | import java.lang.Thread.UncaughtExceptionHandler; |
28 | import java.nio.charset.StandardCharsets; | 28 | import java.nio.charset.StandardCharsets; |
29 | import java.nio.file.FileVisitResult; | 29 | import java.nio.file.FileVisitResult; |
30 | +import java.nio.file.Files; | ||
30 | import java.nio.file.Path; | 31 | import java.nio.file.Path; |
31 | import java.nio.file.Paths; | 32 | import java.nio.file.Paths; |
32 | import java.nio.file.SimpleFileVisitor; | 33 | import java.nio.file.SimpleFileVisitor; |
34 | +import java.nio.file.StandardCopyOption; | ||
33 | import java.nio.file.attribute.BasicFileAttributes; | 35 | import java.nio.file.attribute.BasicFileAttributes; |
34 | import java.util.ArrayList; | 36 | import java.util.ArrayList; |
35 | import java.util.List; | 37 | import java.util.List; |
... | @@ -200,4 +202,60 @@ public abstract class Tools { | ... | @@ -200,4 +202,60 @@ public abstract class Tools { |
200 | } | 202 | } |
201 | } | 203 | } |
202 | 204 | ||
205 | + | ||
206 | + /** | ||
207 | + * Copies the specified directory path. Use with great caution since | ||
208 | + * no attempt is made to check for symbolic links, which could result in | ||
209 | + * copy of unintended files. | ||
210 | + * | ||
211 | + * @param src directory to be copied | ||
212 | + * @param dst destination directory to be removed | ||
213 | + * @throws java.io.IOException if unable to remove contents | ||
214 | + */ | ||
215 | + public static void copyDirectory(String src, String dst) throws IOException { | ||
216 | + walkFileTree(Paths.get(src), new DirectoryCopier(src, dst)); | ||
217 | + } | ||
218 | + | ||
219 | + /** | ||
220 | + * Copies the specified directory path. Use with great caution since | ||
221 | + * no attempt is made to check for symbolic links, which could result in | ||
222 | + * copy of unintended files. | ||
223 | + * | ||
224 | + * @param src directory to be copied | ||
225 | + * @param dst destination directory to be removed | ||
226 | + * @throws java.io.IOException if unable to remove contents | ||
227 | + */ | ||
228 | + public static void copyDirectory(File src, File dst) throws IOException { | ||
229 | + walkFileTree(Paths.get(src.getAbsolutePath()), | ||
230 | + new DirectoryCopier(src.getAbsolutePath(), | ||
231 | + dst.getAbsolutePath())); | ||
232 | + } | ||
233 | + | ||
234 | + | ||
235 | + public static class DirectoryCopier extends SimpleFileVisitor<Path> { | ||
236 | + private Path src; | ||
237 | + private Path dst; | ||
238 | + private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING; | ||
239 | + | ||
240 | + DirectoryCopier(String src, String dst) { | ||
241 | + this.src = Paths.get(src); | ||
242 | + this.dst = Paths.get(dst); | ||
243 | + } | ||
244 | + | ||
245 | + @Override | ||
246 | + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { | ||
247 | + Path targetPath = dst.resolve(src.relativize(dir)); | ||
248 | + if (!Files.exists(targetPath)) { | ||
249 | + Files.createDirectory(targetPath); | ||
250 | + } | ||
251 | + return FileVisitResult.CONTINUE; | ||
252 | + } | ||
253 | + | ||
254 | + @Override | ||
255 | + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||
256 | + Files.copy(file, dst.resolve(src.relativize(file)), copyOption); | ||
257 | + return FileVisitResult.CONTINUE; | ||
258 | + } | ||
259 | + } | ||
260 | + | ||
203 | } | 261 | } | ... | ... |
-
Please register or login to post a comment