diff --git a/app/pom.xml b/app/pom.xml index 8a3b0d0..0fcf3dc 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -23,6 +23,15 @@ 0.1.0.0 compile + + + + + + org.eclipse.jgit + org.eclipse.jgit + 7.3.0.202506031305-r + \ No newline at end of file diff --git a/app/src/main/java/ru/kirillius/pf/sdn/App.java b/app/src/main/java/ru/kirillius/pf/sdn/App.java index a1a465b..f2a34ed 100644 --- a/app/src/main/java/ru/kirillius/pf/sdn/App.java +++ b/app/src/main/java/ru/kirillius/pf/sdn/App.java @@ -1,62 +1,31 @@ package ru.kirillius.pf.sdn; -import lombok.Getter; -import org.eclipse.jetty.server.Server; -import ru.kirillius.pf.sdn.External.API.HENetBGPInfoProvider; -import ru.kirillius.pf.sdn.core.Auth.AuthManager; -import ru.kirillius.pf.sdn.core.Config; -import ru.kirillius.pf.sdn.core.Context; -import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInformationService; -import ru.kirillius.pf.sdn.core.Networking.NetworkManager; +import ru.kirillius.utils.logging.SystemLogger; import java.io.File; import java.io.IOException; +import java.util.Collections; +import java.util.logging.Level; -public class App implements Context { - public static void main(String[] args) { - new App(); - } - +public class App extends AppContext { private final static File configFile = new File("config.json"); - public App() { - try { - config = Config.load(configFile); - } catch (IOException e) { - config = new Config(); - try { - Config.store(config, configFile); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - authManager = new AuthManager(this); - server = new Server(); - autonomousSystemInformationService = new AutonomousSystemInformationService(); - autonomousSystemInformationService.setProvider(new HENetBGPInfoProvider()); - networkManager = new NetworkManager(this); - var inputResources = networkManager.getInputResources(); - inputResources.add(config.getCustomResources()); - networkManager.triggerUpdate(); - - while (networkManager.isUpdatingNow()) { - Thread.yield(); - } - - return; + public App(File configFile) { + super(configFile); } - @Getter - private final NetworkManager networkManager; - @Getter - private Config config; - @Getter - private final AuthManager authManager; - @Getter - private final Server server; - @Getter - private final AutonomousSystemInformationService autonomousSystemInformationService; + static { + SystemLogger.initializeLogging(Level.INFO, Collections.emptyList()); + } + + public static void main(String[] args) { + + try (App app = new App(configFile)) { + + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/app/src/main/java/ru/kirillius/pf/sdn/AppContext.java b/app/src/main/java/ru/kirillius/pf/sdn/AppContext.java new file mode 100644 index 0000000..2b3ab90 --- /dev/null +++ b/app/src/main/java/ru/kirillius/pf/sdn/AppContext.java @@ -0,0 +1,66 @@ +package ru.kirillius.pf.sdn; + +import lombok.Getter; +import org.eclipse.jetty.server.Server; +import ru.kirillius.pf.sdn.External.API.HEInfoProvider; +import ru.kirillius.pf.sdn.core.Auth.AuthManager; +import ru.kirillius.pf.sdn.core.Config; +import ru.kirillius.pf.sdn.core.Context; +import ru.kirillius.pf.sdn.core.ContextEventsHandler; +import ru.kirillius.pf.sdn.core.Networking.ASInfoService; +import ru.kirillius.pf.sdn.core.Networking.NetworkManager; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; + +public class AppContext implements Context, Closeable { + + public AppContext(File configFile) { + try { + config = Config.load(configFile); + } catch (IOException e) { + config = new Config(); + try { + Config.store(config, configFile); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + authManager = new AuthManager(this); + server = new Server(); + ASInfoService = new ASInfoService(); + ASInfoService.setProvider(new HEInfoProvider(this)); + networkManager = new NetworkManager(this); + networkManager.getInputResources().add(config.getCustomResources()); + } + + + @Getter + private final NetworkManager networkManager; + @Getter + private Config config; + @Getter + private final AuthManager authManager; + @Getter + private final Server server; + @Getter + private final ASInfoService ASInfoService; + + @Override + public void close() throws IOException { + ASInfoService.close(); + networkManager.close(); + try { + server.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Getter + private final ContextEventsHandler EventsHandler = new ContextEventsHandler(); + + +} diff --git a/app/src/main/java/ru/kirillius/pf/sdn/External/API/GitSubscription.java b/app/src/main/java/ru/kirillius/pf/sdn/External/API/GitSubscription.java new file mode 100644 index 0000000..e29be84 --- /dev/null +++ b/app/src/main/java/ru/kirillius/pf/sdn/External/API/GitSubscription.java @@ -0,0 +1,119 @@ +package ru.kirillius.pf.sdn.External.API; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import ru.kirillius.pf.sdn.core.Context; +import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle; +import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig; +import ru.kirillius.pf.sdn.core.Subscription.SubscriptionProvider; +import ru.kirillius.pf.sdn.core.Util.HashUtil; +import ru.kirillius.utils.logging.SystemLogger; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +public class GitSubscription implements SubscriptionProvider { + private final Context context; + + public GitSubscription(Context context) { + this.context = context; + } + + @Override + public Map getResources(RepositoryConfig config) { + try { + var repoDir = new File(context.getConfig().getCacheDirectory(), "git/" + HashUtil.md5(config.getName())); + + if (!repoDir.exists()) { + if (!repoDir.mkdirs()) { + throw new IOException("Unable to create directory: " + repoDir.getAbsolutePath()); + } + } + + var repository = isGitRepository(repoDir) ? openRepository(repoDir) : cloneRepository(config.getSource(), repoDir); + + + SystemLogger.message("Fetching git repository " + config.getName(), CTX); + checkAndPullUpdates(repository); + + repository.close(); + return Map.of(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + private static void checkAndPullUpdates(Git git) throws GitAPIException { + + + + + + var fetchResult = git.fetch() + .setCheckFetchedObjects(true) + .call(); + + if (fetchResult.getTrackingRefUpdates() != null && !fetchResult.getTrackingRefUpdates().isEmpty()) { + + + + SystemLogger.message("Downloading updates...", CTX); + + // Выполняем pull чтобы получить изменения + var pullResult = git.pull().call(); + + if (pullResult.isSuccessful()) { + System.out.println("✅ Обновление успешно завершено!"); + + // Проверяем были ли обновлены файлы + if (pullResult.getFetchResult() != null && + !pullResult.getFetchResult().getTrackingRefUpdates().isEmpty()) { + + System.out.println("📁 Были обновлены файлы:"); + pullResult.getFetchResult().getTrackingRefUpdates().forEach(refUpdate -> { + System.out.println(" - " + refUpdate.getLocalName() + + " : " + refUpdate.getOldObjectId().abbreviate(7).name() + + " -> " + refUpdate.getNewObjectId().abbreviate(7).name()); + }); + } + } else { + System.out.println("❌ Ошибка при обновлении репозитория"); + } + } else { + System.out.println("✅ Репозиторий уже актуален, обновлений нет"); + } + + } + + private final static String CTX = GitSubscription.class.getSimpleName(); + + private static Git cloneRepository(String REPO_URL, File path) throws GitAPIException { + SystemLogger.message("Cloning repository " + REPO_URL, CTX); + return Git.cloneRepository() + .setURI(REPO_URL) + .setDirectory(path) + .setCloneAllBranches(true) + .call(); + } + + private static Git openRepository(File repoDir) throws IOException { + var builder = new FileRepositoryBuilder(); + var repository = builder.setGitDir(new File(repoDir, ".git")) + .readEnvironment() + .findGitDir() + .build(); + return new Git(repository); + } + + private static boolean isGitRepository(File directory) { + if (!directory.exists() || !directory.isDirectory()) { + return false; + } + + var gitDir = new File(directory, ".git"); + return gitDir.exists() && gitDir.isDirectory(); + } +} diff --git a/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java b/app/src/main/java/ru/kirillius/pf/sdn/External/API/HEInfoProvider.java similarity index 83% rename from app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java rename to app/src/main/java/ru/kirillius/pf/sdn/External/API/HEInfoProvider.java index ec76931..65fc689 100644 --- a/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java +++ b/app/src/main/java/ru/kirillius/pf/sdn/External/API/HEInfoProvider.java @@ -4,7 +4,8 @@ import lombok.SneakyThrows; import org.jetbrains.annotations.NotNull; import org.json.JSONObject; import org.json.JSONTokener; -import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInfoProvider; +import ru.kirillius.pf.sdn.core.Context; +import ru.kirillius.pf.sdn.core.Networking.ASInfoProvider; import ru.kirillius.pf.sdn.core.Networking.IPv4Subnet; import ru.kirillius.utils.logging.SystemLogger; @@ -17,7 +18,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class HENetBGPInfoProvider implements AutonomousSystemInfoProvider { +public class HEInfoProvider implements ASInfoProvider { + + private final Context context; + + public HEInfoProvider(Context context) { + this.context = context; + } + @Override @SneakyThrows public List getPrefixes(int as) { @@ -52,5 +60,5 @@ public class HENetBGPInfoProvider implements AutonomousSystemInfoProvider { return list; } - private final static String CTX = HENetBGPInfoProvider.class.getSimpleName(); + private final static String CTX = HEInfoProvider.class.getSimpleName(); } diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java index 11c9579..3d5eb75 100644 --- a/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java @@ -11,6 +11,7 @@ import ru.kirillius.json.JSONSerializable; import ru.kirillius.json.JSONUtility; import ru.kirillius.pf.sdn.core.Auth.AuthToken; import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle; +import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig; import java.io.*; import java.util.Collections; @@ -27,11 +28,21 @@ public class Config { @JSONProperty private String host = "0.0.0.0"; + @Setter + @Getter + @JSONProperty + private File cacheDirectory = new File("./.cache"); + @Setter @Getter @JSONArrayProperty(type = AuthToken.class) private List tokens = Collections.emptyList(); + @Setter + @Getter + @JSONArrayProperty(type = RepositoryConfig.class) + private List subscriptions = Collections.emptyList(); + @Setter @Getter @JSONProperty diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java index 4327a74..8a88472 100644 --- a/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java @@ -2,7 +2,7 @@ package ru.kirillius.pf.sdn.core; import org.eclipse.jetty.server.Server; import ru.kirillius.pf.sdn.core.Auth.AuthManager; -import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInformationService; +import ru.kirillius.pf.sdn.core.Networking.ASInfoService; import ru.kirillius.pf.sdn.core.Networking.NetworkManager; public interface Context { @@ -12,8 +12,8 @@ public interface Context { Server getServer(); - AutonomousSystemInformationService getAutonomousSystemInformationService(); + ASInfoService getASInfoService(); NetworkManager getNetworkManager(); - + ContextEventsHandler getEventsHandler(); } diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/ContextEventsHandler.java b/core/src/main/java/ru/kirillius/pf/sdn/core/ContextEventsHandler.java new file mode 100644 index 0000000..3d5f917 --- /dev/null +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/ContextEventsHandler.java @@ -0,0 +1,13 @@ +package ru.kirillius.pf.sdn.core; + +import lombok.Getter; +import ru.kirillius.java.utils.events.ConcurrentEventHandler; +import ru.kirillius.java.utils.events.EventHandler; +import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle; + +public final class ContextEventsHandler { + @Getter + private final EventHandler networkManagerUpdateEvent = new ConcurrentEventHandler<>(); + @Getter + private final EventHandler subscriptionsUpdateEvent = new ConcurrentEventHandler<>(); +} diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoProvider.java new file mode 100644 index 0000000..6a7c17b --- /dev/null +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoProvider.java @@ -0,0 +1,20 @@ +package ru.kirillius.pf.sdn.core.Networking; + +import ru.kirillius.pf.sdn.core.Context; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public interface ASInfoProvider { + List getPrefixes(int as); + + static ASInfoProvider instantiate(Class providerClass, Context context) { + try { + var constructor = providerClass.getConstructor(Context.class); + return constructor.newInstance(context); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | + IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoService.java similarity index 84% rename from core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java rename to core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoService.java index bb39c77..5a5bc2a 100644 --- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoService.java @@ -12,13 +12,13 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; @NoArgsConstructor -public class AutonomousSystemInformationService implements Closeable { +public class ASInfoService implements Closeable { private final ExecutorService executor = Executors.newSingleThreadExecutor(); @Getter @Setter - private AutonomousSystemInfoProvider provider = null; + private ASInfoProvider provider = null; public Future> getPrefixes(int as) { diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java deleted file mode 100644 index 3d1c3ce..0000000 --- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.kirillius.pf.sdn.core.Networking; - -import java.util.List; - -public interface AutonomousSystemInfoProvider { - List getPrefixes(int as); -} diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java index 214e451..f96083c 100644 --- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java @@ -72,6 +72,11 @@ public class NetworkManager implements Closeable { outputResources.setSubnets(subnets.stream().toList()); outputResources.setDomains(domains.stream().toList()); + try { + context.getEventsHandler().getNetworkManagerUpdateEvent().invoke(outputResources); + } catch (Exception e) { + throw new RuntimeException(e); + } })); } @@ -81,7 +86,7 @@ public class NetworkManager implements Closeable { private void fetchPrefixes(List systems) { systems.forEach(as -> { - var service = context.getAutonomousSystemInformationService(); + var service = context.getASInfoService(); var future = service.getPrefixes(as); while (!future.isDone() && !future.isCancelled()) { diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/RepositoryConfig.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/RepositoryConfig.java new file mode 100644 index 0000000..37fd4dc --- /dev/null +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/RepositoryConfig.java @@ -0,0 +1,26 @@ +package ru.kirillius.pf.sdn.core.Subscription; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.kirillius.json.JSONProperty; +import ru.kirillius.json.JSONSerializable; + +@NoArgsConstructor +@JSONSerializable +public class RepositoryConfig { + @Setter + @Getter + @JSONProperty + private String name; + + @Setter + @Getter + @JSONProperty + private Class type; + + @Setter + @Getter + @JSONProperty + private String source; +} diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionManager.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionManager.java new file mode 100644 index 0000000..7f70070 --- /dev/null +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionManager.java @@ -0,0 +1,57 @@ +package ru.kirillius.pf.sdn.core.Subscription; + +import lombok.Getter; +import ru.kirillius.pf.sdn.core.Context; +import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + +public class SubscriptionManager implements Closeable { + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + + private Context context; + + public SubscriptionManager(Context context) { + this.context = context; + } + + private final AtomicReference> updateProcess = new AtomicReference<>(); + + @Getter + private final NetworkResourceBundle outputResources = new NetworkResourceBundle(); + + public boolean isUpdatingNow() { + var future = updateProcess.get(); + return future != null && !future.isDone() && !future.isCancelled(); + } + + + public synchronized void triggerUpdate() { + if (isUpdatingNow()) { + return; + } + updateProcess.set(executor.submit(() -> { + var bundle = new NetworkResourceBundle(); + + + outputResources.clear(); + outputResources.add(bundle); + try { + context.getEventsHandler().getSubscriptionsUpdateEvent().invoke(outputResources); + } catch (Exception e) { + throw new RuntimeException(e); + } + })); + } + + + @Override + public void close() throws IOException { + executor.shutdown(); + } +} diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionProvider.java new file mode 100644 index 0000000..1a9e83f --- /dev/null +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionProvider.java @@ -0,0 +1,21 @@ +package ru.kirillius.pf.sdn.core.Subscription; + +import ru.kirillius.pf.sdn.core.Context; +import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +public interface SubscriptionProvider { + Map getResources(RepositoryConfig config); + + static SubscriptionProvider instantiate(Class providerClass, Context context) { + try { + var constructor = providerClass.getConstructor(Context.class); + return constructor.newInstance(context); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | + IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java index dd62def..5490fbc 100644 --- a/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java +++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java @@ -8,6 +8,26 @@ public final class HashUtil { private HashUtil() { } + public static String md5(String input) { + try { + var md = MessageDigest.getInstance("MD5"); + var hash = md.digest(input.getBytes()); + + + var hexString = new StringBuilder(); + for (var b : hash) { + var hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found", e); + } + } public static String hash(String data, String salt) { String generatedPassword = null; diff --git a/pom.xml b/pom.xml index 94bdf3d..80a6856 100644 --- a/pom.xml +++ b/pom.xml @@ -63,22 +63,8 @@ cron-utils 9.2.0 - - - org.snmp4j - snmp4j - 3.7.7 - - - ru.kirillius - icmp4j - 1.0.0.0 - - - ru.kirillius.util - dynamic-types - 2.0.0.0 - + + ru.kirillius json-rpc-servlet @@ -89,12 +75,17 @@ common-logging 1.3.0.0 - - ru.kirillius - hibernate-commons - 2.2.0.0 - + + org.slf4j + slf4j-api + 2.0.9 + + + org.slf4j + slf4j-jdk14 + 2.0.9 +