Loading src/webui/proto/service_pb2.py +7 −27 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xed\x02\n\x0eServiceService\x12\x38\n\x0eGetServiceList\x12\x0e.context.Empty\x1a\x14.context.ServiceList\"\x00\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x38\n\x0eGetServiceById\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12>\n\x11GetConnectionList\x12\x0e.context.Empty\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) Loading @@ -38,22 +38,12 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=42, serialized_end=407, serialized_end=295, methods=[ _descriptor.MethodDescriptor( name='GetServiceList', full_name='service.ServiceService.GetServiceList', index=0, containing_service=None, input_type=context__pb2._EMPTY, output_type=context__pb2._SERVICELIST, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='CreateService', full_name='service.ServiceService.CreateService', index=1, index=0, containing_service=None, input_type=context__pb2._SERVICE, output_type=context__pb2._SERVICEID, Loading @@ -63,7 +53,7 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='UpdateService', full_name='service.ServiceService.UpdateService', index=2, index=1, containing_service=None, input_type=context__pb2._SERVICE, output_type=context__pb2._SERVICEID, Loading @@ -73,29 +63,19 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='DeleteService', full_name='service.ServiceService.DeleteService', index=3, index=2, containing_service=None, input_type=context__pb2._SERVICEID, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='GetServiceById', full_name='service.ServiceService.GetServiceById', index=4, containing_service=None, input_type=context__pb2._SERVICEID, output_type=context__pb2._SERVICE, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='GetConnectionList', full_name='service.ServiceService.GetConnectionList', index=5, index=3, containing_service=None, input_type=context__pb2._EMPTY, input_type=context__pb2._SERVICEID, output_type=context__pb2._CONNECTIONLIST, serialized_options=None, create_key=_descriptor._internal_create_key, Loading src/webui/service/device/forms.py +31 −6 Original line number Diff line number Diff line # external imports from flask_wtf import FlaskForm from wtforms import StringField, SelectField, SubmitField from wtforms.validators import DataRequired, Length, NumberRange from wtforms import StringField, SelectField, TextAreaField, SubmitField from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError from webui.utils.form_validators import key_value_validator from webui.proto.context_pb2 import (DeviceDriverEnum, DeviceOperationalStatusEnum) class AddDeviceForm(FlaskForm): device_id = StringField('Device ID', device_id = StringField('ID', validators=[DataRequired(), Length(min=5)]) device_type = StringField('Device Type', device_type = StringField('Type', validators=[DataRequired(), Length(min=5)]) device_config = TextAreaField('Configurations', validators=[key_value_validator()]) operational_status = SelectField('Operational Status', choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], # choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], coerce=int, validators=[DataRequired(), NumberRange(min=0)]) validators=[NumberRange(min=0)]) device_drivers = TextAreaField('Drivers', validators=[DataRequired(), Regexp('^\d+(,\d+)*$')]) submit = SubmitField('Add') def validate_operational_status(form, field): if field.data not in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_number: raise ValidationError('The operational status value selected is incorrect!') def validate_device_drivers(form, field): if ',' not in field.data: data = str(field.data) + ',' else: data = field.data for value in data.split(','): value = value.strip() if len(value) == 0: continue try: value_int = int(value) except: raise ValidationError(f'The value "{value}" is not a valid driver identified.') if value_int not in DeviceDriverEnum.DESCRIPTOR.values_by_number: values = ', '.join([str(x) for x in DeviceDriverEnum.DESCRIPTOR.values_by_number]) raise ValidationError(f'The device driver {value_int} is not correct. Allowed values are: {values}.') src/webui/service/device/routes.py +56 −3 Original line number Diff line number Diff line from flask import render_template, Blueprint, flash, session from context.proto.context_pb2 import ConfigActionEnum, ConfigRule, TopologyIdList, TopologyList from webui.Config import CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT from context.client.ContextClient import ContextClient from webui.proto.context_pb2 import (ContextId, DeviceList, DeviceId, Loading @@ -21,10 +22,63 @@ def home(): @device.route('add', methods=['GET', 'POST']) def add(): form = AddDeviceForm() request: ContextId = ContextId() request.context_uuid.uuid = session['context_uuid'] client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) response: TopologyIdList = client.ListTopologyIds(request) client.close() # listing enum values form.operational_status.choices = [(-1, 'Select...')] for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): form.operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) # device driver ids device_driver_ids = [] for key in DeviceDriverEnum.DESCRIPTOR.values_by_name: device_driver_ids.append(f"{DeviceDriverEnum.Value(key)}={key.replace('DEVICEDRIVER_', '')}") device_driver_ids = ', '.join(device_driver_ids) if form.validate_on_submit(): pass device: Device = Device() device.device_id.device_uuid.uuid = form.device_id.data device.device_type = form.device_type.data if '\n' not in form.device_config.data: data = form.device_config.data.strip() + '\n' else: data = form.device_config.data.strip() for config in data.split('\n'): if len(config.strip()) > 0: parts = config.strip().split('=') config_rule: ConfigRule = ConfigRule() config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.resource_key = parts[0].strip() config_rule.resource_value = parts[1].strip() device.device_config.config_rules.extend([config_rule]) device.device_operational_status = form.operational_status.data if ',' not in form.device_drivers.data: data = form.device_drivers.data.strip() + ',' else: data = form.device_drivers.data.strip() for driver in data.split(','): driver = driver.strip() if len(driver) == 0: continue device.device_drivers.extend([int(driver)]) client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) response: DeviceId = client.SetDevice(request) client.close() flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success') return render_template('device/add.html', form=form, submit_text='Add New Device') submit_text='Add New Device', device_driver_ids=device_driver_ids) @device.route('detail/<device_uuid>', methods=['GET', 'POST']) def detail(device_uuid: str): Loading @@ -32,6 +86,5 @@ def detail(device_uuid: str): request.device_uuid.uuid = device_uuid client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) response: Device = client.GetDevice(request) print(response) client.close() return render_template('device/detail.html', device=response) src/webui/service/static/site.js 0 → 100644 +4 −0 Original line number Diff line number Diff line var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl) }) src/webui/service/templates/base.html +1 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ <!-- Option 1: Bootstrap Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ" crossorigin="anonymous"></script> <!-- <script src="{{ url_for('static', filename='site.js') }}"/> --> <!-- Option 2: Separate Popper and Bootstrap JS --> <!-- Loading Loading
src/webui/proto/service_pb2.py +7 −27 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xed\x02\n\x0eServiceService\x12\x38\n\x0eGetServiceList\x12\x0e.context.Empty\x1a\x14.context.ServiceList\"\x00\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x38\n\x0eGetServiceById\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12>\n\x11GetConnectionList\x12\x0e.context.Empty\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) Loading @@ -38,22 +38,12 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=42, serialized_end=407, serialized_end=295, methods=[ _descriptor.MethodDescriptor( name='GetServiceList', full_name='service.ServiceService.GetServiceList', index=0, containing_service=None, input_type=context__pb2._EMPTY, output_type=context__pb2._SERVICELIST, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='CreateService', full_name='service.ServiceService.CreateService', index=1, index=0, containing_service=None, input_type=context__pb2._SERVICE, output_type=context__pb2._SERVICEID, Loading @@ -63,7 +53,7 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='UpdateService', full_name='service.ServiceService.UpdateService', index=2, index=1, containing_service=None, input_type=context__pb2._SERVICE, output_type=context__pb2._SERVICEID, Loading @@ -73,29 +63,19 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='DeleteService', full_name='service.ServiceService.DeleteService', index=3, index=2, containing_service=None, input_type=context__pb2._SERVICEID, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='GetServiceById', full_name='service.ServiceService.GetServiceById', index=4, containing_service=None, input_type=context__pb2._SERVICEID, output_type=context__pb2._SERVICE, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='GetConnectionList', full_name='service.ServiceService.GetConnectionList', index=5, index=3, containing_service=None, input_type=context__pb2._EMPTY, input_type=context__pb2._SERVICEID, output_type=context__pb2._CONNECTIONLIST, serialized_options=None, create_key=_descriptor._internal_create_key, Loading
src/webui/service/device/forms.py +31 −6 Original line number Diff line number Diff line # external imports from flask_wtf import FlaskForm from wtforms import StringField, SelectField, SubmitField from wtforms.validators import DataRequired, Length, NumberRange from wtforms import StringField, SelectField, TextAreaField, SubmitField from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError from webui.utils.form_validators import key_value_validator from webui.proto.context_pb2 import (DeviceDriverEnum, DeviceOperationalStatusEnum) class AddDeviceForm(FlaskForm): device_id = StringField('Device ID', device_id = StringField('ID', validators=[DataRequired(), Length(min=5)]) device_type = StringField('Device Type', device_type = StringField('Type', validators=[DataRequired(), Length(min=5)]) device_config = TextAreaField('Configurations', validators=[key_value_validator()]) operational_status = SelectField('Operational Status', choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], # choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], coerce=int, validators=[DataRequired(), NumberRange(min=0)]) validators=[NumberRange(min=0)]) device_drivers = TextAreaField('Drivers', validators=[DataRequired(), Regexp('^\d+(,\d+)*$')]) submit = SubmitField('Add') def validate_operational_status(form, field): if field.data not in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_number: raise ValidationError('The operational status value selected is incorrect!') def validate_device_drivers(form, field): if ',' not in field.data: data = str(field.data) + ',' else: data = field.data for value in data.split(','): value = value.strip() if len(value) == 0: continue try: value_int = int(value) except: raise ValidationError(f'The value "{value}" is not a valid driver identified.') if value_int not in DeviceDriverEnum.DESCRIPTOR.values_by_number: values = ', '.join([str(x) for x in DeviceDriverEnum.DESCRIPTOR.values_by_number]) raise ValidationError(f'The device driver {value_int} is not correct. Allowed values are: {values}.')
src/webui/service/device/routes.py +56 −3 Original line number Diff line number Diff line from flask import render_template, Blueprint, flash, session from context.proto.context_pb2 import ConfigActionEnum, ConfigRule, TopologyIdList, TopologyList from webui.Config import CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT from context.client.ContextClient import ContextClient from webui.proto.context_pb2 import (ContextId, DeviceList, DeviceId, Loading @@ -21,10 +22,63 @@ def home(): @device.route('add', methods=['GET', 'POST']) def add(): form = AddDeviceForm() request: ContextId = ContextId() request.context_uuid.uuid = session['context_uuid'] client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) response: TopologyIdList = client.ListTopologyIds(request) client.close() # listing enum values form.operational_status.choices = [(-1, 'Select...')] for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): form.operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) # device driver ids device_driver_ids = [] for key in DeviceDriverEnum.DESCRIPTOR.values_by_name: device_driver_ids.append(f"{DeviceDriverEnum.Value(key)}={key.replace('DEVICEDRIVER_', '')}") device_driver_ids = ', '.join(device_driver_ids) if form.validate_on_submit(): pass device: Device = Device() device.device_id.device_uuid.uuid = form.device_id.data device.device_type = form.device_type.data if '\n' not in form.device_config.data: data = form.device_config.data.strip() + '\n' else: data = form.device_config.data.strip() for config in data.split('\n'): if len(config.strip()) > 0: parts = config.strip().split('=') config_rule: ConfigRule = ConfigRule() config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.resource_key = parts[0].strip() config_rule.resource_value = parts[1].strip() device.device_config.config_rules.extend([config_rule]) device.device_operational_status = form.operational_status.data if ',' not in form.device_drivers.data: data = form.device_drivers.data.strip() + ',' else: data = form.device_drivers.data.strip() for driver in data.split(','): driver = driver.strip() if len(driver) == 0: continue device.device_drivers.extend([int(driver)]) client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) response: DeviceId = client.SetDevice(request) client.close() flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success') return render_template('device/add.html', form=form, submit_text='Add New Device') submit_text='Add New Device', device_driver_ids=device_driver_ids) @device.route('detail/<device_uuid>', methods=['GET', 'POST']) def detail(device_uuid: str): Loading @@ -32,6 +86,5 @@ def detail(device_uuid: str): request.device_uuid.uuid = device_uuid client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) response: Device = client.GetDevice(request) print(response) client.close() return render_template('device/detail.html', device=response)
src/webui/service/static/site.js 0 → 100644 +4 −0 Original line number Diff line number Diff line var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl) })
src/webui/service/templates/base.html +1 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,7 @@ <!-- Option 1: Bootstrap Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ" crossorigin="anonymous"></script> <!-- <script src="{{ url_for('static', filename='site.js') }}"/> --> <!-- Option 2: Separate Popper and Bootstrap JS --> <!-- Loading