Merge branch 'coretk' into coretk-validation
This commit is contained in:
commit
2292001e17
13 changed files with 346 additions and 235 deletions
|
@ -6,7 +6,6 @@ import logging
|
|||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from tkinter import messagebox
|
||||
|
||||
import grpc
|
||||
|
||||
|
@ -14,6 +13,7 @@ from core.api.grpc import client, core_pb2
|
|||
from coretk import appconfig
|
||||
from coretk.dialogs.mobilityplayer import MobilityPlayer
|
||||
from coretk.dialogs.sessions import SessionsDialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.graph import tags
|
||||
from coretk.graph.shape import AnnotationData, Shape
|
||||
from coretk.graph.shapeutils import ShapeType
|
||||
|
@ -196,6 +196,7 @@ class CoreClient:
|
|||
self.reset()
|
||||
|
||||
# get session data
|
||||
try:
|
||||
response = self.client.get_session(self.session_id)
|
||||
session = response.session
|
||||
self.state = session.state
|
||||
|
@ -235,7 +236,9 @@ class CoreClient:
|
|||
if _id >= 1000:
|
||||
interface = _id % 1000
|
||||
node_id = int(_id / 1000)
|
||||
self.set_emane_model_config(node_id, config.model, config.config, interface)
|
||||
self.set_emane_model_config(
|
||||
node_id, config.model, config.config, interface
|
||||
)
|
||||
|
||||
# save and retrieve data, needed for session nodes
|
||||
for node in session.nodes:
|
||||
|
@ -267,7 +270,9 @@ class CoreClient:
|
|||
|
||||
# store links as created links
|
||||
for link in session.links:
|
||||
self.created_links.add(tuple(sorted([link.node_one_id, link.node_two_id])))
|
||||
self.created_links.add(
|
||||
tuple(sorted([link.node_one_id, link.node_two_id]))
|
||||
)
|
||||
|
||||
# draw session
|
||||
self.app.canvas.reset_and_redraw(session)
|
||||
|
@ -275,6 +280,8 @@ class CoreClient:
|
|||
# get metadata
|
||||
response = self.client.get_session_metadata(self.session_id)
|
||||
self.parse_metadata(response.config)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
# update ui to represent current state
|
||||
if self.is_runtime():
|
||||
|
@ -353,6 +360,7 @@ class CoreClient:
|
|||
|
||||
:return: nothing
|
||||
"""
|
||||
try:
|
||||
response = self.client.create_session()
|
||||
logging.info("created session: %s", response)
|
||||
location_config = self.app.guiconfig["location"]
|
||||
|
@ -366,12 +374,17 @@ class CoreClient:
|
|||
scale=location_config["scale"],
|
||||
)
|
||||
self.join_session(response.session_id, query_location=False)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def delete_session(self, session_id=None):
|
||||
if session_id is None:
|
||||
session_id = self.session_id
|
||||
try:
|
||||
response = self.client.delete_session(session_id)
|
||||
logging.info("Deleted session result: %s", response)
|
||||
logging.info("deleted session result: %s", response)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def set_up(self):
|
||||
"""
|
||||
|
@ -403,21 +416,15 @@ class CoreClient:
|
|||
x.node_type: set(x.services) for x in response.defaults
|
||||
}
|
||||
except grpc.RpcError as e:
|
||||
if e.code() == grpc.StatusCode.UNAVAILABLE:
|
||||
|
||||
messagebox.showerror("Server Error", "CORE Daemon Unavailable")
|
||||
else:
|
||||
messagebox.showerror("GRPC Error", e.details())
|
||||
show_grpc_error(e)
|
||||
self.app.close()
|
||||
|
||||
def get_session_state(self):
|
||||
response = self.client.get_session(self.session_id)
|
||||
logging.info("get session: %s", response)
|
||||
return response.session.state
|
||||
|
||||
def edit_node(self, node_id, x, y):
|
||||
position = core_pb2.Position(x=x, y=y)
|
||||
try:
|
||||
self.client.edit_node(self.session_id, node_id, position, source="gui")
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def start_session(self):
|
||||
nodes = [x.core_node for x in self.canvas_nodes.values()]
|
||||
|
@ -436,6 +443,7 @@ class CoreClient:
|
|||
emane_config = None
|
||||
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
response = self.client.start_session(
|
||||
self.session_id,
|
||||
nodes,
|
||||
|
@ -451,24 +459,35 @@ class CoreClient:
|
|||
)
|
||||
self.set_metadata()
|
||||
process_time = time.perf_counter() - start
|
||||
logging.debug("start session(%s), result: %s", self.session_id, response.result)
|
||||
logging.debug(
|
||||
"start session(%s), result: %s", self.session_id, response.result
|
||||
)
|
||||
self.app.statusbar.start_session_callback(process_time)
|
||||
|
||||
# display mobility players
|
||||
for node_id, config in self.mobility_configs.items():
|
||||
canvas_node = self.canvas_nodes[node_id]
|
||||
mobility_player = MobilityPlayer(self.app, self.app, canvas_node, config)
|
||||
mobility_player = MobilityPlayer(
|
||||
self.app, self.app, canvas_node, config
|
||||
)
|
||||
mobility_player.show()
|
||||
self.mobility_players[node_id] = mobility_player
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def stop_session(self, session_id=None):
|
||||
if not session_id:
|
||||
session_id = self.session_id
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
response = self.client.stop_session(session_id)
|
||||
logging.debug(
|
||||
"stopped session(%s), result: %s", session_id, response.result
|
||||
)
|
||||
process_time = time.perf_counter() - start
|
||||
self.app.statusbar.stop_session_callback(process_time)
|
||||
logging.debug("stopped session(%s), result: %s", session_id, response.result)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def set_metadata(self):
|
||||
# create canvas data
|
||||
|
@ -492,9 +511,12 @@ class CoreClient:
|
|||
logging.info("set session metadata: %s", response)
|
||||
|
||||
def launch_terminal(self, node_id):
|
||||
try:
|
||||
response = self.client.get_node_terminal(self.session_id, node_id)
|
||||
logging.info("get terminal %s", response.terminal)
|
||||
os.system(f"xterm -e {response.terminal} &")
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def save_xml(self, file_path):
|
||||
"""
|
||||
|
@ -503,9 +525,11 @@ class CoreClient:
|
|||
:param str file_path: file path that user pick
|
||||
:return: nothing
|
||||
"""
|
||||
try:
|
||||
response = self.client.save_xml(self.session_id, file_path)
|
||||
logging.info("saved xml(%s): %s", file_path, response)
|
||||
self.client.events(self.session_id, self.handle_events)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def open_xml(self, file_path):
|
||||
"""
|
||||
|
@ -514,9 +538,12 @@ class CoreClient:
|
|||
:param str file_path: file to open
|
||||
:return: session id
|
||||
"""
|
||||
try:
|
||||
response = self.client.open_xml(file_path)
|
||||
logging.debug("open xml: %s", response)
|
||||
self.join_session(response.session_id)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def get_node_service(self, node_id, service_name):
|
||||
response = self.client.get_node_service(self.session_id, node_id, service_name)
|
||||
|
@ -553,7 +580,7 @@ class CoreClient:
|
|||
"""
|
||||
node_protos = [x.core_node for x in self.canvas_nodes.values()]
|
||||
link_protos = list(self.links.values())
|
||||
if self.get_session_state() != core_pb2.SessionState.DEFINITION:
|
||||
if self.state != core_pb2.SessionState.DEFINITION:
|
||||
self.client.set_session_state(
|
||||
self.session_id, core_pb2.SessionState.DEFINITION
|
||||
)
|
||||
|
@ -590,7 +617,7 @@ class CoreClient:
|
|||
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("Close grpc")
|
||||
logging.debug("close grpc")
|
||||
self.client.close()
|
||||
|
||||
def next_node_id(self):
|
||||
|
|
|
@ -6,7 +6,10 @@ import tkinter as tk
|
|||
import webbrowser
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.images import ImageEnum, Images
|
||||
from coretk.widgets import ConfigFrame
|
||||
|
||||
|
@ -52,9 +55,13 @@ class EmaneModelDialog(Dialog):
|
|||
self.model = f"emane_{model}"
|
||||
self.interface = interface
|
||||
self.config_frame = None
|
||||
try:
|
||||
self.config = self.app.core.get_emane_model_config(
|
||||
self.node.id, self.model, self.interface
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
self.draw()
|
||||
|
||||
def draw(self):
|
||||
|
|
|
@ -3,7 +3,10 @@ mobility configuration
|
|||
"""
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.widgets import ConfigFrame
|
||||
|
||||
PAD = 5
|
||||
|
@ -20,7 +23,11 @@ class MobilityConfigDialog(Dialog):
|
|||
self.canvas_node = canvas_node
|
||||
self.node = canvas_node.core_node
|
||||
self.config_frame = None
|
||||
try:
|
||||
self.config = self.app.core.get_mobility_config(self.node.id)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
self.draw()
|
||||
|
||||
def draw(self):
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.core_pb2 import MobilityAction
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.images import ImageEnum, Images
|
||||
|
||||
PAD = 5
|
||||
|
@ -50,6 +53,7 @@ class MobilityPlayerDialog(Dialog):
|
|||
super().__init__(
|
||||
master, app, f"{canvas_node.core_node.name} Mobility Player", modal=False
|
||||
)
|
||||
self.geometry("")
|
||||
self.canvas_node = canvas_node
|
||||
self.node = canvas_node.core_node
|
||||
self.config = config
|
||||
|
@ -123,20 +127,29 @@ class MobilityPlayerDialog(Dialog):
|
|||
def click_play(self):
|
||||
self.set_play()
|
||||
session_id = self.app.core.session_id
|
||||
try:
|
||||
self.app.core.client.mobility_action(
|
||||
session_id, self.node.id, MobilityAction.START
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def click_pause(self):
|
||||
self.set_pause()
|
||||
session_id = self.app.core.session_id
|
||||
try:
|
||||
self.app.core.client.mobility_action(
|
||||
session_id, self.node.id, MobilityAction.PAUSE
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def click_stop(self):
|
||||
self.set_stop()
|
||||
session_id = self.app.core.session_id
|
||||
try:
|
||||
self.app.core.client.mobility_action(
|
||||
session_id, self.node.id, MobilityAction.STOP
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
|
|
@ -3,8 +3,11 @@ import logging
|
|||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.images import ImageEnum, Images
|
||||
from coretk.widgets import CodeText, ListboxScroll
|
||||
|
||||
|
@ -47,9 +50,9 @@ class ServiceConfiguration(Dialog):
|
|||
self.draw()
|
||||
|
||||
def load(self):
|
||||
try:
|
||||
# create nodes and links in definition state for getting and setting service file
|
||||
self.app.core.create_nodes_and_links()
|
||||
|
||||
service_configs = self.app.core.service_configs
|
||||
if (
|
||||
self.node_id in service_configs
|
||||
|
@ -72,7 +75,9 @@ class ServiceConfiguration(Dialog):
|
|||
self.validation_mode = service_config.validation_mode
|
||||
self.validation_time = service_config.validation_timer
|
||||
self.original_service_files = {
|
||||
x: self.app.core.get_node_service_file(self.node_id, self.service_name, x)
|
||||
x: self.app.core.get_node_service_file(
|
||||
self.node_id, self.service_name, x
|
||||
)
|
||||
for x in self.filenames
|
||||
}
|
||||
self.temp_service_files = {
|
||||
|
@ -85,6 +90,8 @@ class ServiceConfiguration(Dialog):
|
|||
):
|
||||
for file, data in file_configs[self.node_id][self.service_name].items():
|
||||
self.temp_service_files[file] = data
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def draw(self):
|
||||
# self.columnconfigure(1, weight=1)
|
||||
|
@ -366,6 +373,7 @@ class ServiceConfiguration(Dialog):
|
|||
startup_commands = self.startup_commands_listbox.get(0, "end")
|
||||
shutdown_commands = self.shutdown_commands_listbox.get(0, "end")
|
||||
validate_commands = self.validate_commands_listbox.get(0, "end")
|
||||
try:
|
||||
config = self.core.set_node_service(
|
||||
self.node_id,
|
||||
self.service_name,
|
||||
|
@ -390,6 +398,8 @@ class ServiceConfiguration(Dialog):
|
|||
self.app.core.set_node_service_file(
|
||||
self.node_id, self.service_name, file, self.temp_service_files[file]
|
||||
)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
|
||||
def display_service_file_data(self, event):
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import logging
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.widgets import ConfigFrame
|
||||
|
||||
PAD_X = 2
|
||||
|
@ -12,17 +15,23 @@ class SessionOptionsDialog(Dialog):
|
|||
def __init__(self, master, app):
|
||||
super().__init__(master, app, "Session Options", modal=True)
|
||||
self.config_frame = None
|
||||
self.config = self.get_config()
|
||||
self.draw()
|
||||
|
||||
def get_config(self):
|
||||
try:
|
||||
session_id = self.app.core.session_id
|
||||
response = self.app.core.client.get_session_options(session_id)
|
||||
return response.config
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
|
||||
def draw(self):
|
||||
self.top.columnconfigure(0, weight=1)
|
||||
self.top.rowconfigure(0, weight=1)
|
||||
|
||||
session_id = self.app.core.session_id
|
||||
response = self.app.core.client.get_session_options(session_id)
|
||||
logging.info("session options: %s", response)
|
||||
|
||||
self.config_frame = ConfigFrame(self.top, self.app, config=response.config)
|
||||
self.config_frame = ConfigFrame(self.top, self.app, config=self.config)
|
||||
self.config_frame.draw_config()
|
||||
self.config_frame.grid(sticky="nsew")
|
||||
|
||||
|
@ -37,7 +46,10 @@ class SessionOptionsDialog(Dialog):
|
|||
|
||||
def save(self):
|
||||
config = self.config_frame.parse_config()
|
||||
try:
|
||||
session_id = self.app.core.session_id
|
||||
response = self.app.core.client.set_session_options(session_id, config)
|
||||
logging.info("saved session config: %s", response)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
|
|
|
@ -3,8 +3,11 @@ import threading
|
|||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.images import ImageEnum, Images
|
||||
|
||||
|
||||
|
@ -14,8 +17,18 @@ class SessionsDialog(Dialog):
|
|||
self.selected = False
|
||||
self.selected_id = None
|
||||
self.tree = None
|
||||
self.sessions = self.get_sessions()
|
||||
self.draw()
|
||||
|
||||
def get_sessions(self):
|
||||
try:
|
||||
response = self.app.core.client.get_sessions()
|
||||
logging.info("sessions: %s", response)
|
||||
return response.sessions
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
|
||||
def draw(self):
|
||||
self.top.columnconfigure(0, weight=1)
|
||||
self.draw_description()
|
||||
|
@ -48,9 +61,7 @@ class SessionsDialog(Dialog):
|
|||
self.tree.column("nodes", stretch=tk.YES)
|
||||
self.tree.heading("nodes", text="Node Count")
|
||||
|
||||
response = self.app.core.client.get_sessions()
|
||||
logging.info("sessions: %s", response)
|
||||
for index, session in enumerate(response.sessions):
|
||||
for index, session in enumerate(self.sessions):
|
||||
state_name = core_pb2.SessionState.Enum.Name(session.state)
|
||||
self.tree.insert(
|
||||
"",
|
||||
|
|
|
@ -4,7 +4,10 @@ wlan configuration
|
|||
|
||||
from tkinter import ttk
|
||||
|
||||
import grpc
|
||||
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.widgets import ConfigFrame
|
||||
|
||||
PAD = 5
|
||||
|
@ -18,7 +21,11 @@ class WlanConfigDialog(Dialog):
|
|||
self.canvas_node = canvas_node
|
||||
self.node = canvas_node.core_node
|
||||
self.config_frame = None
|
||||
try:
|
||||
self.config = self.app.core.get_wlan_config(self.node.id)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
self.destroy()
|
||||
self.draw()
|
||||
|
||||
def draw(self):
|
||||
|
|
8
coretk/coretk/errors.py
Normal file
8
coretk/coretk/errors.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from tkinter import messagebox
|
||||
|
||||
|
||||
def show_grpc_error(e):
|
||||
title = [x.capitalize() for x in e.code().name.lower().split("_")]
|
||||
title = " ".join(title)
|
||||
title = f"GRPC {title}"
|
||||
messagebox.showerror(title, e.details())
|
|
@ -2,11 +2,14 @@ import logging
|
|||
import tkinter as tk
|
||||
from tkinter import font
|
||||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc.core_pb2 import NodeType
|
||||
from coretk.dialogs.emaneconfig import EmaneConfigDialog
|
||||
from coretk.dialogs.mobilityconfig import MobilityConfigDialog
|
||||
from coretk.dialogs.nodeconfig import NodeConfigDialog
|
||||
from coretk.dialogs.wlanconfig import WlanConfigDialog
|
||||
from coretk.errors import show_grpc_error
|
||||
from coretk.graph import tags
|
||||
from coretk.graph.enums import GraphMode
|
||||
from coretk.graph.tooltip import CanvasTooltip
|
||||
|
@ -138,8 +141,11 @@ class CanvasNode:
|
|||
if self.app.core.is_runtime() and self.app.core.observer:
|
||||
self.tooltip.text.set("waiting...")
|
||||
self.tooltip.on_enter(event)
|
||||
try:
|
||||
output = self.app.core.run(self.core_node.id)
|
||||
self.tooltip.text.set(output)
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
||||
def on_leave(self, event):
|
||||
self.tooltip.on_leave(event)
|
||||
|
|
|
@ -10,7 +10,6 @@ from tkinter import filedialog, messagebox
|
|||
|
||||
import grpc
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.appconfig import XML_PATH
|
||||
from coretk.dialogs.about import AboutDialog
|
||||
from coretk.dialogs.canvasbackground import CanvasBackgroundDialog
|
||||
|
@ -52,12 +51,7 @@ class MenuAction:
|
|||
"menuaction.py: clean_nodes_links_and_set_configuration() Exiting the program"
|
||||
)
|
||||
try:
|
||||
state = self.app.core.get_session_state()
|
||||
|
||||
if (
|
||||
state == core_pb2.SessionState.SHUTDOWN
|
||||
or state == core_pb2.SessionState.DEFINITION
|
||||
):
|
||||
if not self.app.core.is_runtime():
|
||||
self.app.core.delete_session()
|
||||
if quitapp:
|
||||
self.app.quit()
|
||||
|
@ -73,7 +67,7 @@ class MenuAction:
|
|||
elif quitapp:
|
||||
self.app.quit()
|
||||
except grpc.RpcError:
|
||||
logging.error("error getting session state")
|
||||
logging.exception("error deleting session")
|
||||
if quitapp:
|
||||
self.app.quit()
|
||||
|
||||
|
|
|
@ -195,24 +195,29 @@ In order to be able to do use the Bird Internet Routing Protocol, you must modif
|
|||
### FRRouting
|
||||
FRRouting is a routing software package that provides TCP/IP based routing services with routing protocols support such as BGP, RIP, OSPF, IS-IS and more. FRR also supports special BGP Route Reflector and Route Server behavior. In addition to traditional IPv4 routing protocols, FRR also supports IPv6 routing protocols. With an SNMP daemon that supports the AgentX protocol, FRR provides routing protocol MIB read-only access (SNMP Support).
|
||||
|
||||
FRR currently supports the following protocols:
|
||||
* BGP
|
||||
FRR (as of v7.2) currently supports the following protocols:
|
||||
* BGPv4
|
||||
* OSPFv2
|
||||
* OSPFv3
|
||||
* RIPv1
|
||||
* RIPv2
|
||||
* RIPng
|
||||
* RIPv1/v2/ng
|
||||
* IS-IS
|
||||
* PIM-SM/MSDP
|
||||
* PIM-SM/MSDP/BSM(AutoRP)
|
||||
* LDP
|
||||
* BFD
|
||||
* Babel
|
||||
* PBR
|
||||
* OpenFabric
|
||||
* VRRPv2/v3
|
||||
* EIGRP (alpha)
|
||||
* NHRP (alpha)
|
||||
|
||||
#### FRRouting Package Install
|
||||
Ubuntu 19.10 and later
|
||||
```shell
|
||||
sudo apt update && sudo apt install frr
|
||||
```
|
||||
|
||||
Ubuntu 16.04 and Ubuntu 18.04
|
||||
```shell
|
||||
sudo apt install curl
|
||||
curl -s https://deb.frrouting.org/frr/keys.asc | sudo apt-key add -
|
||||
|
@ -220,6 +225,10 @@ FRRVER="frr-stable"
|
|||
echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) $FRRVER | sudo tee -a /etc/apt/sources.list.d/frr.list
|
||||
sudo apt update && sudo apt install frr frr-pythontools
|
||||
```
|
||||
Fedora 31
|
||||
```shell
|
||||
sudo dnf update && sudo dnf install frr
|
||||
```
|
||||
|
||||
#### FRRouting Source Code Install
|
||||
Building FRR from source is the best way to ensure you have the latest features and bug fixes. Details for each supported platform, including dependency package listings, permissions, and other gotchas, are in the developer’s documentation.
|
||||
|
|
|
@ -35,7 +35,7 @@ corestart() {
|
|||
echo "$NAME already started"
|
||||
else
|
||||
echo "starting $NAME"
|
||||
sudo $CMD 2>&1 >> "$LOG" &
|
||||
$CMD 2>&1 >> "$LOG" &
|
||||
fi
|
||||
|
||||
echo $! > "$PIDFILE"
|
||||
|
|
Loading…
Reference in a new issue