From 1eb69ec7bf6b251e2ec72510da0e20d4df862fc8 Mon Sep 17 00:00:00 2001 From: Abdel-Kader Chabi-Sika-Boni Date: Fri, 11 Dec 2020 09:22:37 +0100 Subject: [PATCH] ajout gctrl --- gctrl/README.md | 2 + gctrl/pom.xml | 49 +++ gctrl/src/main/java/Analyze.java | 78 +++++ gctrl/src/main/java/Execute.java | 102 +++++++ gctrl/src/main/java/Knowledge.java | 404 +++++++++++++++++++++++++ gctrl/src/main/java/MANOAPI.java | 36 +++ gctrl/src/main/java/Main.java | 92 ++++++ gctrl/src/main/java/Monitor.java | 159 ++++++++++ gctrl/src/main/java/Plan.java | 87 ++++++ gctrl/src/main/java/SDNCtrlAPI.java | 34 +++ gctrl/src/misc/5sdbd_gc.draft.svg | 1 + gctrl/src/test/java/Tester.java | 35 +++ gctrl/target/classes/Analyze.class | Bin 0 -> 2724 bytes gctrl/target/classes/Execute.class | Bin 0 -> 4652 bytes gctrl/target/classes/Knowledge.class | Bin 0 -> 12476 bytes gctrl/target/classes/Main.class | Bin 0 -> 3857 bytes gctrl/target/classes/Monitor.class | Bin 0 -> 7114 bytes gctrl/target/classes/Plan.class | Bin 0 -> 2837 bytes gctrl/target/test-classes/Tester.class | Bin 0 -> 2189 bytes 19 files changed, 1079 insertions(+) create mode 100644 gctrl/README.md create mode 100644 gctrl/pom.xml create mode 100644 gctrl/src/main/java/Analyze.java create mode 100644 gctrl/src/main/java/Execute.java create mode 100644 gctrl/src/main/java/Knowledge.java create mode 100644 gctrl/src/main/java/MANOAPI.java create mode 100644 gctrl/src/main/java/Main.java create mode 100644 gctrl/src/main/java/Monitor.java create mode 100644 gctrl/src/main/java/Plan.java create mode 100644 gctrl/src/main/java/SDNCtrlAPI.java create mode 100644 gctrl/src/misc/5sdbd_gc.draft.svg create mode 100644 gctrl/src/test/java/Tester.java create mode 100644 gctrl/target/classes/Analyze.class create mode 100644 gctrl/target/classes/Execute.class create mode 100644 gctrl/target/classes/Knowledge.class create mode 100644 gctrl/target/classes/Main.class create mode 100644 gctrl/target/classes/Monitor.class create mode 100644 gctrl/target/classes/Plan.class create mode 100644 gctrl/target/test-classes/Tester.class diff --git a/gctrl/README.md b/gctrl/README.md new file mode 100644 index 0000000..9b6634f --- /dev/null +++ b/gctrl/README.md @@ -0,0 +1,2 @@ +# General Controller +![Software architecture of the project](src/misc/5sdbd_gc.draft.svg) diff --git a/gctrl/pom.xml b/gctrl/pom.xml new file mode 100644 index 0000000..b0efab4 --- /dev/null +++ b/gctrl/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + fr.laas.sara.sdci + fr.laas.sara.sdci.gctrl + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + + + + + + com.h2database + h2 + 1.4.200 + + + de.vandermeer + asciitable + 0.3.2 + + + com.github.signaflo + timeseries + 0.4 + + + org.slf4j + slf4j-api + 1.7.5 + + + org.slf4j + slf4j-log4j12 + 1.7.5 + + + \ No newline at end of file diff --git a/gctrl/src/main/java/Analyze.java b/gctrl/src/main/java/Analyze.java new file mode 100644 index 0000000..62bb9d7 --- /dev/null +++ b/gctrl/src/main/java/Analyze.java @@ -0,0 +1,78 @@ +import java.util.List; + +// + +//* @author couedrao on 25/11/2019. + +//* @project gctrl + +// + +// +//* 1)Perform complex data analysis and reasoning on the symptoms provided by the monitor function. +//* 2)Influenced by stored knowledge data. +//* 3)If changes are required, a change request is logically passed to the plan function. +//* +@SuppressWarnings({"SameParameterValue", "SynchronizeOnNonFinalField"}) +class Analyze { + public String gw_current_RFC = ""; + private static int i; + + void start() { + Main.logger(this.getClass().getSimpleName(), "Start Analyzing"); + + while (Main.run) { + + String current_symptom = get_symptom(); + //Main.logger(this.getClass().getSimpleName(), "Received Symptom : " + current_symptom); + + update_rfc(rfc_generator(current_symptom)); + } + } + + //Symptom Receiver + private String get_symptom() { + synchronized (Main.monitor.gw_current_SYMP) { + try { + Main.monitor.gw_current_SYMP.wait(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + return Main.monitor.gw_current_SYMP; + } + + //Rule-based RFC Generator + private String rfc_generator(String symptom) { + List symptoms = Main.shared_knowledge.get_symptoms(); + List rfcs = Main.shared_knowledge.get_rfc(); + + if (symptom.contentEquals(symptoms.get(0)) || symptom.contentEquals(symptoms.get(2))) { + Main.logger(this.getClass().getSimpleName(), "RFC --> To plan : " + rfcs.get(0)); + i = 0; + return rfcs.get(0); + } else if (symptom.contentEquals(symptoms.get(1))) { + i++; + if (i < 3) { + Main.logger(this.getClass().getSimpleName(), "RFC --> To plan : " + rfcs.get(1)); + return rfcs.get(1); + } else { + Main.logger(this.getClass().getSimpleName(), "RFC --> To plan : " + "YourPlansDoNotWork"); + return "YourPlansDoNotWork"; + } + } else + return null; + + } + + + private void update_rfc(String rfc) { + + synchronized (gw_current_RFC) { + gw_current_RFC.notify(); + gw_current_RFC = rfc; + + } + } + +} diff --git a/gctrl/src/main/java/Execute.java b/gctrl/src/main/java/Execute.java new file mode 100644 index 0000000..8c9c52d --- /dev/null +++ b/gctrl/src/main/java/Execute.java @@ -0,0 +1,102 @@ +import java.util.List; + +// + +//* @author couedrao on 25/11/2019. + +//* @project gctrl + +// + +// +//* Changes the behavior of the managed resource using effectors Changes the behavior of the managed resource using effectors, based on the actions recommended by the plan function. +//* +@SuppressWarnings({"SameParameterValue", "SynchronizeOnNonFinalField"}) +class Execute { + private static List workflow_lists; + private static final MANOAPI manoapi = new MANOAPI(); + private static final SDNCtrlAPI sdnctlrapi = new SDNCtrlAPI(); + + void start() throws InterruptedException { + Main.logger(this.getClass().getSimpleName(), "Start Execution"); + workflow_lists = Main.shared_knowledge.get_worklow_lists(); + + while (Main.run) { + String current_plan = get_plan(); + + // Main.logger(this.getClass().getSimpleName(), "Received Plan : " + current_plan); + String[] workflow = workflow_generator(current_plan); + for (int i = 0; i < workflow.length; i++) { + Main.logger(this.getClass().getSimpleName(), "workflow [" + i + "] : " + workflow[i]); + + } + + for (String w : workflow) { + Main.logger(this.getClass().getSimpleName(), "UC : " + w); + switch (w) { + case "UC1": + Main.logger(this.getClass().getSimpleName(), "Nothing to do"); + break; + case "UC2": + Main.logger(this.getClass().getSimpleName(), "Deploying GW"); + String newdestip = manoapi.deploy_gw(Main.shared_knowledge.getGwinfo()); + Main.shared_knowledge.setNewdestip(newdestip); + Main.shared_knowledge.setOldgwip(newdestip); + break; + case "UC3": + Main.logger(this.getClass().getSimpleName(), "Redirecting Traffic"); + String status = sdnctlrapi.redirect_traffic(Main.shared_knowledge.getOlddestip(), Main.shared_knowledge.getNewdestip()); + Main.logger(this.getClass().getSimpleName(), status); + break; + case "UC4": + Main.logger(this.getClass().getSimpleName(), "Deploying LB+GWs"); + List newgwsip = manoapi.deploy_multi_gws_and_lb(Main.shared_knowledge.getGwsinfo()); + Main.shared_knowledge.setLbip(newgwsip.get(0)); + Main.shared_knowledge.setNewgwsip(newgwsip.subList(1, newgwsip.size())); + break; + case "UC5": + Main.logger(this.getClass().getSimpleName(), "Inserting a loadbalancer"); + status = sdnctlrapi.insert_a_loadbalancer(Main.shared_knowledge.getOldgwip(), Main.shared_knowledge.getLbip(), Main.shared_knowledge.getNewgwsip()); + Main.logger(this.getClass().getSimpleName(), status); + break; + case "UC6": + Main.logger(this.getClass().getSimpleName(), "Removing less important traffic"); + status = sdnctlrapi.remove_less_important_traffic(Main.shared_knowledge.getImportantsrcip()); + Main.logger(this.getClass().getSimpleName(), status); + break; + default: + } + Thread.sleep(2000); + continue; + + + } + + } + } + + //Plan Receiver + private String get_plan() { + synchronized (Main.plan.gw_PLAN) { + try { + Main.plan.gw_PLAN.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return Main.plan.gw_PLAN; + } + + //Rule-based Workflow Generator + private String[] workflow_generator(String plan) { + List plans = Main.shared_knowledge.get_plans(); + if (plan.contentEquals(plans.get(0))) { + return workflow_lists.get(0).split("/"); + } else if (plan.contentEquals(plans.get(1))) { + return workflow_lists.get(1).split("/"); + } else if (plan.contentEquals(plans.get(2))) { + return workflow_lists.get(2).split("/"); + } else + return null; + } +} diff --git a/gctrl/src/main/java/Knowledge.java b/gctrl/src/main/java/Knowledge.java new file mode 100644 index 0000000..1145379 --- /dev/null +++ b/gctrl/src/main/java/Knowledge.java @@ -0,0 +1,404 @@ +import org.h2.tools.DeleteDbFiles; + +import java.sql.*; +import java.util.*; + +// + +//* @author couedrao on 25/11/2019. + +//* @project gctrl + +// + +// + +//* 1)Standard data shared among the monitor analyze plan and Standard data shared among the monitor, analyze, plan and execute functions + +//* 2)The shared knowledge includes data such as topology information, historical logs, metrics, symptoms and policies + +//* 3)Created by the monitor part while execute part might update the knowledge + +//* + +class Knowledge { + + private static final String DB_DRIVER = "org.h2.Driver"; + private static final String DB_CONNECTION = "jdbc:h2:~/test"; + private static final String DB_USER = ""; + private static final String DB_PASSWORD = ""; + + static final int moving_wind = 10; + static final int horizon = 3; + static final String gw = "GW_I"; + static final double gw_lat_threshold = 20; + + /*TODO : edit symptom, rfc, workflow_lists, plan*/ + private static final List symptom = Arrays.asList("N/A", "NOK", "OK"); + private static final List rfc = Arrays.asList("DoNotDoAnything", "DecreaseLatencyIn" + gw); + private static final List workflow_lists = Arrays.asList("UC1", "UC2/UC3", "UC4/UC5/UC6"); + private static final List plan = Arrays.asList("A", "B", "C"); + private final Map gwinfo = new HashMap<>(); + private final List> gwsinfo = new ArrayList<>(); + private final String olddestip = "192.168.0.2"; + private String newdestip; + private String oldgwip; + private String lbip; + private List newgwsip; + private final String importantsrcip = "192.168.0.1"; + + void start() throws Exception { + // delete the H2 database named 'test' in the user home directory + DeleteDbFiles.execute("~", "test", true); + Main.logger(this.getClass().getSimpleName(), "old database 'test' deleted"); + //Initialization of the Knowledge + store_symptoms(); + store_rfcs(); + store_plans(); + store_execution_workflow(); + //TODO : update gwinfo + gwinfo.put("name", "gw"); + gwinfo.put("image", "alpine:latest"); + gwinfo.put("net", "new_network"); + + gwsinfo.add(0, gwinfo); + gwsinfo.add(1, gwinfo); + gwsinfo.add(2, gwinfo); + + Main.logger(this.getClass().getSimpleName(), "Knowledge Starting"); + + } + + void insert_in_tab(Timestamp timestamp, double lat) { + try (Connection conn = getDBConnection()) { + PreparedStatement insert; + String InsertQuery = "INSERT INTO " + Knowledge.gw + "_LAT" + " (id, latency) values" + "(?,?)"; + conn.setAutoCommit(false); + insert = conn.prepareStatement(InsertQuery); + insert.setTimestamp(1, timestamp); + insert.setDouble(2, lat); + insert.executeUpdate(); + insert.close(); + conn.commit(); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + List get_symptoms() { + String gw_symp = gw + "_SYMP"; + + Connection conn = getDBConnection(); + String SelectQuery = "select * from " + gw_symp; + PreparedStatement select; + List r = null; + try { + select = conn.prepareStatement(SelectQuery); + ResultSet rs = select.executeQuery(); + r = new ArrayList<>(); + while (rs.next()) { + r.add(rs.getString("symptom")); + } + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + return r; + + } + + List get_rfc() { + String gw_rfc = gw + "_RFC"; + + Connection conn = getDBConnection(); + String SelectQuery = "select * from " + gw_rfc; + PreparedStatement select; + List r = null; + try { + select = conn.prepareStatement(SelectQuery); + ResultSet rs = select.executeQuery(); + r = new ArrayList<>(); + while (rs.next()) { + r.add(rs.getString("rfc")); + } + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + + return r; + + } + + List get_plans() { + String gw_plan = gw + "_PLAN"; + + Connection conn = getDBConnection(); + String SelectQuery = "select * from " + gw_plan; + PreparedStatement select; + List r = null; + try { + select = conn.prepareStatement(SelectQuery); + ResultSet rs = select.executeQuery(); + r = new ArrayList<>(); + while (rs.next()) { + r.add(rs.getString("plan")); + } + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + + return r; + + } + + List get_worklow_lists() { + String gw_execw = gw + "_EXECW"; + + Connection conn = getDBConnection(); + String SelectQuery = "select * from " + gw_execw; + PreparedStatement select; + List r = null; + try { + select = conn.prepareStatement(SelectQuery); + ResultSet rs = select.executeQuery(); + r = new ArrayList<>(); + while (rs.next()) { + r.add(rs.getString("workflow")); + } + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + + return r; + + } + + ResultSet select_from_tab() { + //Main.logger("Select the last " + n + " latencies"); + Connection conn = getDBConnection(); + String SelectQuery = "select TOP " + moving_wind + " * from " + Knowledge.gw + "_LAT" + " ORDER BY id DESC"; + //PreparedStatement select; + ResultSet rs = null; + try { + Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + // select = conn.prepareStatement(SelectQuery); + rs = stmt.executeQuery(SelectQuery); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + return rs; + + + } + + void create_lat_tab() { + try (Connection conn = getDBConnection()) { + Statement create; + conn.setAutoCommit(false); + create = conn.createStatement(); + create.execute("CREATE TABLE " + Knowledge.gw + "_LAT" + " (id timestamp primary key, latency double )"); + create.close(); + conn.commit(); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + Main.logger(this.getClass().getSimpleName(), "... Database Created"); + + } + } + + private void store_plans() throws SQLException { + String gw_plan = gw + "_PLAN"; + Connection conn = getDBConnection(); + Statement create; + conn.setAutoCommit(false); + create = conn.createStatement(); + create.execute("CREATE TABLE " + gw_plan + " (id int primary key, plan varchar(20) )"); + create.close(); + + for (int i = 0; i < plan.size(); i++) { + conn = getDBConnection(); + PreparedStatement insert; + try { + insert = conn.prepareStatement("INSERT INTO " + gw_plan + " (id, plan) values" + "(?,?)"); + insert.setInt(1, i + 1); + insert.setString(2, plan.get(i)); + insert.executeUpdate(); + insert.close(); + conn.commit(); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + conn.close(); + } + } + } + + private void store_rfcs() throws SQLException { + String gw_rfc = gw + "_RFC"; + Connection conn = getDBConnection(); + Statement create; + conn.setAutoCommit(false); + create = conn.createStatement(); + create.execute("CREATE TABLE " + gw_rfc + " (id int primary key, rfc varchar(40) )"); + create.close(); + + for (int i = 0; i < rfc.size(); i++) { + conn = getDBConnection(); + PreparedStatement insert; + try { + insert = conn.prepareStatement("INSERT INTO " + gw_rfc + " (id, rfc) values" + "(?,?)"); + insert.setInt(1, i + 1); + insert.setString(2, rfc.get(i)); + insert.executeUpdate(); + insert.close(); + conn.commit(); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + conn.close(); + } + } + } + + private void store_execution_workflow() throws SQLException { + String gw_execw = gw + "_EXECW"; + Connection conn = getDBConnection(); + Statement create; + conn.setAutoCommit(false); + create = conn.createStatement(); + create.execute("CREATE TABLE " + gw_execw + " (id int primary key, workflow varchar(50) )"); + create.close(); + + for (int i = 0; i < workflow_lists.size(); i++) { + conn = getDBConnection(); + PreparedStatement insert; + try { + insert = conn.prepareStatement("INSERT INTO " + gw_execw + " (id, workflow) values" + "(?,?)"); + insert.setInt(1, i + 1); + insert.setString(2, workflow_lists.get(i)); + insert.executeUpdate(); + insert.close(); + conn.commit(); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + conn.close(); + } + } + } + + private void store_symptoms() throws SQLException { + String gw_symp = gw + "_SYMP"; + Connection conn = getDBConnection(); + Statement create; + conn.setAutoCommit(false); + create = conn.createStatement(); + create.execute("CREATE TABLE " + gw_symp + " (id int primary key, symptom varchar(5) )"); + create.close(); + + for (int i = 0; i < symptom.size(); i++) { + conn = getDBConnection(); + PreparedStatement insert; + + try { + insert = conn.prepareStatement("INSERT INTO " + gw_symp + " (id, symptom) values" + "(?,?)"); + insert.setInt(1, i + 1); + insert.setString(2, symptom.get(i)); + insert.executeUpdate(); + insert.close(); + conn.commit(); + } catch (SQLException e) { + System.out.println("Exception Message " + e.getLocalizedMessage()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + conn.close(); + } + } + } + + private Connection getDBConnection() { + // Main.logger("Connecting the database ..."); + try { + Class.forName(DB_DRIVER); + } catch (ClassNotFoundException e) { + System.out.println(e.getMessage()); + } + try { + return DriverManager.getConnection(DB_CONNECTION, DB_USER, DB_PASSWORD); + } catch (SQLException e) { + System.out.println(e.getMessage()); + return null; + } + + } + + public Map getGwinfo() { + return gwinfo; + } + + public List> getGwsinfo() { + return gwsinfo; + } + + public String getOlddestip() { + return olddestip; + } + + public String getNewdestip() { + return newdestip; + } + + public void setNewdestip(String newdestip) { + this.newdestip = newdestip; + } + + public String getOldgwip() { + return oldgwip; + } + + public void setOldgwip(String oldgwip) { + this.oldgwip = oldgwip; + } + + public String getLbip() { + return lbip; + } + + public void setLbip(String lbip) { + this.lbip = lbip; + } + + public List getNewgwsip() { + return newgwsip; + } + + public void setNewgwsip(List newgwsip) { + this.newgwsip = newgwsip; + } + + public String getImportantsrcip() { + return importantsrcip; + } + +} \ No newline at end of file diff --git a/gctrl/src/main/java/MANOAPI.java b/gctrl/src/main/java/MANOAPI.java new file mode 100644 index 0000000..c41999f --- /dev/null +++ b/gctrl/src/main/java/MANOAPI.java @@ -0,0 +1,36 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; + +/** + * @author couedrao on 27/11/2019. + * @project gctrl + */ +class MANOAPI { + + String deploy_gw(Map vnfinfos) { + String ip = "192.168.0." + (new Random().nextInt(253) + 1); + Main.logger(this.getClass().getSimpleName(), "Deploying VNF ..."); + + //printing + for (Entry e : vnfinfos.entrySet()) { + Main.logger(this.getClass().getSimpleName(), "\t" + e.getKey() + " : " + e.getValue()); + } + //TODO + + return ip; + } + + List deploy_multi_gws_and_lb(List> vnfsinfos) { + List ips = new ArrayList<>(); + //TODO + + for (Map vnfsinfo : vnfsinfos) { + ips.add(deploy_gw(vnfsinfo)); + } + + return ips; + } +} diff --git a/gctrl/src/main/java/Main.java b/gctrl/src/main/java/Main.java new file mode 100644 index 0000000..2eb6eb9 --- /dev/null +++ b/gctrl/src/main/java/Main.java @@ -0,0 +1,92 @@ +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +// + +//* @author couedrao on 25/11/2019. + +//* @project gctrl + +// + +class Main { + static boolean run = true; + static final Monitor monitor = new Monitor(); + static final Analyze analyze = new Analyze(); + static final Plan plan = new Plan(); + private static final Execute execute = new Execute(); + static final Knowledge shared_knowledge = new Knowledge(); + private static final boolean log = true; + + public static void main(String[] args) throws Exception { + Logger.getRootLogger().setLevel(Level.ERROR); + + + shared_knowledge.start(); + Thread.sleep(3000); + + Thread thread_m = new Thread(() -> { + try { + monitor.start(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + + Thread thread_a = new Thread(() -> { + try { + analyze.start(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + Thread thread_p = new Thread(() -> { + try { + plan.start(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + Thread thread_e = new Thread(() -> { + try { + execute.start(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + thread_m.start(); + thread_a.start(); + thread_p.start(); + thread_e.start(); + + } + + static void logger(String from, String msg) { + if (log) { + switch (from) { + case "Knowledge": + System.out.println("\u001B[1;31m" + "\t[" + from + "] : \t\t" + msg + "\u001B[0m"); + break; + case "Monitor": + System.out.println("\u001B[1;32m" + "\t[" + from + "] : \t\t" + msg + "\u001B[0m"); + break; + case "Analyze": + System.out.println("\u001B[1;34m" + "\t[" + from + "] : \t\t" + msg + "\u001B[0m"); + break; + case "Plan": + System.out.println("\u001B[1;35m" + "\t[" + from + "] : \t\t\t" + msg + "\u001B[0m"); + break; + case "Execute": + System.out.println("\u001B[1;36m" + "\t[" + from + "] : \t\t" + msg + "\u001B[0m"); + break; + default: + System.out.println("\t[" + from + "] : \t\t" + msg); + } + + } + } +} \ No newline at end of file diff --git a/gctrl/src/main/java/Monitor.java b/gctrl/src/main/java/Monitor.java new file mode 100644 index 0000000..e5fb4d5 --- /dev/null +++ b/gctrl/src/main/java/Monitor.java @@ -0,0 +1,159 @@ +import com.github.signaflo.math.operations.DoubleFunctions; +import com.github.signaflo.timeseries.TimeSeries; +import com.github.signaflo.timeseries.forecast.Forecast; +import com.github.signaflo.timeseries.model.arima.Arima; +import com.github.signaflo.timeseries.model.arima.ArimaOrder; +import de.vandermeer.asciitable.AsciiTable; +import de.vandermeer.asciitable.CWC_LongestWord; +import de.vandermeer.asciithemes.a7.A7_Grids; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +// + +//* @author couedrao on 25/11/2019. + +//* @project gctrl + +// + +// + + +//* 1)Collects the details from the managed resources e g topology Collects the details from the managed resources e.g. topology information, metrics (e.g. offered capacity and throughput), configuration property settings and so on. + + +//* 2)The monitor function aggregates,correlates and filters these details until it determines a symptom that needs to be analyzed. + + +//* + + +@SuppressWarnings({"SynchronizeOnNonFinalField"}) +class Monitor { + private static List symptom; + private static final int period = 2000; + private static double i = 0; + public String gw_current_SYMP = "N/A"; + + void start() { + Main.logger(this.getClass().getSimpleName(), "Start monitoring of " + Knowledge.gw); + symptom = Main.shared_knowledge.get_symptoms(); + Main.shared_knowledge.create_lat_tab(); + data_collector(); //in bg + symptom_generator(); + } + + //Symptom Generator (can be modified) + private void symptom_generator() { + while (Main.run) + try { + Thread.sleep(period * 5); + ResultSet rs = Main.shared_knowledge.select_from_tab(); + //print_nice_rs(rs); + double[] prediction = predict_next_lat(rs); + boolean isOk = true; + for (int j = 0; j < Knowledge.horizon; j++) { + if (prediction[j] > Knowledge.gw_lat_threshold) { + Main.logger(this.getClass().getSimpleName(), "Symptom --> To Analyse : " + symptom.get(1)); + update_symptom(symptom.get(1)); + isOk = false; + break; + } else if (prediction[j] < .0) { + Main.logger(this.getClass().getSimpleName(), " Symptom --> To Analyse : " + symptom.get(0)); + update_symptom(symptom.get(0)); + isOk = false; + break; + } + } + if (isOk) { + Main.logger(this.getClass().getSimpleName(), "Symptom --> To Analyse : " + symptom.get(2)); + update_symptom(symptom.get(2)); + } + } catch (SQLException | InterruptedException e) { + e.printStackTrace(); + } + } + + //Data Collector TODO : modify + private void data_collector() { + new Thread(() -> { + Main.logger(this.getClass().getSimpleName(), "Filling db with latencies"); + while (Main.run) + try { + //TODO: Remove this + Thread.sleep(period); + Main.shared_knowledge.insert_in_tab(new java.sql.Timestamp(new java.util.Date().getTime()), get_fake_data()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + ).start(); + } + + private int get_data() { + //Call Sensors + /*TODO*/ + return 0; + } + + private double get_fake_data() { + //return new Random().nextInt(); + return i += 2.5; + } + + //ARIMA-based Forecasting + private double[] predict_next_lat(ResultSet rs) throws SQLException { + rs.first(); + double[] history = new double[Knowledge.moving_wind]; + double[] p = new double[Knowledge.horizon]; + int j = Knowledge.moving_wind - 1; + while (rs.next()) { + history[j] = Double.parseDouble(rs.getString("latency")); + j--; + } + TimeSeries timeSeries = TimeSeries.from(DoubleFunctions.arrayFrom(history)); + ArimaOrder modelOrder = ArimaOrder.order(0, 1, 1, 0, 1, 1); + //ArimaOrder modelOrder = ArimaOrder.order(0, 0, 0, 1, 1, 1); + Arima model = Arima.model(timeSeries, modelOrder); + Forecast forecast = model.forecast(Knowledge.moving_wind); + System.out.print("Point Estimates : "); + for (int k = 0; k < Knowledge.horizon; k++) { + p[k] = forecast.pointEstimates().at(k); + System.out.print(p[k] + "; "); + } + System.out.println(); + return p; + } + + private void print_nice_rs(ResultSet rs) throws SQLException { + rs.first(); + AsciiTable at = new AsciiTable(); + at.addRule(); + at.addRow("Timestamp", "Latency_in_" + Knowledge.gw); + at.addRule(); + while (rs.next()) { + at.addRow(rs.getTimestamp("id").getTime(), rs.getString("latency")); + at.addRule(); + } + at.getContext().setGrid(A7_Grids.minusBarPlusEquals()); + at.getRenderer().setCWC(new CWC_LongestWord()); + System.out.println(this.getClass().getSimpleName() + " : "); + System.out.println(at.render()); + + } + + private void update_symptom(String symptom) { + + synchronized (gw_current_SYMP) { + gw_current_SYMP.notify(); + gw_current_SYMP = symptom; + + } + } + + +} \ No newline at end of file diff --git a/gctrl/src/main/java/Plan.java b/gctrl/src/main/java/Plan.java new file mode 100644 index 0000000..7655407 --- /dev/null +++ b/gctrl/src/main/java/Plan.java @@ -0,0 +1,87 @@ +import java.util.List; + +// + +// @author couedrao on 25/11/2019. + +//* @project gctrl + +// + +// + +//* 1)Structures the actions needed to achieve goals and objectives Structures the actions needed to achieve goals and objectives. + +//* 2)The plan function creates or selects a procedure to enact a desired alteration in the managed resource. + +//* 3)The plan function can take on many forms, ranging from a single command to a complex workflow. + +//* + +@SuppressWarnings({"SynchronizeOnNonFinalField"}) +class Plan { + private static int i; + public String gw_PLAN = ""; + + void start() { + Main.logger(this.getClass().getSimpleName(), "Start Planning"); + + while (Main.run) { + String current_rfc = get_rfc(); + //Main.logger(this.getClass().getSimpleName(), "Received RFC : " + current_rfc); + update_plan(plan_generator(current_rfc)); + + } + } + + //RFC Receiver + private String get_rfc() { + synchronized (Main.analyze.gw_current_RFC) { + try { + Main.analyze.gw_current_RFC.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return Main.analyze.gw_current_RFC; + } + + + //Rule-based Plan Generator + private String plan_generator(String rfc) { + List rfcs = Main.shared_knowledge.get_rfc(); + List plans = Main.shared_knowledge.get_plans(); + + if ("YourPlansDoNotWork".contentEquals(rfc)) { + // Thread.sleep(2000); + Main.run = false; + Main.logger(this.getClass().getSimpleName(), "All the Plans were executed without success. \n \t\t The loop will stop!"); + // Terminate JVM + System.exit(0); + } else if (rfc.contentEquals(rfcs.get(0))) { + Main.logger(this.getClass().getSimpleName(), "Plan --> To Execute : " + plans.get(0)); + i = 0; + return plans.get(0); + } else if (rfc.contentEquals(rfcs.get(1))) { + if (i == 0) { + Main.logger(this.getClass().getSimpleName(), "Plan --> To Execute : " + plans.get(1)); + i++; + return plans.get(1); + } else if (i == 1) { + Main.logger(this.getClass().getSimpleName(), "Plan --> To Execute : " + plans.get(2)); + i++; + return plans.get(2); + } + } + return null; + } + + + private void update_plan(String plan) { + synchronized (gw_PLAN) { + gw_PLAN.notify(); + gw_PLAN = plan; + } + } +} + diff --git a/gctrl/src/main/java/SDNCtrlAPI.java b/gctrl/src/main/java/SDNCtrlAPI.java new file mode 100644 index 0000000..c7cfbb2 --- /dev/null +++ b/gctrl/src/main/java/SDNCtrlAPI.java @@ -0,0 +1,34 @@ +import java.util.List; + +/** + * @author couedrao on 27/11/2019. + * @project gctrl + */ +class SDNCtrlAPI { + + String redirect_traffic(String olddestip, String newdestip) { + String status = "OK"; + Main.logger(this.getClass().getSimpleName(), "olddestip = " + olddestip + "; newdestip = " + newdestip); + //TODO + + return status; + } + + String insert_a_loadbalancer(String oldgwip, String lbip, List newgwsip) { + String status = "OK"; + Main.logger(this.getClass().getSimpleName(), "oldgwip = " + oldgwip + "; lbip = " + lbip + "; newgwsip = " + newgwsip); + //TODO + + return status; + } + + String remove_less_important_traffic(String importantsrcip) { + String status = "OK"; + Main.logger(this.getClass().getSimpleName(), "importantsrcip = " + importantsrcip); + //TODO + + return status; + } + + +} diff --git a/gctrl/src/misc/5sdbd_gc.draft.svg b/gctrl/src/misc/5sdbd_gc.draft.svg new file mode 100644 index 0000000..2e3127d --- /dev/null +++ b/gctrl/src/misc/5sdbd_gc.draft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gctrl/src/test/java/Tester.java b/gctrl/src/test/java/Tester.java new file mode 100644 index 0000000..10d35cd --- /dev/null +++ b/gctrl/src/test/java/Tester.java @@ -0,0 +1,35 @@ +import java.util.List; +import java.util.Scanner; + +// +//* @author couedrao on 25/11/2019. +//* @project gctrl +// +class Tester { + + private static final Knowledge k = new Knowledge(); + + public static void main(String[] args) throws Exception { + k.start(); + List workflow_lists = k.get_worklow_lists(); + while (Main.run) { + logger("You are in test mode! The following actions can be performed : "); + for (int i = 0; i < workflow_lists.size(); i++) { + logger("[" + i + "] :" + workflow_lists.get(i)); + } + logger("Select any number in [0-" + (workflow_lists.size() - 1) + "] to continue"); + int input = new Scanner(System.in).nextInt(); + if (input < workflow_lists.size() ) + logger("Execution of Action : [" + workflow_lists.get(input) + "]"); + else logger("(-_-)"); + + //Call Effectors + /*TODO : */ + } + } + + private static void logger(String msg) { + if (true) + System.out.println(msg); + } +} diff --git a/gctrl/target/classes/Analyze.class b/gctrl/target/classes/Analyze.class new file mode 100644 index 0000000000000000000000000000000000000000..20782713457deb4a190cb1ff8f7d9682a4824c38 GIT binary patch literal 2724 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 H`fvXaeOz6J literal 0 HcmV?d00001 diff --git a/gctrl/target/classes/Plan.class b/gctrl/target/classes/Plan.class new file mode 100644 index 0000000000000000000000000000000000000000..e32fcc041dd26a1918836edd06f71e5dc57905cd GIT binary patch literal 2837 zcmZuzOLr4x7=9)Rog^J7meN9jGDv|o=|vG0DIle^P)W~!VE!yoV$=uv#WnQ5BZF7n;p`};ocJGuAo?VkWViO-T) zi_SQ9C(wW{dD)XhH+tgOo5Vi!Cb&G4ByGQ3GIHsYZ~X}zz(L7>NX6kKsxTm*2a`C0 zqjEVG$MGcUaUzbBNu0vzN_5~^70;;{s;23zG&n332o>j4 z>dblLqS0;{cCLNEb4@$fp`c>BX`9|o1<|JFqY7eO&ahBWl`(D6UmQCt+(F~4#h2=g zlQpcPhHJ`u_!#p>%mQ^}4pJ$VEh>11>nW(r7F}1^-jI7Pt6=efXJkkFjC?4i=0r%T zX=+}`Cx!O(GD5HI3D+&=Ju%#UDJ$}x>DV1AMieZ`)9_GE*uphD$5pVgsZ1^mazPtW z-?bQ52Cy^`Dte~Xo-qra3`J@tsdjGM6Zx6g8~-P>eZJwHbdpu5Eary|PYg+o3YIVA zq0D?hP7XLlH!FHfne@a#inPh9X=ua-1<>#?)@wMAQ5BYkF|;!Y%&+U|lGkQYRoEIF zURKcEX<52AB7Bz$`nYg~E-r~|kpe|0dU~Ol&5A;y zO;75Hgg!`a%W?7~Q?}qa`E@E@(eNr>)9^apU?FDR>>4rL0db)y>@1VCbe0eloKL$Q z$18ZQk?#|6DviRCWKC=e;j=78CMB#x?P^@MzjDKcP=0f5ZM zmTcCjTcn_Jl1*;dhBa}S+7g-0;7HqQcqBwP1ZH~ja8DPFjvFSUtjco?cz%Blx`v<4 z0+TC-hemB@+!Dh%s;tcHpSe(JqI4kyW(E6b*~_Rpqgj zm$9RnQIuD5qTq=!`YA3+KVwb3e!x^MJE*t3rGtufB=IiZQ&1->s<*c8)CV2CI{=-& zO;<=6Ge$+1V`mNT7%|8br)D_iz5M{tm{X94z8W&B|( z%Ci%y=rSy8!1M$s(x(bG%!_X!-{y>lIj)2;u#p=0$pIor%BJE;k@JcBBK|nBc;Cp= zQ{+&%SEhb|lB$k;kLYz?BiO_>$sMRg3@gyYlNPX|87ba?bV4L?)F=Pa`>v%4-k)l=`E-{(4mKyo!3)pqdWX(N*1dbG>h`mOAT@Knoq0 zPBw;?TX~KlqDeZ3C}}d3?|6%GPftO+iNz-}cY?z-mRv);uVorb(|395Pv33*IUr5a zkpxXI=XX7U)qcj-(gB&dz{LirJZ~bknS3qeOfey8e(+oAPFu-MnWRVXsGo#%nVa>9 zJ)lrTMT1JUii)b(9u=Gak^#u!A2_dkLwnNs`%`#m8r6ONwyeLphIw0l74^{@h|lD< zmbs1ILfth?rCXvuVnqbMV5QHO4tZKIlVE*hboRrgaCN z#!ejPdK$ZsLnl9VyZH(0!spn7ulUdHTlC;M_TnD@js1pR{DF*Lj8u@u5=QS+zeJCU z-|+=g0kmrURy;)Ohw0M*mSK>6aHIq&YA!|5 wz--7Uq*p5tdB7#=L#jSRl@F*W^Cf$C7e3;iB>xAvf)BA0XLyhEZt!>ZKV+ei6aWAK literal 0 HcmV?d00001 diff --git a/gctrl/target/test-classes/Tester.class b/gctrl/target/test-classes/Tester.class new file mode 100644 index 0000000000000000000000000000000000000000..1dbca41bdb070bf6b1ce133c1bc74b6ab6694b72 GIT binary patch literal 2189 zcmaJ?U2_vv7=BI?y6JY?(m+4JiVGGrXTn< zqh5Qhx8C&HIs*bTj@SMQe}}_}&)LK#G#%T?`FPKJ-t)Zg^PIDP{`2H_0B_@K6GJ%L zi9x($q92pGnd-ze&Lwf)r1?S@#__IhE*iL$;-)Jp2wXLg>BI-PW}+Ke-OQ#C!;K_v zYV(ICK0?mGoQ`+az{dt|35b$FSN58();w7#$|-@4Y1end^8)eoC>;_rsvre=v#u|% z*UC#Wm~)mqT2fh+cf4Cp;Ocq9mp^Rw>rG^7;(-G% z;@YVEhizns?{qt$^W5K^3!Qvv)~Q4(8MrNAUS7}3O6V%TYTy%=$Wz6l3voc&!g=%jH@<|jdNGvB&)cuM;U$ir-evT*kGBDs;xQ~)f z!Lv}ty(E0?tw?ctY;kPVLIs~22rN`_TA*`Vd#r1{1}`AGv2^UFW4gBEZ`gix z+ce7yr^a|jsO-G*L)Whn-VR9H0b04-;1w(j#Pt2^uLE$^L`L5;`b($G^oJS>-m#!X zZ1{3L%=pazz~yzBC(FvW)v|ptk`{%;#EE#fE6|=TOR4nrZsNGfC!mR=&L&Z`mwbl@SHXsXFUd<*sZy;XU}dy0XFR7`l_cZGR=|xI zb&+3n{DLQ|BlV3_?Mbv)Uf4LtM`joI)+el?nO4#pA!Z!U%ekSPGVqna(YE|{!zOU( z|NUU|J2AvBgTO)kj&PLVkIxD%FLN}(Ua;SD;t|9z9K>*lZAWBG;uVgqx&uQU=ByJ( z@G8ByS&Spjf8e_<#2z9ZwTbK&_WXn()$tpQ__^VekC7Dk8NxBlJGR-e!QI*8P2v=O&V=6nAVs|3O1fM9?SE!*MrL z=waK7eb~zi^kD=kepLH#4*N07Pu~ItD5XI>;OaN5$G1#Lr+b2N4l>0N9A%md?2knw zritT7N3mBiN{w511SHP+pta+1(B;LTA+8=Li)2JEIe_c<}KmP~=zapl?P#