blob: 0d3b1c3f2e0fa600c89ce7a6cf86a84741b5ee31 [file] [log] [blame]
Ray Milkey644472e2017-06-19 13:34:25 -07001#!/usr/bin/env python
2"""
Brian O'Connora09fe5b2017-08-03 21:12:30 -07003 Copyright 2017-present Open Networking Foundation
Ray Milkey644472e2017-06-19 13:34:25 -07004 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
Ray Milkey589b8992017-06-20 12:02:01 -070017import argparse
18import json
Ray Milkeyff18b6e2017-07-13 08:34:19 -070019import subprocess
Ray Milkey589b8992017-06-20 12:02:01 -070020import sys
Ray Milkeyff18b6e2017-07-13 08:34:19 -070021import os
Ray Milkey589b8992017-06-20 12:02:01 -070022from httplib import OK, NOT_FOUND, BAD_REQUEST
Ray Milkeyff18b6e2017-07-13 08:34:19 -070023from subprocess import Popen
Ray Milkey644472e2017-06-19 13:34:25 -070024
Ray Milkeyff18b6e2017-07-13 08:34:19 -070025from flask import Flask, jsonify, request
26from jsonschema import validate
Ray Milkey644472e2017-06-19 13:34:25 -070027
28"""
29Onos Distributed Manager
30this app handles controller information in system
31
32Message Samples :
33--Adding node
Ray Milkeyff18b6e2017-07-13 08:34:19 -070034curl -H "Content-Type: application/json" -X POST --data '{"id":"node-1","ip":"1.2.3.4","port":3456}' http://localhost:5000
Ray Milkey644472e2017-06-19 13:34:25 -070035--Updating node
Ray Milkeyff18b6e2017-07-13 08:34:19 -070036curl -H "Content-Type: application/json" -X PUT --data '{"id":"node-1","ip":"1.2.3.4","port":3456}' http://localhost:5000
Ray Milkey644472e2017-06-19 13:34:25 -070037--Deleting node
Ray Milkeyff18b6e2017-07-13 08:34:19 -070038curl -H "Content-Type: application/json" -X DELETE --data '{"id":"node-1","ip":"1.2.3.4","port":3456}' http://localhost:5000
Ray Milkey644472e2017-06-19 13:34:25 -070039--Getting node data
40curl -X GET http://10.15.176.228:5000/cluster.json
41
42"""
43
Ray Milkey589b8992017-06-20 12:02:01 -070044
45schema = {
46 "type": "object",
47 "properties": {
Ray Milkeyff18b6e2017-07-13 08:34:19 -070048 "id": {"type": "string"},
49 "port": {"type": "number"},
50 "ip": {"type": "string"},
Ray Milkey589b8992017-06-20 12:02:01 -070051 },
Ray Milkeyff18b6e2017-07-13 08:34:19 -070052 "required": ["id", "port", "ip"]
Ray Milkey589b8992017-06-20 12:02:01 -070053}
54
55
56def is_valid_node_json(content):
57 try:
58 validate(content, schema)
59 except Exception as validation_error:
60 return False
61
62 return True
63
64class Manager:
Ray Milkeyff18b6e2017-07-13 08:34:19 -070065 cluster_config = {
66 "nodes": [],
67 "name": 315256287,
68 "partitions": []
69 }
Ray Milkey589b8992017-06-20 12:02:01 -070070 persistence_filename = ""
71
Ray Milkeyff18b6e2017-07-13 08:34:19 -070072 def found_node_index(self, content):
73 for index, node in enumerate(self.cluster_config["nodes"]):
74 if node["id"] == content["id"]:
75 return index
76 return None
77
78
Ray Milkey589b8992017-06-20 12:02:01 -070079 def save_to_file(self):
Ray Milkeyff18b6e2017-07-13 08:34:19 -070080 config = self.cluster_config
81 if len(self.cluster_config["nodes"]) > 0:
82 command = ["/Users/ray/Documents/work/onos-next/tools/test/bin/onos-gen-partitions", "" ]
83 for controller_node in self.cluster_config["nodes"]:
84 node_string = controller_node["id"]
85 node_string += ":"
86 node_string += controller_node["ip"]
87 node_string += ":"
88 node_string += str(controller_node["port"])
89 command.append(node_string)
90 process = Popen(command, stdout=subprocess.PIPE)
91 config = json.load(process.stdout)
92 self.cluster_config = config
93 else:
94 self.cluster_config["nodes"] = []
95 self.cluster_config["partitions"] = []
Ray Milkey589b8992017-06-20 12:02:01 -070096 try:
97 with open(self.persistence_filename, 'w') as file:
Ray Milkeyff18b6e2017-07-13 08:34:19 -070098 file.write(json.dumps(config))
Ray Milkey589b8992017-06-20 12:02:01 -070099 file.close()
100 except Exception as error:
101 print error
102 sys.exit()
103
104 def load_from_file(self):
105 try:
106 with open(self.persistence_filename, 'r') as file:
107 data = file.read()
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700108 self.cluster_config = json.loads(data)
Ray Milkey589b8992017-06-20 12:02:01 -0700109 except Exception:
110 self.save_to_file()
111
112 def data_get_handler(self):
113
114 pagereturn = "<h2> Onos Distributed Controller Manager2 </h2>"
115 pagereturn += "<br><h3> Status of Added controllers </h3><br>"
116 pagereturn += " Id,&emsp; Ip,&emsp; Port,&emsp; Is Active <br> "
117
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700118 for key in self.cluster_config["nodes"].keys():
119 pagereturn += self.cluster_config["nodes"][key]["id"] + ",&emsp; " + \
120 self.cluster_config["nodes"][key]["ip"] + ",&emsp; " + \
121 str(self.cluster_config["nodes"][key]["port"]) + ",&emsp; "
Ray Milkey589b8992017-06-20 12:02:01 -0700122 pagereturn += " <br>"
123
124 return pagereturn, OK
125
126 def data_post_handler(self, content):
127 if not is_valid_node_json(content):
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700128 return "id, ip, and port must be specified", \
Ray Milkey589b8992017-06-20 12:02:01 -0700129 BAD_REQUEST
130
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700131 if self.found_node_index(content) is not None:
Ray Milkey589b8992017-06-20 12:02:01 -0700132 return "Content Id is already in the list", BAD_REQUEST
133
134 else:
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700135 self.cluster_config["nodes"].append(content)
Ray Milkey589b8992017-06-20 12:02:01 -0700136 self.save_to_file()
137
138 return "POST called with content", OK
139
140 def data_put_handler(self, content):
141 if not is_valid_node_json(content):
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700142 return "id, ip, and port must be specified", \
Ray Milkey589b8992017-06-20 12:02:01 -0700143 BAD_REQUEST
144
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700145 node_index = self.found_node_index(content)
146 if node_index is not None:
147 self.cluster_config["nodes"][node_index] = content
Ray Milkey589b8992017-06-20 12:02:01 -0700148 self.save_to_file()
149 return "Update succeeded", OK
150
151 else:
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700152 return "Id %s is not found " % (content["id"]), NOT_FOUND
Ray Milkey589b8992017-06-20 12:02:01 -0700153
154 def data_delete_handler(self, content):
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700155 node_index = self.found_node_index(content)
156 if node_index is not None:
157 del self.cluster_config["nodes"][node_index]
Ray Milkey589b8992017-06-20 12:02:01 -0700158 self.save_to_file()
159 return "Deletion succeed.", OK
160
161 else:
162 return "Id is not found", NOT_FOUND
163
164 """
165 This function returns onos cluster information
166 based on data in memory
167 """
168
169 def cluster_responder(self):
170
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700171 return jsonify(self.cluster_config), OK
Ray Milkey589b8992017-06-20 12:02:01 -0700172
173app = Flask(__name__)
174manager = Manager()
175
Ray Milkey644472e2017-06-19 13:34:25 -0700176
177@app.route('/', methods=['GET'])
178def data_get_handler():
Ray Milkey589b8992017-06-20 12:02:01 -0700179 return manager.data_get_handler()
Ray Milkey644472e2017-06-19 13:34:25 -0700180
181
182@app.route('/cluster.json', methods=['GET'])
183def cluster_responder():
Ray Milkey589b8992017-06-20 12:02:01 -0700184 return manager.cluster_responder()
Ray Milkey644472e2017-06-19 13:34:25 -0700185
Ray Milkey644472e2017-06-19 13:34:25 -0700186
Ray Milkey589b8992017-06-20 12:02:01 -0700187@app.route('/', methods=['POST'])
188def data_post_handler():
189 if request.is_json:
190 content = dict(request.json)
191 return manager.data_post_handler(content)
192 else:
193 return "json required", BAD_REQUEST
Ray Milkey644472e2017-06-19 13:34:25 -0700194
Ray Milkey644472e2017-06-19 13:34:25 -0700195
Ray Milkeyff18b6e2017-07-13 08:34:19 -0700196@app.route('/exit', methods=['PUT'])
197def data_exit_handler():
198 func = request.environ.get('werkzeug.server.shutdown')
199 if func is None:
200 raise RuntimeError('Not running with the Werkzeug Server')
201 func()
202 return "", OK
203
204
Ray Milkey589b8992017-06-20 12:02:01 -0700205@app.route('/', methods=['PUT'])
206def data_put_handler():
207 if request.is_json:
208 content = dict(request.json)
209 return manager.data_put_handler(content)
210 else:
211 return "json data is missing", BAD_REQUEST
212
213
214@app.route('/', methods=['DELETE'])
215def data_delete_handler():
216 if request.is_json:
217 content = dict(request.json)
218 return manager.data_delete_handler(content)
219 else:
220 return "No json is found", BAD_REQUEST
221
222
223def main():
224 parser = argparse.ArgumentParser(
225 description="Web server and app to maintain ONOS cluster state.")
226 parser.add_argument(
227 '-p', '--persistence-filename',
228 default='./onos-distributed-manager-persistence.json',
229 help="Filename used to store persistence information." )
230
231 args = parser.parse_args()
232 manager.persistence_filename = args.persistence_filename
233 manager.load_from_file()
234
235 app.run(host="0.0.0.0", debug=True)
236
Ray Milkey644472e2017-06-19 13:34:25 -0700237
238if __name__ == '__main__':
Ray Milkey589b8992017-06-20 12:02:01 -0700239 manager = Manager()
240 main()