Skip to content

Commit

Permalink
Merge pull request #18 from epics-extensions/archiver-appliance
Browse files Browse the repository at this point in the history
Archiver Appliance
  • Loading branch information
minijackson authored Aug 7, 2023
2 parents 76298ba + f274f34 commit b7d5853
Show file tree
Hide file tree
Showing 12 changed files with 1,018 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/module-list.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[
./modules/archiver-appliance.nix
./modules/phoebus/alarm-logger.nix
./modules/phoebus/alarm-server.nix
./modules/phoebus/local-kafka.nix
Expand Down
378 changes: 378 additions & 0 deletions nixos/modules/archiver-appliance.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,378 @@
{
config,
lib,
pkgs,
...
}: let
cfg = config.services.archiver-appliance;

hostname = "localhost";

defaultAppliancesXml = ''
<appliances>
<appliance>
<identity>appliance0</identity>
<cluster_inetport>${hostname}:16670</cluster_inetport>
<mgmt_url>http://${hostname}:8080/mgmt/bpl</mgmt_url>
<engine_url>http://${hostname}:8080/engine/bpl</engine_url>
<etl_url>http://${hostname}:8080/etl/bpl</etl_url>
<retrieval_url>http://${hostname}:8080/retrieval/bpl</retrieval_url>
<data_retrieval_url>http://${hostname}:8080/retrieval</data_retrieval_url>
</appliance>
</appliances>
'';

contextXml = pkgs.writeTextDir "/context.xml" ''
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>''${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
<Resource
name="jdbc/archappl"
type="javax.sql.DataSource"
removeAbandonedTimeout="60"
removeAbandoned="true"
logAbandoned="true"
jmxEnabled="true"
driverClassName="org.mariadb.jdbc.Driver"
url="jdbc:mariadb://localhost:3306/archappl?localSocket=/run/mysqld/mysqld.sock"
/>
</Context>
'';

loggingProperties = pkgs.writeTextDir "/logging.properties" ''
.handlers = java.util.logging.ConsoleHandler
'';

log4jProperties = pkgs.writeTextDir "/log4j.properties" ''
log4j.rootLogger = ERROR, stdout
log4j.logger.config.org.epics.archiverappliance = INFO
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d] %p %m (%c)%n
'';
in {
options.services.archiver-appliance = {
enable = lib.mkEnableOption ''
Archiver Appliance.
Archiver Appliance will listen on port 8080'';

package = lib.mkOption {
description = "Archiver Appliance package to use.";
type = lib.types.package;
default = pkgs.epnix.archiver-appliance;
defaultText = lib.literalExpression "pkgs.epnix.archiver-appliance";
};

# TODO: make port configurable, which is harder than it sounds
openFirewall = lib.mkOption {
description = ''
Open the firewall for the Archiver Appliance service.
Warning: this opens the firewall on all network interfaces.
'';
type = lib.types.bool;
default = false;
};

appliancesXml = lib.mkOption {
description = ''
Content of the `appliances.xml` file.
See the [ARCHAPPL_APPLIANCES documentation] for more details.
[ARCHAPPL_APPLIANCES documentation]: https://slacmshankar.github.io/epicsarchiver_docs/api/org/epics/archiverappliance/config/ConfigService.html#ARCHAPPL_APPLIANCES
'';
type = lib.types.str;
default = defaultAppliancesXml;
};

settings = lib.mkOption {
description = ''
Configuration for Archiver Appliance.
These options will be put into the Archiver Appliance's environment.
'';
default = {};
type = lib.types.submodule {
freeformType = with lib.types; attrsOf (oneOf [str path]);
options = {
ARCHAPPL_MYIDENTITY = lib.mkOption {
description = ''
The identity of the current appliance.
If you change this value, you will need to modify the content of
`appliances.xml`: the specified identity must match an identity
of one of the appliance XML elements.
See the [ARCHAPPL_MYIDENTITY documentation] for more details.
[ARCHAPPL_MYIDENTITY documentation]: https://slacmshankar.github.io/epicsarchiver_docs/api/org/epics/archiverappliance/config/ConfigService.html#ARCHAPPL_MYIDENTITY
'';
type = lib.types.str;
default = "appliance0";
};

ARCHAPPL_APPLIANCES = lib.mkOption {
description = ''
Path to an `appliances.xml` file.
By default this NixOS module will generate a file from the
`appliancesXml` option, so you might want to modify the
`appliancesXml` option instead.
'';
type = lib.types.path;
default = pkgs.writeText "appliances.xml" cfg.appliancesXml;
defaultText = lib.literalExpression ''pkgs.writeText "appliances.xml" cfg.appliancesXml'';
};

ARCHAPPL_POLICIES = lib.mkOption {
description = ''
Path to a `policies.py` file.
This file specifies the various policies that can be used when
archiving a PV. For example, you can specify that a given policy
archive PVs at a rate of 2Hz.
By default, the `policies.py` found in
`src/sitespecific/tests/classpathfiles/policies.py` is used.
'';
type = lib.types.path;
default = "${cfg.package}/share/archappl/policies.py";
defaultText = lib.literalExpression "\${cfg.package}/share/archappl/policies.py";
};

ARCHAPPL_SHORT_TERM_FOLDER = lib.mkOption {
description = ''
Path to the Short Term Store (STS) folder.
'';
type = lib.types.path;
default = "/arch/sts/ArchiverStore";
};

ARCHAPPL_MEDIUM_TERM_FOLDER = lib.mkOption {
description = ''
Path to the Medium Term Store (MTS) folder.
'';
type = lib.types.path;
default = "/arch/mts/ArchiverStore";
};

ARCHAPPL_LONG_TERM_FOLDER = lib.mkOption {
description = ''
Path to the Long Term Store (MTS) folder.
'';
type = lib.types.path;
default = "/arch/lts/ArchiverStore";
};

EPICS_CA_AUTO_ADDR_LIST = lib.mkOption {
description = ''
If set, behave as if every broadcast address of every network
interface is added to EPICS_CA_ADDR_LIST.
'';
type = lib.types.bool;
default = true;
apply = b:
if b
then "YES"
else "NO";
};

EPICS_CA_ADDR_LIST = lib.mkOption {
description = ''
List of Channel Access destination IP addresses.
Each IP address can be a unicast address, or a broadcast address.
This option is ignored of EPICS_CA_AUTO_ADDR_LIST is enabled (the default).
'';
type = with lib.types; listOf str;
default = [];
# Separated by spaces
apply = toString;
};
};
};
};

stores = {
configure = lib.mkOption {
description = ''
Whether to automatically configure the local STS, MTS, and LTS
directories.
'';
type = lib.types.bool;
default = true;
};

sts.size = lib.mkOption {
description = ''
Size of the STS in bytes.
If null, the size will depend on the amount of RAM available,
normally half of your physical RAM without swap.
Warning: If you oversize it, the machine will deadlock since the
OOM handler will not be able to free that memory.
'';
example = "20g";
type = with lib.types; nullOr str;
default = null;
};

mts.location = lib.mkOption {
description = ''
Backing directory containing the MTS.
'';
example = "/data/mts";
type = lib.types.str;
};

lts.location = lib.mkOption {
description = ''
Backing directory containing the LTS.
'';
example = "/data/lts";
type = lib.types.str;
};
};
};

config = lib.mkIf cfg.enable {
services.archiver-appliance.settings.CATALINA_OUT_CMD = "cat";

services.tomcat = {
enable = true;

webapps = [cfg.package];

extraConfigFiles = [
"${loggingProperties}/logging.properties"
"${contextXml}/context.xml"
];

commonLibs = [
"${log4jProperties}/log4j.properties"

# We use the mariadb connecter, since it supports UNIX socket connection,
# which allows us not not store the password in plaintext in the config.
"${pkgs.epnix.mariadb_jdbc}/share/java/mariadb-java-client.jar"

# Dependencies of the mariadb connector, for UNIX sockets:
# TODO: use the nixpkgs version when we switch to NixOS 23.05
"${pkgs.epnix.jna}/share/java/jna.jar"
"${pkgs.epnix.jna}/share/java/jna-platform.jar"
];

user = "archappl";
group = "archappl";
};

systemd.services.tomcat = {
after = ["mysql.service"];
wants = ["mysql.service"];

environment = cfg.settings;
};

networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [8080];

users.groups."archappl" = {};
users.users."archappl" = {
group = "archapl";
isSystemUser = true;
};

services.mysql = {
enable = true;
package = pkgs.mariadb;

initialDatabases = [
{
name = "archappl";
schema = "${cfg.package}/share/archappl/sql/archappl_mysql.sql";
}
];
ensureUsers = [
{
name = "archappl";
ensurePermissions."archappl.*" = "ALL PRIVILEGES";
}
];
};

systemd.mounts = let
defaultOptions = [
"rw"
# Security: only normal files allowed
"nosuid"
"noexec"
"nodev"
];
in
lib.mkIf cfg.stores.configure [
# STS
{
what = "tmpfs";
where = "${cfg.settings.ARCHAPPL_SHORT_TERM_FOLDER}";
type = "tmpfs";
mountConfig.Options =
lib.concatStringsSep ","
(defaultOptions
++ lib.optional (cfg.stores.sts.size != null) "size=${cfg.stores.sts.size}");
wantedBy = ["local-fs.target"];
}

# MTS
{
what = "${cfg.stores.mts.location}";
where = "${cfg.settings.ARCHAPPL_MEDIUM_TERM_FOLDER}";
mountConfig.Options = lib.concatStringsSep "," (defaultOptions ++ ["bind"]);
wantedBy = ["local-fs.target"];
}

# LTS
{
what = "${cfg.stores.lts.location}";
where = "${cfg.settings.ARCHAPPL_LONG_TERM_FOLDER}";
mountConfig.Options = lib.concatStringsSep "," (defaultOptions ++ ["bind"]);
wantedBy = ["local-fs.target"];
}
];

systemd.tmpfiles.rules = lib.mkIf cfg.stores.configure [
''Z "${cfg.settings.ARCHAPPL_SHORT_TERM_FOLDER}" 0755 "archappl" "archappl"''
''Z "${cfg.settings.ARCHAPPL_MEDIUM_TERM_FOLDER}" 0755 "archappl" "archappl"''
''Z "${cfg.settings.ARCHAPPL_LONG_TERM_FOLDER}" 0755 "archappl" "archappl"''
];
};
}
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

handleTest = path: args: nixosTesting.simpleTest (import path (pkgs // args));
in {
archiver-appliance = handleTest ./archiver-appliance {};
phoebus-alarm = handleTest ./phoebus/alarm.nix {};
phoebus-olog = handleTest ./phoebus/olog.nix {};
}
Loading

0 comments on commit b7d5853

Please sign in to comment.