initial rough working service edit, with special way to retrieve custom values
This commit is contained in:
parent
517ef4c3d3
commit
3e5cd61ecc
6 changed files with 331 additions and 13 deletions
163
webapp/app.py
163
webapp/app.py
|
@ -17,7 +17,7 @@ from core.emulator.coreemu import CoreEmu
|
|||
from core.emulator.emudata import InterfaceData
|
||||
from core.emulator.emudata import LinkOptions
|
||||
from core.emulator.emudata import NodeOptions
|
||||
from core.enumerations import EventTypes, ConfigFlags
|
||||
from core.enumerations import EventTypes, ConfigFlags, ConfigDataTypes
|
||||
from core.enumerations import LinkTypes
|
||||
from core.enumerations import NodeTypes
|
||||
from core.misc import nodeutils
|
||||
|
@ -32,6 +32,13 @@ socketio = SocketIO(app)
|
|||
coreemu = CoreEmu()
|
||||
|
||||
|
||||
def custom_service_values(service):
|
||||
valmap = [service._dirs, service._configs, service._startindex, service._startup,
|
||||
service._shutdown, service._validate, service._meta, service._starttime]
|
||||
vals = map(lambda a, b: "%s=%s" % (a, str(b)), service.keys, valmap)
|
||||
return "|".join(vals)
|
||||
|
||||
|
||||
def synchronized(function):
|
||||
global CORE_LOCK
|
||||
|
||||
|
@ -142,7 +149,7 @@ def get_sessions():
|
|||
@synchronized
|
||||
def create_session():
|
||||
session = coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
session.set_state(EventTypes.DEFINITION_STATE)
|
||||
|
||||
# set session location
|
||||
session.location.setrefgeo(47.57917, -122.13232, 2.0)
|
||||
|
@ -340,6 +347,9 @@ def create_node(session_id):
|
|||
)
|
||||
session.config_object(config_data)
|
||||
|
||||
logger.info("custom services: %s", session.services.customservices)
|
||||
for service in node.services:
|
||||
logger.info("node services: %s - %s", service._name, service._custom)
|
||||
return jsonify(
|
||||
id=node.objid,
|
||||
url="/sessions/%s/nodes/%s" % (session_id, node.objid)
|
||||
|
@ -459,15 +469,18 @@ def delete_node(session_id, node_id):
|
|||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>/services")
|
||||
def node_services(session_id, node_id):
|
||||
def get_node_services(session_id, node_id):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
|
||||
config_data = ConfigData(
|
||||
node=node_id,
|
||||
object="services",
|
||||
type=1,
|
||||
type=ConfigFlags.REQUEST.value,
|
||||
)
|
||||
logger.debug("configuration message for %s node %s", config_data.object, config_data.node)
|
||||
|
||||
|
@ -489,6 +502,148 @@ def node_services(session_id, node_id):
|
|||
return jsonify(services)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>/services/<service>")
|
||||
def get_node_service(session_id, node_id, service):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
|
||||
config_data = ConfigData(
|
||||
node=node_id,
|
||||
object="services",
|
||||
opaque="service:%s" % service,
|
||||
type=ConfigFlags.REQUEST.value,
|
||||
)
|
||||
logger.debug("configuration message for %s node %s", config_data.object, config_data.node)
|
||||
|
||||
# dispatch to any registered callback for this object type
|
||||
replies = session.config_object(config_data)
|
||||
if len(replies) != 1:
|
||||
return jsonify(error="failure getting node services"), 404
|
||||
response = replies[0]
|
||||
data_values = response.data_values
|
||||
|
||||
# check if this is a custom service, since core cant currently handle this right
|
||||
node_services, _ = session.services.servicesfromopaque("service:%s" % service, node_id)
|
||||
node_service = node_services[0]
|
||||
if node_service._custom:
|
||||
data_values = custom_service_values(node_service)
|
||||
|
||||
data_values = data_values.split("|")
|
||||
service_config = {}
|
||||
for data_value in data_values:
|
||||
name, value = data_value.split("=")
|
||||
if value.startswith("("):
|
||||
value = value.strip("()").split(",")
|
||||
value = [x.strip(" '") for x in value if x]
|
||||
service_config[name] = value
|
||||
|
||||
return jsonify(service_config)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>/services/<service>", methods=["PUT"])
|
||||
def set_node_service(session_id, node_id, service):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
node = session.objects.get(node_id)
|
||||
if not node:
|
||||
return jsonify(error="node does not exist"), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
data_types = list()
|
||||
data_values = list()
|
||||
data_types.append(ConfigDataTypes.STRING.value)
|
||||
data_values.append("startidx=%s" % data["index"])
|
||||
index = data["time"]
|
||||
if index:
|
||||
data_types.append(ConfigDataTypes.STRING.value)
|
||||
data_values.append("starttime=%s" % index)
|
||||
startup = create_tuple_str(data["startup"])
|
||||
if startup:
|
||||
data_types.append(ConfigDataTypes.STRING.value)
|
||||
data_values.append("cmdup=%s" % startup)
|
||||
shutdown = create_tuple_str(data["shutdown"])
|
||||
if shutdown:
|
||||
data_types.append(ConfigDataTypes.STRING.value)
|
||||
data_values.append("cmddown=%s" % shutdown)
|
||||
validate = create_tuple_str(data["validate"])
|
||||
if validate:
|
||||
data_types.append(ConfigDataTypes.STRING.value)
|
||||
data_values.append("cmdval=%s" % validate)
|
||||
data_values = "|".join(data_values)
|
||||
logger.info("service types: %s", data_types)
|
||||
logger.info("service values: %s", data_values)
|
||||
|
||||
config_data = ConfigData(
|
||||
node=node_id,
|
||||
object="services",
|
||||
opaque="service:%s" % service,
|
||||
type=ConfigFlags.NONE.value,
|
||||
data_types=data_types,
|
||||
data_values=data_values
|
||||
)
|
||||
session.config_object(config_data)
|
||||
logger.info("custom services: %s", session.services.customservices)
|
||||
return jsonify()
|
||||
|
||||
|
||||
def create_tuple_str(data):
|
||||
if not data:
|
||||
return None
|
||||
data = data.strip()
|
||||
|
||||
output = "("
|
||||
for line in data.split("\n"):
|
||||
output += "'%s'," % line.strip()
|
||||
output += ")"
|
||||
return output
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>/services/<service>/file")
|
||||
def get_node_service_file(session_id, node_id, service):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
node = session.objects.get(node_id)
|
||||
if not node:
|
||||
return jsonify(error="node does not exist"), 404
|
||||
|
||||
service_file = request.args["file"]
|
||||
services, _ = session.services.servicesfromopaque("service:%s" % service, node_id)
|
||||
file_data = session.services.getservicefile(services, node, service_file)
|
||||
return jsonify(file_data.data)
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/nodes/<node_id>/services/<service>/file", methods=["PUT"])
|
||||
def set_node_service_file(session_id, node_id, service):
|
||||
session = coreemu.sessions.get(session_id)
|
||||
if not session:
|
||||
return jsonify(error="session does not exist"), 404
|
||||
|
||||
if node_id.isdigit():
|
||||
node_id = int(node_id)
|
||||
node = session.objects.get(node_id)
|
||||
if not node:
|
||||
return jsonify(error="node does not exist"), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
file_name = data["name"]
|
||||
data = data["data"]
|
||||
session.add_node_service_file(node_id, service, file_name, None, data)
|
||||
return jsonify()
|
||||
|
||||
|
||||
@app.route("/sessions/<int:session_id>/state", methods=["PUT"])
|
||||
@synchronized
|
||||
def set_session_state(session_id):
|
||||
|
|
|
@ -31,11 +31,18 @@
|
|||
padding: .5rem 1.25rem;
|
||||
}
|
||||
|
||||
#config-modal {
|
||||
.modal {
|
||||
overflow-y: initial !important;
|
||||
}
|
||||
|
||||
#config-modal .modal-body {
|
||||
.modal-body {
|
||||
height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.file-content {
|
||||
background-color: #000 !important;
|
||||
color: #00ff26 !important;
|
||||
font-size: small;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
|
|
@ -401,6 +401,10 @@ class CoreNetwork {
|
|||
}
|
||||
|
||||
async start() {
|
||||
// clear current session and set for nodes to start
|
||||
await coreRest.setSessionState(SessionStates.definition);
|
||||
await coreRest.setSessionState(SessionStates.configuration);
|
||||
|
||||
const nodes = coreNetwork.getCoreNodes();
|
||||
for (let node of nodes) {
|
||||
const response = await coreRest.createNode(node);
|
||||
|
|
|
@ -86,6 +86,10 @@ class CoreRest {
|
|||
return await putJson(`/sessions/${this.currentSession}/config`, config);
|
||||
}
|
||||
|
||||
async getNode(nodeId) {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}`);
|
||||
}
|
||||
|
||||
async createNode(node) {
|
||||
return await postJson(`/sessions/${this.currentSession}/nodes`, node);
|
||||
}
|
||||
|
@ -111,11 +115,31 @@ class CoreRest {
|
|||
}
|
||||
|
||||
async getLinks(nodeId) {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/links`)
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/links`);
|
||||
}
|
||||
|
||||
async getServices(nodeId) {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/services`)
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/services`);
|
||||
}
|
||||
|
||||
async getService(nodeId, service) {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/services/${service}`);
|
||||
}
|
||||
|
||||
async setService(nodeId, service, data) {
|
||||
return await putJson(`/sessions/${this.currentSession}/nodes/${nodeId}/services/${service}`, data);
|
||||
}
|
||||
|
||||
async getServiceFile(nodeId, service, serviceFile) {
|
||||
return await $.getJSON(`/sessions/${this.currentSession}/nodes/${nodeId}/services/${service}/file`,
|
||||
{file: serviceFile});
|
||||
}
|
||||
|
||||
async setServiceFile(nodeId, service, serviceFile, data) {
|
||||
return await putJson(`/sessions/${this.currentSession}/nodes/${nodeId}/services/${service}/file`, {
|
||||
name: serviceFile,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
async getNodeIps(nodeId, ip4Prefix, ip6Prefix) {
|
||||
|
|
|
@ -149,18 +149,88 @@ class ServiceModal {
|
|||
constructor(coreRest) {
|
||||
this.coreRest = coreRest;
|
||||
this.$modal = $('#service-modal');
|
||||
this.$form = this.$modal.find('form');
|
||||
this.$files = this.$modal.find('select[name=file]');
|
||||
this.$files.change(this.fileChange.bind(this));
|
||||
this.$fileContent = this.$modal.find('textarea[name=filecontent]');
|
||||
this.$startIndex = this.$modal.find('input[name=index]');
|
||||
this.$startTime = this.$modal.find('input[name=time]');
|
||||
this.$startup = this.$modal.find('textarea[name=startup]');
|
||||
this.$shutdown = this.$modal.find('textarea[name=shutdown]');
|
||||
this.$validate = this.$modal.find('textarea[name=validate]');
|
||||
this.$title = this.$modal.find('.modal-title');
|
||||
this.$saveButton = $('#service-button');
|
||||
this.$saveButton.click(this.onClick.bind(this));
|
||||
this.$saveButton.click(this.saveClicked.bind(this));
|
||||
this.node = null;
|
||||
this.service = null;
|
||||
}
|
||||
|
||||
async show(service) {
|
||||
async fileChange(event) {
|
||||
const currentFile = this.$files.val();
|
||||
console.log('current file: ', currentFile);
|
||||
if (currentFile) {
|
||||
const fileData = await this.coreRest.getServiceFile(this.node.id, this.service, currentFile);
|
||||
this.$fileContent.val(fileData);
|
||||
}
|
||||
}
|
||||
|
||||
async show(node, service) {
|
||||
this.node = node;
|
||||
this.service = service;
|
||||
this.$title.text(`Edit ${service}`);
|
||||
this.$modal.modal('show');
|
||||
|
||||
try {
|
||||
await this.coreRest.getNode(node.id);
|
||||
} catch (err) {
|
||||
console.log('node does not exist, creating for editing services');
|
||||
await this.coreRest.createNode(node);
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
try {
|
||||
const response = await this.coreRest.getService(node.id, service);
|
||||
console.log('service data: ', response);
|
||||
this.$files.html('');
|
||||
for (let fileName of response.files) {
|
||||
const $option = $('<option>', {value: fileName, text: fileName});
|
||||
this.$files.append($option);
|
||||
}
|
||||
|
||||
this.$fileContent.val('');
|
||||
this.$files.change();
|
||||
this.$startIndex.val(response.startidx);
|
||||
this.$startTime.val(response.starttime);
|
||||
this.$startup.val(response.cmdup.join('\n'));
|
||||
this.$shutdown.val(response.cmddown.join('\n'));
|
||||
this.$validate.val(response.cmdval.join('\n'));
|
||||
|
||||
this.$modal.modal('show');
|
||||
} catch (err) {
|
||||
console.log('error getting service data: ', err);
|
||||
toastr.error('Get service error', 'Internal Error');
|
||||
}
|
||||
}
|
||||
|
||||
async saveClicked() {
|
||||
const formData = formToJson(this.$form);
|
||||
console.log('saved service data: ', formData);
|
||||
|
||||
// update current service file data
|
||||
try {
|
||||
await this.coreRest.setServiceFile(this.node.id, this.service, formData.file, formData.filecontent);
|
||||
} catch (err) {
|
||||
console.log('error saving service file data: ', err);
|
||||
}
|
||||
|
||||
// update all other service settings
|
||||
delete formData.file;
|
||||
delete formData.filecontent;
|
||||
try {
|
||||
await this.coreRest.setService(this.node.id, this.service, formData);
|
||||
} catch (err) {
|
||||
console.log('error saving service data: ', err);
|
||||
}
|
||||
|
||||
this.$modal.modal('hide');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +266,7 @@ class ServicesModal {
|
|||
const service = $target.parent().parent().find('label').text();
|
||||
console.log('edit service: ', service);
|
||||
this.$modal.modal('hide');
|
||||
this.serviceModal.show(service);
|
||||
this.serviceModal.show(this.node, service);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,65 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs mb-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#service-files" role="tab"
|
||||
aria-selected="true">
|
||||
Files
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#service-lifecycle" role="tab"
|
||||
aria-selected="true">
|
||||
Lifecycle
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="service-files" role="tabpanel">
|
||||
<div class="form-group">
|
||||
<label>File Name</label>
|
||||
<select name="file" class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>File Content</label>
|
||||
<textarea name="filecontent" class="form-control file-content" rows="15"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="service-lifecycle" role="tabpanel">
|
||||
<div class="form-row">
|
||||
<div class="form-group col">
|
||||
<label>Startup Index</label>
|
||||
<input name="index" class="form-control" type="text" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group col">
|
||||
<label>Startup Time</label>
|
||||
<input name="time" class="form-control" type="text">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Startup</label>
|
||||
<textarea name="startup" class="form-control file-content" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Shutdown</label>
|
||||
<textarea name="shutdown" class="form-control file-content" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Validate</label>
|
||||
<textarea name="validate" class="form-control file-content" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
Loading…
Add table
Reference in a new issue