Yuta HIGUCHI

DatabaseService subsystem: add admin commands, etc.

Change-Id: I24124579f5e0b03ccbf35a03230ae5a7aff95f22
1 +/*
2 + * Copyright 2014 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.onlab.onos.cli;
17 +
18 +import org.apache.karaf.shell.commands.Argument;
19 +import org.apache.karaf.shell.commands.Command;
20 +import org.onlab.onos.cluster.ControllerNode;
21 +import org.onlab.onos.cluster.DefaultControllerNode;
22 +import org.onlab.onos.cluster.NodeId;
23 +import org.onlab.onos.store.service.DatabaseAdminService;
24 +import org.onlab.packet.IpAddress;
25 +
26 +/**
27 + * Adds a new controller cluster node.
28 + */
29 +@Command(scope = "onos", name = "tablet-add",
30 + description = "Adds a new member to tablet")
31 +public class TabletAddCommand extends AbstractShellCommand {
32 +
33 + @Argument(index = 0, name = "nodeId", description = "Node ID",
34 + required = true, multiValued = false)
35 + String nodeId = null;
36 +
37 + // TODO context aware completer to get IP from ClusterService?
38 + @Argument(index = 1, name = "ip", description = "Node IP address",
39 + required = true, multiValued = false)
40 + String ip = null;
41 +
42 + @Argument(index = 2, name = "tcpPort", description = "Node TCP listen port",
43 + required = false, multiValued = false)
44 + int tcpPort = 9876;
45 +
46 + // TODO add tablet name argument when we support multiple tablets
47 +
48 + @Override
49 + protected void execute() {
50 + DatabaseAdminService service = get(DatabaseAdminService.class);
51 + ControllerNode node = new DefaultControllerNode(new NodeId(nodeId),
52 + IpAddress.valueOf(ip),
53 + tcpPort);
54 + service.addMember(node);
55 + }
56 +}
1 +/*
2 + * Copyright 2014 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.onlab.onos.cli;
17 +
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.ObjectMapper;
20 +import com.fasterxml.jackson.databind.node.ArrayNode;
21 +
22 +import org.apache.karaf.shell.commands.Command;
23 +import org.onlab.onos.cluster.ClusterService;
24 +import org.onlab.onos.cluster.ControllerNode;
25 +import org.onlab.onos.store.service.DatabaseAdminService;
26 +
27 +import java.util.Collections;
28 +import java.util.List;
29 +
30 +import static com.google.common.collect.Lists.newArrayList;
31 +
32 +/**
33 + * Lists all controller cluster nodes.
34 + */
35 +@Command(scope = "onos", name = "tablet-member",
36 + description = "Lists all member nodes")
37 +public class TabletMemberCommand extends AbstractShellCommand {
38 +
39 + // TODO add tablet name argument when we support multiple tablets
40 +
41 + @Override
42 + protected void execute() {
43 + DatabaseAdminService service = get(DatabaseAdminService.class);
44 + ClusterService clusterService = get(ClusterService.class);
45 + List<ControllerNode> nodes = newArrayList(service.listMembers());
46 + Collections.sort(nodes, Comparators.NODE_COMPARATOR);
47 + if (outputJson()) {
48 + print("%s", json(service, nodes));
49 + } else {
50 + ControllerNode self = clusterService.getLocalNode();
51 + for (ControllerNode node : nodes) {
52 + print("id=%s, address=%s:%s %s",
53 + node.id(), node.ip(), node.tcpPort(),
54 + node.equals(self) ? "*" : "");
55 + }
56 + }
57 + }
58 +
59 + // Produces JSON structure.
60 + private JsonNode json(DatabaseAdminService service, List<ControllerNode> nodes) {
61 + ObjectMapper mapper = new ObjectMapper();
62 + ArrayNode result = mapper.createArrayNode();
63 + for (ControllerNode node : nodes) {
64 + result.add(mapper.createObjectNode()
65 + .put("id", node.id().toString())
66 + .put("ip", node.ip().toString())
67 + .put("tcpPort", node.tcpPort()));
68 + }
69 + return result;
70 + }
71 +
72 +}
1 +/*
2 + * Copyright 2014 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.onlab.onos.cli;
17 +
18 +import org.apache.karaf.shell.commands.Argument;
19 +import org.apache.karaf.shell.commands.Command;
20 +import org.onlab.onos.cluster.ClusterService;
21 +import org.onlab.onos.cluster.ControllerNode;
22 +import org.onlab.onos.cluster.NodeId;
23 +import org.onlab.onos.store.service.DatabaseAdminService;
24 +
25 +/**
26 + * Removes a controller cluster node.
27 + */
28 +@Command(scope = "onos", name = "tablet-remove",
29 + description = "Removes a member from tablet")
30 +public class TabletRemoveCommand extends AbstractShellCommand {
31 +
32 + @Argument(index = 0, name = "nodeId", description = "Node ID",
33 + required = true, multiValued = false)
34 + String nodeId = null;
35 +
36 + // TODO add tablet name argument when we support multiple tablets
37 +
38 + @Override
39 + protected void execute() {
40 + DatabaseAdminService service = get(DatabaseAdminService.class);
41 + ClusterService clusterService = get(ClusterService.class);
42 + ControllerNode node = clusterService.getNode(new NodeId(nodeId));
43 + if (node != null) {
44 + service.removeMember(node);
45 + }
46 + }
47 +}
...@@ -20,6 +20,26 @@ ...@@ -20,6 +20,26 @@
20 <action class="org.onlab.onos.cli.SummaryCommand"/> 20 <action class="org.onlab.onos.cli.SummaryCommand"/>
21 </command> 21 </command>
22 <command> 22 <command>
23 + <action class="org.onlab.onos.cli.TabletMemberCommand"/>
24 + </command>
25 + <command>
26 + <action class="org.onlab.onos.cli.TabletAddCommand"/>
27 + <completers>
28 + <ref component-id="nodeIdCompleter"/>
29 + <null/>
30 + <null/>
31 + </completers>
32 + </command>
33 + <command>
34 + <action class="org.onlab.onos.cli.TabletRemoveCommand"/>
35 + <completers>
36 + <ref component-id="nodeIdCompleter"/>
37 + <null/>
38 + <null/>
39 + </completers>
40 + </command>
41 +
42 + <command>
23 <action class="org.onlab.onos.cli.NodesListCommand"/> 43 <action class="org.onlab.onos.cli.NodesListCommand"/>
24 </command> 44 </command>
25 <command> 45 <command>
......
1 package org.onlab.onos.store.service; 1 package org.onlab.onos.store.service;
2 2
3 +import java.util.Collection;
3 import java.util.List; 4 import java.util.List;
4 5
6 +import org.onlab.onos.cluster.ControllerNode;
7 +
5 /** 8 /**
6 * Service interface for running administrative tasks on a Database. 9 * Service interface for running administrative tasks on a Database.
7 */ 10 */
...@@ -32,4 +35,26 @@ public interface DatabaseAdminService { ...@@ -32,4 +35,26 @@ public interface DatabaseAdminService {
32 * Deletes all tables from the database. 35 * Deletes all tables from the database.
33 */ 36 */
34 public void dropAllTables(); 37 public void dropAllTables();
38 +
39 +
40 + /**
41 + * Add member to default Tablet.
42 + *
43 + * @param node to add
44 + */
45 + public void addMember(ControllerNode node);
46 +
47 + /**
48 + * Remove member from default Tablet.
49 + *
50 + * @param node node to remove
51 + */
52 + public void removeMember(ControllerNode node);
53 +
54 + /**
55 + * List members forming default Tablet.
56 + *
57 + * @return Copied collection of members forming default Tablet.
58 + */
59 + public Collection<ControllerNode> listMembers();
35 } 60 }
......
...@@ -169,7 +169,9 @@ public class ClusterMessagingProtocol ...@@ -169,7 +169,9 @@ public class ClusterMessagingProtocol
169 @Override 169 @Override
170 public ProtocolClient createClient(TcpMember member) { 170 public ProtocolClient createClient(TcpMember member) {
171 ControllerNode remoteNode = getControllerNode(member.host(), member.port()); 171 ControllerNode remoteNode = getControllerNode(member.host(), member.port());
172 - checkNotNull(remoteNode, "A valid controller node is expected"); 172 + checkNotNull(remoteNode,
173 + "A valid controller node is expected for %s:%s",
174 + member.host(), member.port());
173 return new ClusterMessagingProtocolClient( 175 return new ClusterMessagingProtocolClient(
174 clusterCommunicator, clusterService.getLocalNode(), remoteNode); 176 clusterCommunicator, clusterService.getLocalNode(), remoteNode);
175 } 177 }
......
1 +/*
2 + * Copyright 2014 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.onlab.onos.store.service.impl;
17 +
18 +import static com.google.common.base.Preconditions.checkArgument;
19 +import static com.google.common.base.Preconditions.checkNotNull;
20 +import static org.slf4j.LoggerFactory.getLogger;
21 +
22 +import java.io.File;
23 +import java.io.IOException;
24 +import java.util.HashMap;
25 +import java.util.HashSet;
26 +import java.util.Iterator;
27 +import java.util.Map;
28 +import java.util.Map.Entry;
29 +import java.util.Set;
30 +
31 +import org.onlab.onos.cluster.DefaultControllerNode;
32 +import org.onlab.onos.cluster.NodeId;
33 +import org.onlab.packet.IpAddress;
34 +import org.slf4j.Logger;
35 +
36 +import com.fasterxml.jackson.core.JsonEncoding;
37 +import com.fasterxml.jackson.core.JsonFactory;
38 +import com.fasterxml.jackson.databind.JsonNode;
39 +import com.fasterxml.jackson.databind.ObjectMapper;
40 +import com.fasterxml.jackson.databind.node.ArrayNode;
41 +import com.fasterxml.jackson.databind.node.ObjectNode;
42 +
43 +/**
44 + * Allows for reading and writing tablet definition as a JSON file.
45 + */
46 +public class TabletDefinitionStore {
47 +
48 + private final Logger log = getLogger(getClass());
49 +
50 + private final File file;
51 +
52 + /**
53 + * Creates a reader/writer of the tablet definition file.
54 + *
55 + * @param filePath location of the definition file
56 + */
57 + public TabletDefinitionStore(String filePath) {
58 + file = new File(filePath);
59 + }
60 +
61 + /**
62 + * Creates a reader/writer of the tablet definition file.
63 + *
64 + * @param filePath location of the definition file
65 + */
66 + public TabletDefinitionStore(File filePath) {
67 + file = checkNotNull(filePath);
68 + }
69 +
70 + /**
71 + * Returns the Map from tablet name to set of initial member nodes.
72 + *
73 + * @return Map from tablet name to set of initial member nodes
74 + * @throws IOException when I/O exception of some sort has occurred.
75 + */
76 + public Map<String, Set<DefaultControllerNode>> read() throws IOException {
77 +
78 + final Map<String, Set<DefaultControllerNode>> tablets = new HashMap<>();
79 +
80 + final ObjectMapper mapper = new ObjectMapper();
81 + final ObjectNode tabletNodes = (ObjectNode) mapper.readTree(file);
82 + final Iterator<Entry<String, JsonNode>> fields = tabletNodes.fields();
83 + while (fields.hasNext()) {
84 + final Entry<String, JsonNode> next = fields.next();
85 + final Set<DefaultControllerNode> nodes = new HashSet<>();
86 + final Iterator<JsonNode> elements = next.getValue().elements();
87 + while (elements.hasNext()) {
88 + ObjectNode nodeDef = (ObjectNode) elements.next();
89 + nodes.add(new DefaultControllerNode(new NodeId(nodeDef.get("id").asText()),
90 + IpAddress.valueOf(nodeDef.get("ip").asText()),
91 + nodeDef.get("tcpPort").asInt(9876)));
92 + }
93 +
94 + tablets.put(next.getKey(), nodes);
95 + }
96 + return tablets;
97 + }
98 +
99 + /**
100 + * Updates the Map from tablet name to set of member nodes.
101 + *
102 + * @param tabletName name of the tablet to update
103 + * @param nodes set of initial member nodes
104 + * @throws IOException when I/O exception of some sort has occurred.
105 + */
106 + public void write(String tabletName, Set<DefaultControllerNode> nodes) throws IOException {
107 + checkNotNull(tabletName);
108 + checkArgument(tabletName.isEmpty(), "Tablet name cannot be empty");
109 + // TODO should validate if tabletName is allowed in JSON
110 +
111 + // load current
112 + Map<String, Set<DefaultControllerNode>> config;
113 + try {
114 + config = read();
115 + } catch (IOException e) {
116 + log.info("Reading tablet config failed, assuming empty definition.");
117 + config = new HashMap<>();
118 + }
119 + // update with specified
120 + config.put(tabletName, nodes);
121 +
122 + // write back to file
123 + final ObjectMapper mapper = new ObjectMapper();
124 + final ObjectNode tabletNodes = mapper.createObjectNode();
125 + for (Entry<String, Set<DefaultControllerNode>> tablet : config.entrySet()) {
126 + ArrayNode nodeDefs = mapper.createArrayNode();
127 + tabletNodes.set(tablet.getKey(), nodeDefs);
128 +
129 + for (DefaultControllerNode node : tablet.getValue()) {
130 + ObjectNode nodeDef = mapper.createObjectNode();
131 + nodeDef.put("id", node.id().toString())
132 + .put("ip", node.ip().toString())
133 + .put("tcpPort", node.tcpPort());
134 + nodeDefs.add(nodeDef);
135 + }
136 + }
137 + mapper.writeTree(new JsonFactory().createGenerator(file, JsonEncoding.UTF8),
138 + tabletNodes);
139 + }
140 +
141 +}
1 +onos-config command will copy files contained in this directory to ONOS instances according to cell definition
2 +
...@@ -25,3 +25,18 @@ ssh $remote " ...@@ -25,3 +25,18 @@ ssh $remote "
25 >> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/system.properties 25 >> $ONOS_INSTALL_DIR/$KARAF_DIST/etc/system.properties
26 " 26 "
27 scp -q $CDEF_FILE $remote:$ONOS_INSTALL_DIR/config/ 27 scp -q $CDEF_FILE $remote:$ONOS_INSTALL_DIR/config/
28 +
29 +# Generate a default tablets.json from the ON* environment variables
30 +TDEF_FILE=/tmp/tablets.json
31 +echo "{ \"default\":[" > $TDEF_FILE
32 +for node in $(env | sort | egrep "OC[2-9]+" | cut -d= -f2); do
33 + echo " { \"id\": \"$node\", \"ip\": \"$node\", \"tcpPort\": 9876 }," >> $TDEF_FILE
34 +done
35 +echo " { \"id\": \"$OC1\", \"ip\": \"$OC1\", \"tcpPort\": 9876 }" >> $TDEF_FILE
36 +echo "]}" >> $TDEF_FILE
37 +scp -q $TDEF_FILE $remote:$ONOS_INSTALL_DIR/config/
38 +
39 +
40 +# copy tools/package/config/ to remote
41 +scp -qr ${ONOS_ROOT}/tools/package/config/ $remote:$ONOS_INSTALL_DIR/
42 +
......