initial rough working service edit, with special way to retrieve custom values

This commit is contained in:
Blake J. Harnden 2018-05-23 12:41:29 -07:00
parent 517ef4c3d3
commit 3e5cd61ecc
6 changed files with 331 additions and 13 deletions

View file

@ -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):

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -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;
}

View file

@ -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">