From 28ac13f5a7ffde6873d49aced6e877697c7aff27 Mon Sep 17 00:00:00 2001 From: Trumeet Date: Thu, 8 Apr 2021 22:30:34 -0700 Subject: feat(agent): bird: use a concentrated configuration --- .../agent/provision/BGPProvisioner.java | 82 +++++++++------------- agent/src/main/resources/bird2.conf.ftlh | 18 +++-- docs/agent/Configuration.md | 3 +- 3 files changed, 45 insertions(+), 58 deletions(-) diff --git a/agent/src/main/java/moe/yuuta/dn42peering/agent/provision/BGPProvisioner.java b/agent/src/main/java/moe/yuuta/dn42peering/agent/provision/BGPProvisioner.java index 1ae8a2b..81eec33 100644 --- a/agent/src/main/java/moe/yuuta/dn42peering/agent/provision/BGPProvisioner.java +++ b/agent/src/main/java/moe/yuuta/dn42peering/agent/provision/BGPProvisioner.java @@ -35,44 +35,28 @@ public class BGPProvisioner implements IProvisioner { } @Nonnull - private Future> calculateDeleteChanges(@Nonnull List allDesired) { - final String[] actualNamesRaw = new File("/etc/bird/peers").list((dir, name) -> name.matches("dn42_.*\\.conf")); - final List actualNames = Arrays.stream(actualNamesRaw == null ? new String[]{} : actualNamesRaw) - .sorted() - .collect(Collectors.toList()); - final String[] desiredNames = allDesired - .stream() - .map(desired -> generateBGPPath(desired.getId())) - .sorted() - .collect(Collectors.toList()) - .toArray(new String[]{}); - final List toRemove = new ArrayList<>(actualNames.size()); - for (int i = 0; i < desiredNames.length; i ++) { - toRemove.clear(); - for(int j = 0; j < actualNames.size(); j ++) { - if(("/etc/bird/peers/" + actualNames.get(j)).equals(desiredNames[i])) { - toRemove.add(j); - } - } - for (int j = 0; j < toRemove.size(); j ++) { - actualNames.remove(toRemove.get(j).intValue()); - } - } - return Future.succeededFuture(actualNames.stream() + private Future> calculateDeleteChanges() { + final String[] actualNames = new File("/etc/bird/peers").list((dir, name) -> name.matches("dn42_.*\\.conf")); + if(actualNames == null) + return Future.succeededFuture(Collections.emptyList()); + return Future.succeededFuture(Arrays.stream(actualNames) .map(string -> new FileChange("/etc/bird/peers/" + string, null, FileChange.Action.DELETE.toString())) .collect(Collectors.toList())); } @Nonnull - private static String generateBGPPath(long id) { - return String.format("/etc/bird/peers/dn42_%d.conf", id); + private String generateBGPPath() { + return vertx + .getOrCreateContext() + .config() + .getString("bird_output_path", "/etc/bird/peers/dn42peers.conf"); } @Nonnull - private Future readConfig(long id) { + private Future readConfig() { return Future.future(f -> { vertx.fileSystem() - .readFile(generateBGPPath(id)) + .readFile(generateBGPPath()) .onFailure(err -> { if(err instanceof FileSystemException && err.getCause() instanceof NoSuchFileException) { @@ -86,31 +70,37 @@ public class BGPProvisioner implements IProvisioner { } @Nonnull - private Future renderConfig(@Nonnull BGPConfig config) { - final Map params = new HashMap<>(3); - params.put("name", config.getId()); - params.put("asn", config.getAsn()); - params.put("ipv4", config.getIpv4()); - params.put("ipv6", config.getIpv6().equals("") ? null : config.getIpv6()); - params.put("mpbgp", config.getMpbgp()); - params.put("dev", config.getInterface()); + private Future renderConfig(@Nonnull List configs) { + final Map params = new HashMap<>(1); + final List configParams = new ArrayList<>(configs.size()); + configs.forEach(config -> { + final Map param = new HashMap<>(6); + param.put("name", config.getId()); + param.put("asn", config.getAsn()); + param.put("ipv4", config.getIpv4()); + param.put("ipv6", config.getIpv6().equals("") ? null : config.getIpv6()); + param.put("mpbgp", config.getMpbgp()); + param.put("dev", config.getInterface()); + configParams.add(param); + }); + params.put("sessions", configParams); return engine.render(params, "bird2.conf.ftlh"); } @Nonnull - private Future> calculateSingleChange(@Nonnull BGPConfig desiredConfig) { - return CompositeFuture.all(readConfig(desiredConfig.getId()), renderConfig(desiredConfig)) + private Future> calculateSingleChange(@Nonnull List desiredConfigs) { + return CompositeFuture.all(readConfig(), renderConfig(desiredConfigs)) .compose(future -> { final Buffer actualBuff = future.resultAt(0); final String actual = actualBuff == null ? null : actualBuff.toString(); final String desired = future.resultAt(1).toString(); final List changes = new ArrayList<>(1); if(actual == null) { - changes.add(new FileChange(generateBGPPath(desiredConfig.getId()), + changes.add(new FileChange(generateBGPPath(), desired, FileChange.Action.CREATE_AND_WRITE.toString())); } else if(!actual.equals(desired)) { - changes.add(new FileChange(generateBGPPath(desiredConfig.getId()), + changes.add(new FileChange(generateBGPPath(), desired, FileChange.Action.OVERWRITE.toString())); } @@ -124,18 +114,10 @@ public class BGPProvisioner implements IProvisioner { // All of these calculations can be done in parallel but we must wait all of them to finish. // The three major steps above must be done in sequence. // Step 1: Calculate individual BGP changes in parallel and combine them into a single future. - return CompositeFuture.join(allDesired.stream() - .map(this::calculateSingleChange) - .collect(Collectors.toList())) - .compose(compositeFuture -> { - final List changes = new ArrayList<>(); - for (int i = 0; i < compositeFuture.size(); i++) - changes.addAll(compositeFuture.resultAt(i)); - return Future.succeededFuture(changes); - }) + return calculateSingleChange(allDesired) // Step 2: Calculate things to delete. .compose(changes -> { - return calculateDeleteChanges(allDesired).compose(deleteChangeList -> { + return calculateDeleteChanges().compose(deleteChangeList -> { changes.addAll(deleteChangeList); return Future.succeededFuture(changes); }); diff --git a/agent/src/main/resources/bird2.conf.ftlh b/agent/src/main/resources/bird2.conf.ftlh index fa98469..23ed1fd 100644 --- a/agent/src/main/resources/bird2.conf.ftlh +++ b/agent/src/main/resources/bird2.conf.ftlh @@ -1,12 +1,16 @@ -<#if !mpbgp> -protocol bgp dn42_${name?long?c} from dnpeers { - neighbor ${ipv4} as ${asn?long?c}; +# Generated by dn42peering agent. Do not modify. + +<#list sessions as session> +<#if !session.mpbgp> +protocol bgp dn42_${session.name?long?c} from dnpeers { + neighbor ${session.ipv4} as ${session.asn?long?c}; direct; } -<#if ipv6??> -protocol bgp dn42_${name?long?c}_v6 from dnpeers { - neighbor ${ipv6}%${dev} as ${asn?long?c}; +<#if session.ipv6??> +protocol bgp dn42_${session.name?long?c}_v6 from dnpeers { + neighbor ${session.ipv6}%${session.dev} as ${session.asn?long?c}; direct; } - \ No newline at end of file + + \ No newline at end of file diff --git a/docs/agent/Configuration.md b/docs/agent/Configuration.md index ee3caaf..ed393dd 100644 --- a/docs/agent/Configuration.md +++ b/docs/agent/Configuration.md @@ -8,7 +8,8 @@ The configuration format of agent is JSON. { "internal_ip": "", "persistent": false, - "persistent_path": "/var/lib/dn42peering/agent/config" + "persistent_path": "/var/lib/dn42peering/agent/config", + "bird_output_path": "/etc/bird/peers/dn42peers.conf" } ``` -- cgit v1.2.3