except KeyError: reply[path] = data['error'] request.sdata.add_to_push_queue('explorer', text=dumps(reply)) request.sdata.log('got reply {}'.format(paths)) reqs = map(request.sdata.api.get_schema, paths) d = defer.gatherResults(reqs) d.addCallback(got_data) request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1 id="explorer_title" style="display: table-cell; width: 300px">Schema Explorer</h1> <div id="explorer_show_hide" style="display: table-cell; transform: translateY(-10%);"> <button id="explorer_show_hide_button" type="button">Show</button> </div> <div id="explorer_body"> <div id="explorer_form_explanation"> When you click on a path below, it will fill in the most recently clicked on or created path box. </div> <span class="spinner" id="spinner_explorer"></span> <div id="explorer_wrapper" style="height: ; overflow: auto;"> <!-- style is to make scrollable in telemetry tab --> <pre id="explorer_display"></pre> <div id="explorer_root"></div> </div> </div> ''' page = RegisterPage(ExplorerTab, path="/explorer", tab="explorer")
d.addCallback(request_protobufs) def get_protobufs(replies): line = '-' * 77 sep = '\n//\n// ' + line + '\n//\n\n' text = sep.join([reply[1]['result'] for reply in replies]) request.sdata.set_text('#protobuf_result', text) request.sdata.add_to_push_queue('stop_current_spinner') request.sdata.highlight('#protobuf_result') d.addCallback(get_protobufs) request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1>Candidate Protocol Buffer definition</h1> <form class="ajax_form" action="protobuf" method="post"> <input type="text" name="path" size="120" placeholder="Enter path or show command here..." /> <input type="submit" value="Go!" /> </form> <span class="spinner" id="spinner_protobuf"></span> <hr /> <h3>Result: <span id="protobuf_success"></span></h3> <pre id="protobuf_result"></pre> ''' page = RegisterPage(ProtobufTab, path="/protobuf", tab="protobuf")
path = request.args['path'][0] # Path to be queried fmt = request.args['format'][0] # Requested data format d = request.sdata.api.get(path) def got_reply(reply): text = process_reply(reply, fmt == 'nest') request.sdata.add_to_push_queue('get', text=text) request.sdata.highlight('#get_result') request.sdata.log('got reply id {}'.format(reply['id'])) d.addCallback(got_reply) request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1>Get</h1> <form class="ajax_form" action="get" method="post"> <input type="text" name="path" id="get_input" size="120" placeholder="Enter path here..." /> <input type="submit" value="Go!" /> <input type="radio" name="format" value="list" checked><span>List</span> <input type="radio" name="format" value="nest"><span>Nest</span> </form> <span class="spinner" id="spinner_get"></span> <hr /> <h3>Result: <span id="get_success"></span></h3> <pre id="get_result"></pre> ''' page = RegisterPage(GetTab, path="/get", tab="get")
# ============================================================================= # render_about.py # # This file implements the `About` tab. # # December 2015 # # Copyright (c) 2015 by cisco Systems, Inc. # All rights reserved. # ============================================================================= from m2m_demo.frontend import RegisterPage, BaseResource class AboutTab(BaseResource): """ About tab """ def render_tab(self, request): self.has_debug_panel = False return help_html help_html = '''\ <h1>About this app</h1> <img src="about1.png" alt="about pic" class="about_pic"/> <img src="about2.png" alt="about pic" class="about_pic"/> <img src="about3.png" alt="about pic" class="about_pic"/> ''' page = RegisterPage(AboutTab, tab="about")
def run_script(script): try: exec script in script_env except: exception_info = sys.exc_info() buf.extend(traceback.format_exception(*exception_info)) request.sdata.log('got reply {}'.format(buf)) request.sdata.add_to_push_queue('script', text=dumps(buf)) script = request.args['script'][0] reactor.callInThread(run_script, script) html = '''\ <h1>Run script</h1> <form id="script_form" class="ajax_form" action="script" method="post"> <textarea form="script_form" name="script" id="script_box" rows="10" cols="120" placeholder="Enter script here..."></textarea> <input type="submit" value="Go!" /> </form> <div class="script_loader"> Examples <ul>{}</ul> </div> <span class="spinner" id="spinner_script"></span> <hr /> <h3>Result: <span id="script_success"></span></h3> <pre id="script_result"></pre> ''' page = RegisterPage(ScriptTab, path="/script", tab="script")
def get_policy_contents(cfg, filename): return policies[filename] def maybe_load_policies(cfg, policies_dict): # Update policies_dict with files found in /src/assets/policies policy_dir = os.path.join(cfg['assets'], 'policies') for root, dirs, files in os.walk(policy_dir): for filename in files: contents = open(os.path.join(root, filename)).read() policies_dict[filename] = contents return policies_dict class PoliciesPage(BaseResource): """ Serve up policies as a json object """ def render_GET(self, request): request.setHeader('Content-Type', 'application/json') temp_pols = maybe_load_policies(self.cfg, policies) global policies policies = temp_pols return json.dumps(policies) page = RegisterPage(PoliciesPage, '/policies')
<span class="label">Interface:</span> <input type="text" name="interface" size="42" value="GigabitEthernet0/0/0/5" /> </div> <div> <span class="label">Description:</span> <input type="text" name="description" size="42" value="to PHX" /> </div> <div> <span class="label">IPv4 address:</span> <input type="text" name="ipv4_addr" size="15" value="10.5.128.6" /> mask: <input type="text" name="ipv4_mask" size="15" value="255.255.255.252" /> </div> <textarea name="extra_cli" rows="4" cols="43" placeholder="Additional config in CLI form"></textarea> <div class="buttons"> <input type="submit" id="update_json_input" name="action" value="Update JSON"> <input type="submit" name="action" value="Show changes"> <input type="submit" name="action" value="Commit"> <input type="submit" name="action" value="Replace subtree"> <input type="submit" name="action" value="Replace all"> </div> </form> <span class="spinner" id="spinner_manage_intf"></span> <hr /> <h3>Result:</h3> <div id="manage_intf_success"></div> ''' page = RegisterPage(ManageIntfTab, path="/manage_intf", tab="manage_intf")
FEATURES['features'].add_blurb('Enable/disable the component features of the app.') FEATURES['simple_cli'].add_blurb('Execute CLI show commands (equivalent to CLI on the router).') FEATURES['json_cli'].add_blurb('Execute CLI show commands, but get structured data in response.') FEATURES['get'].add_blurb('Get manageability data for a given schema path.') FEATURES['schema'].add_blurb('Get schema information for a given schema path.') FEATURES['script'].add_blurb('Write and execute Python scripts using the M2M API.') FEATURES['explorer'].add_blurb('Explore the schema.') FEATURES['write_file'].add_blurb('Write a file to disk on the connected IOS-XR device.') FEATURES['telemetry'].add_blurb('Generate policy files for use with Telemetry.<br>Note that this feature is relevant to Telemetry in IOX-XR 6.0.0 only, the process has changed in 6.1.0') FEATURES['gpb'].add_blurb('Construct a Google Protocol Buffer from a candidate file.<br> Note that this feature is relevant to Telemetry in IOX-XR 6.0.0 only, the process has changed in 6.1.0') FEATURES['history'].add_blurb('Examine the commit history.') FEATURES['current_config'].add_blurb('Calculate the mapping between the current config and equivalent schema paths and values.') FEATURES['manage_intf'].add_blurb('Manage an interface.') FEATURES['protobuf'].add_blurb('Generate a candidate Google Protocol Buffer from a schema path or show command.') FEATURES['about'].add_blurb('Information about M2M.') html = '''\ <h1>Features</h1> ''' + '\n'.join([ '<input type="checkbox" value="{0}"{1}{2}>{3}{4}'.format( name, ' checked' if feature.enabled else '', ' disabled' if feature.immutable else '', feature.name, feature.blurb) for name, feature in FEATURES.iteritems() ]) page = RegisterPage(FeaturesTab, path="/features", tab="features")
""" def render_tab(self, request): return html def render_POST(self, request): cmd = request.args['cmd'][0] d = request.sdata.api.cli_exec(cmd) def got_reply(reply): request.sdata.add_to_push_queue('simple_cli', text=dumps(reply)) request.sdata.log('got reply id {}'.format(reply['id'])) d.addCallback(got_reply) request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1>Simple CLI</h1> <form class="ajax_form" action="simple_cli" method="post"> <input type="text" name="cmd" id="simple_cli_input" size="120" placeholder="Enter command here..." /> <input type="submit" value="Go!" /> </form> <span class="spinner" id="spinner_simple_cli"></span> <hr /> <h3>Result: <span id="simple_cli_success"></span></h3> <pre id="simple_cli_result"></pre> ''' page = RegisterPage(SimpleCliTab, path="/simple_cli", tab="simple_cli")
Connection status: <span id="connection_status"></span> <img id="connection_loader" src="loader-red-circle.gif" alt="loading" /> <span id="connection_failure_reason"></span> <button id="disconnect">Disconnect</button> </div> <form name="start_connection" id="start_connection" action="connection" method="post"> <fieldset id="connection_fieldset"> XR router address: <input type="text" name="host" value="{host}" size="25"> <input type="submit" value="Connect"> <button id="toggle_creds">Credentials</button> <div id="creds"> <span class="label">Username:</span> <input type="text" name="username" value="{username}" size="30"> <span class="label">Secret:</span> <input type="text" name="secret" value="{secret}" size="30"> <span class="label">Type:</span> <input type="radio" name="secret_type" id="radio1" value="key" {secret_key}> <label for="radio1">SSH key</label> <input type="radio" name="secret_type" id="radio2" value="password" {secret_password}> <label for="radio2">Password</label> <span class="label">Port:</span> <input type="text" name="port" value="{port}" size="10"> </div></fieldset></form> <form class="ajax_form" action="yang_mode" id="yang_mode" method="post"> <input type="submit" value="Yang mode"> </form> ''' page = RegisterPage(ConnectionTab, path="/connection", tab="connection")
row_style = "" row = '<tr {}><td><button id="toggle_{}">X</button></td>'.format(row_style, commit_id) for h in main_headings: row += '<td>{}</td>'.format(entry[h]) table += row + '</tr>\n' row = '<tr class="subrow" id="subrow_{}"><td colspan=4></a>'.format(commit_id) for h in sub_headings: if entry[h] != "": row += '<span><b>{}:</b> {}</span>'.format(h, entry[h]) table += row + '<pre id="cfg_diff_{}"></pre></td></tr>\n'.format(commit_id) commit_ids.append(entry['Commit ID']) html = thead + table + '</div>' request.sdata.add_to_push_queue('set_html', selector='#cfg_hist_output', html=html) request.sdata.add_to_push_queue('commit_history_updates') for commit_id in commit_ids: self.render_single_diff(request, commit_id) d = request.sdata.api.cli_exec('show configuration history commit reverse detail') d.addCallback(done) return '{}' tab_html = '''\ <h1>Configuration commit history</h1> <form class="ajax_form" action="history" method="post"> <input type="submit" value="Update" /></form> <div id="cfg_hist_output"></div> ''' page = RegisterPage(ConfigHistoryPage, path='/history', tab='history')
d.addCallback(got_reply) request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1>Telemetry GPB</h1> <form class="ajax_form" id="gpb_form" action="gpb" method="post"> <span class="label">Path:</span> <input type="text" id="gpb_path" name="gpb_path" size="50"/> <input type="button" id="gpb_path_button" value="Get Candidate File"/> <span class="spinner" id="spinner_gpb"></span> </form> <br><br> <div id="proto_editor_wrapper"> <hr> <h2>Edit Proto File</h2> <div id="proto_editor"></div> </div> <div id="gpb_result_wrapper"> <hr /> <h2>Generated file <span id="gpb_success"></span></h2> <pre id="gpb_result"></pre> </div> ''' page = RegisterPage(GPBTab, path="/gpb", tab="gpb")
cmd = request.args['cmd'][0] fmt = request.args['format'][0] # Requested data format d = request.sdata.api.cli_get(cmd) def got_reply(reply): text = process_reply(reply, fmt == 'nest') request.sdata.add_to_push_queue('json_cli', text=text) request.sdata.highlight('#json_cli_result') request.sdata.log('got reply id {}'.format(reply['id'])) d.addCallback(got_reply) request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1>JSON CLI</h1> <form class="ajax_form" action="json_cli" method="post"> <input name="cmd" id="json_cli_input" type="text" size="120" placeholder="Enter command here..." /> <input type="submit" value="Go!" /> <input type="radio" name="format" value="list" checked><span>List</span> <input type="radio" name="format" value="nest"><span>Nest</span> </form> <span class="spinner" id="spinner_json_cli"></span> <hr /> <h3>Result: <span id="json_cli_success"></span></h3> <pre id="json_cli_result"></pre> ''' page = RegisterPage(JsonCliTab, path="/json_cli", tab="json_cli")
if len(info_paths): text = "Requires parent: " + '\n'.join(info_paths) info_td = '''<img class="info" src="info.png" title='{}' alt="" />'''.format(text) else: info_td = '' output.append('<tr><td class="cli">{}</td>' \ '<td class="path">{}</td>' '<td class="path">{}</td></tr>'. \ format(cli_td, info_td, json_td)) output.append('</table>') return '\n'.join(output) html = '''\ <h1>Current configuration</h1> <div class="toggle_cli_or_json"> Show/hide: <br><input type="checkbox" name="toggle_cli" checked>CLI <br><input type="checkbox" name="toggle_path" checked>JSON paths <br><input type="checkbox" name="toggle_value" checked>JSON values </div> <form class="ajax_form" action="current_config" method="post"> <input type="submit" value="Update" /> <span class="spinner" id="spinner_current_config"></span> <span id="current_config_progress"></span> </form> <div id="current_config_result"></div> ''' page = RegisterPage(CurrentConfigTab, path="/current_config", tab="current_config")
from twisted.web import server from m2m_demo.frontend import RegisterPage, BaseResource class PushQueuePage(BaseResource): ''' Serve up the push queue ''' def render_POST(self, request): request.setHeader('Content-Type', 'application/json') pq = request.sdata.drain_push_queue() if len(pq) > 0: return json.dumps(pq) else: def finish_later(pq): try: request.write(json.dumps(pq)) request.finish() except exceptions.RuntimeError as e: print("### can't send push queue: ", e) request.sdata.restore_push_queue(pq) request.sdata.add_pending_push_queue_dispatch(finish_later) request.notifyFinish().addErrback( lambda _: request.sdata.remove_pending_push_queue_dispatch()) return server.NOT_DONE_YET page = RegisterPage(PushQueuePage, '/push_queue')
# ============================================================================= # render_yang_mode.py # # This file implements the Yang mode callback. # # December 2015 # # Copyright (c) 2015 by cisco Systems, Inc. # All rights reserved. # ============================================================================= from m2m_demo.frontend import RegisterPage, BaseResource class YangModeCallback(BaseResource): def render_POST(self, request): print('### starting yfw mode') request.sdata.api.yfw() request.setHeader('Content-Type', 'application/json') return '{}' page = RegisterPage(YangModeCallback, path="/yang_mode")
maybe_load_scriptlets(cfg) return scriptlets.keys() def maybe_load_scriptlets(cfg): if scriptlets is not None: return global scriptlets scriptlets = OrderedDict() scriptlet_dir = os.path.join(cfg['assets'], 'scriptlets') for root, dirs, files in os.walk(scriptlet_dir): for filename in files: contents = open(os.path.join(root, filename)).read() m = re.search('_(.*)\.py', filename) if m: filename = m.group(1) scriptlets[filename] = contents class ScriptletsPage(BaseResource): """ Serve up scriptlets as a json object """ def render_GET(self, request): request.setHeader('Content-Type', 'application/json') maybe_load_scriptlets(self.cfg) return json.dumps(scriptlets) page = RegisterPage(ScriptletsPage, '/scriptlets')
</div> <div> <textarea name="policy" id="telemetry_policy_contents" rows="6" onchange='M2M.fill_builder()' placeholder="Enter policy file here..."></textarea> <br> <span class="label">Add config?</span> <input type="checkbox" name="do_config" id="do_config" checked> <span class="label">Destination:</span> <input type="text" name="destination_ip" id="destination_ip" size="15"> <span class="label">Port:</span> <input type="text" name="destination_port" id="destination_port" size="5"> <input type="submit" value="Write to router" style="float: right;" /> </div> </form> <div class="policy_loader"> <h4>Load Example Policy</h4> <div class="preloaded_policies" name="preloaded_policies"> <ul>{}</ul> </div> <h4>Upload Policy From File System</h4> <div> <input type='file' id='telemetry_policy_upload' onchange='M2M.get_contents_from_uploaded_policy()'> </div> </div> <span class="spinner" id="spinner_telemetry"></span> <hr /> <h3>Result: <span id="telemetry_success"></span></h3> <pre id="telemetry_result"></pre> ''' page = RegisterPage(TelemetryTab, path="/telemetry", tab="telemetry")
if 'message' in keys and \ 'get_keys is not supported for leaf nodes' in keys['message']: keys = 'No keys (leaf node)' return (schema, keys) d.addCallback(canonicalize) d.addCallback(send_response) # For now, just send back an empty response. request.setHeader('Content-Type', 'application/json') return '{}' html = '''\ <h1>Schema</h1> <form class="ajax_form" action="schema" method="post"> <input type="text" name="path" id="schema_input" size="120" placeholder="Enter path here..." /> <input type="submit" value="Go!" /> </form> <span class="spinner" id="spinner_schema"></span> <hr /> <div> <h3>Schema:</h3> <pre id="schema_result_schema"></pre> <h3>Keys:</h3> <pre id="schema_result_keys"></pre> </div> ''' page = RegisterPage(SchemaTab, path="/schema", tab="schema")
class HomePage(BaseResource): """ Home page. This is bolted together from a big static head fetched from a file, dynamic contributions from each plugin with a tab, and a static tail. """ def __init__(self, cfg): BaseResource.__init__(self, cfg) index_html_path = os.path.join(self.cfg['assets'], 'fragments/index.html') with open(index_html_path) as f: self.index_html_base = f.read() def render_GET(self, request): parts = [self.index_html_base] for tab, resource in self.cfg['tab_map'].items(): parts.append('<div class="tab" id="{}">'.format(tab)) tab_plugin = resource(self.cfg) parts.append(tab_plugin.render_tab(request)) if tab_plugin.has_debug_panel: parts.append( '<div class="debug" id="{}_debug"><table></table></div>'. format(tab)) parts.append('</div>') parts.append('</div></div></body></html>') return '\n'.join(parts) page = RegisterPage(HomePage, '/')