* The Abbey DVR Role
-The abbey uses Zoneminder to record video from PoE IP HD security
-cameras. The Abbey DVR Role installs Zoneminder and configures it to
-record to =/Zoneminder/=, the mount point for a separate, large
-storage volume. It follows the instructions in Zoneminder's
-=README.Debian= (in =/usr/share/doc/zoneminder/=) to create the ~zm~
-database and configure Apache.
+The abbey uses AgentDVR to record video from PoE IP HD security
+cameras. The "download" button on iSpy's Download page
+([[https://www.ispyconnect.com/download]]), when "Agent DVR - Linux/
+macOS/ RPi" is chosen, suggests the following command lines (the
+second of which is broken across three lines).
-** DVR Machine Setup
-
-The installation process involves some manual intervention. The first
-time a host is enrolled, Ansible will install the necessary packages,
-but it cannot create the database, nor the database user (yet, in the
-first pass). After adding the new machine to the ~dvrs~ group in
-[[=hosts=]], run Ansible to get the Zoneminder software installed.
-
-: ./abbey config HOST
-
-Several configuration steps will be skipped because =/Zoneminder/= has
-not been created yet. To proceed, first create the database and
-database user manually, as described in section [[*Manually Create Zoneminder DB and User][Manually Create
-Zoneminder DB and User]].
-
-** Create =/Zoneminder/=
-
-=/Zoneminder/= should be a separate, large volume lest Zoneminder fill
-the root file system. For acceptable performance, =/Zoneminder/=
-should also be the mount point of a solid-state disk (SSD). A
-symbolic link at =/var/cache/zoneminder/events= targets =/Zoneminder=
-to make it Zoneminder's "default" storage area. (The ~PurgeWhenFull~
-filter only works with the default storage area in v1.34.)
-
-** Continue Zoneminder Configuration
-
-Once the ~zm~ database (and ~zmuser~ database user) are created, and a
-large volume mounted at =/Zoneminder/=, Ansible can continue with the
-Zoneminder configuration.
-
-: ./abbey configure HOST
-
-Configuring Zoneminder's cameras is still a manual process as
-described in the final section, [[*Configure Cameras][Configure Cameras]], below.
-
-** Include Abbey Variables
+#+BEGIN_SRC sh
+sudo apt-get install curl
+bash <(curl -s "https://raw.githubusercontent.com/\
+ispysoftware/agent-install-scripts/main/v2/\
+install.sh")
+#+END_SRC
-Private variables in =private/vars-abbey.yml= are needed, and included
-here, as in the ~abbey-core~ role. The file path is relative to the
-playbook's directory, =playbooks/=.
+Ansible assists by creating the system user ~agentdvr~ and granting it
+enough ~sudo~ latitude to run the installer as instructed above.
+Though a system user, the account gets a home directory,
+=/home/agentdvr/= in which to do the installation. The rest of the
+DVR role, "phase two", waits until AgentDVR is installed.
-#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
-#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml :mkdirp yes
----
-- name: Include private abbey variables.
- include_vars: ../private/vars-abbey.yml
-#+END_SRC
+AgentDVR is installed, after Ansible has set things up, by running the
+command lines prescribed by iSpy while logged in as ~agentdvr~ with
+the current default directory =/home/agentdvr/=. The installer should
+create the =/home/agentdvr/AgentDVR/= directory. Its offer to install
+a system service is declined.
-** Install Zoneminder v1.34
+After AgentDVR is installed, when the =/home/agentdvr/AgentDVR/=
+directory exists, Ansible is run again to install the system service.
-The latest version of Zoneminder (1.36) was manually downloaded, built
-and installed, but it immediately had problems, randomly producing
-short events, dropping "problem" cameras entirely, etc. Version 1.34
-did not have those problems, but could still melt down (thrash?) when
-=/Zoneminder/= was a Seagate Barracuda in a USB3.1gen2 external drive
-enclosure. A Western Digital Passport Ultra seemed to work much
-better, for a short while. Ultimately a solid-state drive (a 2TB
-USB3.2 Gen2 Samsung T7 Shield) mounted at =/Zoneminder/= got
-Zoneminder 1.34 to work reliably.
+** Create User ~agentdvr~
-After uninstalling 1.36, the Debian 11 package (1.34) was installed
-and configured per the instructions in sections "Web server set-up"
-and "Time Zone" in =/usr/share/doc/zoneminder/README.Debian.gz=.
+AgentDVR runs as the system user ~agentdvr~, which is created here.
#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
-
-- name: Install Zoneminder.
- become: yes
- apt: pkg=zoneminder
-
-- name: Enable Apache modules for Zoneminder.
- become: yes
- apache2_module:
- name: "{{ item }}"
- loop: [ cgid, rewrite, expires, headers ]
- notify: Restart Apache2.
-
-- name: Enable Zoneminder Apache configuration.
- become: yes
- command:
- cmd: a2enconf zoneminder
- creates: /etc/apache2/conf-enabled/zoneminder.conf
- notify: Restart Apache2.
-
-- name: Configure MySQL for Zoneminder.
+---
+- name: Create agentdvr.
become: yes
- copy:
- content: |
- [mysqld]
- sql_mode = NO_ENGINE_SUBSTITUTION
- dest: /etc/mysql/conf.d/zoneminder.cnf
- notify: Restart MySQL.
+ user:
+ name: agentdvr
+ system: yes
+ home: /home/agentdvr
+ shell: /bin/bash
+ append: yes
+ groups: video
-- name: Configure PHP date.timezone.
+- name: Add {{ ansible_user }} to agentdvr group.
become: yes
- lineinfile:
- regexp: date.timezone ?=
- line: date.timezone = {{ lookup('file', '/etc/timezone') }}
- path: "{{ item }}"
- loop:
- - /etc/php/8.2/cli/php.ini
- - /etc/php/8.2/apache2/php.ini
- notify: Restart Apache2.
+ user:
+ name: "{{ ansible_user }}"
+ append: yes
+ groups: agentdvr
-- name: Enable/Start Apache2.
+- name: Create /home/agentdvr/.
become: yes
- systemd:
- service: apache2
- enabled: yes
- state: started
+ file:
+ path: /home/agentdvr
+ state: directory
+ owner: agentdvr
+ group: agentdvr
+ mode: u=rwx,g=rwx,o=rx
#+END_SRC
-#+CAPTION: [[file:roles_t/abbey-dvr/handlers/main.yml][=roles_t/abbey-dvr/handlers/main.yml=]]
-#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/handlers/main.yml :mkdirp yes
----
-- name: Restart MySQL.
- become: yes
- systemd:
- service: mysql
- state: restarted
+** Authorize User ~agentdvr~
-- name: Restart Apache2.
- become: yes
- systemd:
- service: apache2
- state: restarted
-#+END_SRC
-
-The following Rsyslog configuration drop-in gets Zoneminder's natter
-out of =/var/log/syslog=.
+The AgentDVR installer is also run by ~agentdvr~, which is authorized
+to run a handful of system commands. This small set is sufficient
+/if/ the offer to create the system service is declined. In that
+case, the installer will run the program in the terminal.
#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
-- name: Use /var/log/zoneminder.log
- become: yes
+- name: Authorized agentdvr.
copy:
content: |
- :programname,startswith,"zm" -/var/log/zoneminder.log
- & stop
- dest: /etc/rsyslog.d/40-zoneminder.conf
+ ALL ALL=(agentdvr) NOPASSWD: /bin/systemctl,/bin/apt-get,\
+ /sbin/adduser,/sbin/usermod
+ dest: /etc/sudoers.d/agentdvr
#+END_SRC
-** Create Zoneminder Database
+** Test For =AgentDVR/=
-Zoneminder's MariaDB database is created by the following task, when
-the ~mysql_db~ Ansible module supports ~check_implicit_admin~.
+The following task probes for the =/home/agentdvr/AgentDVR/=
+directory, to detect that the build/install process has completed. It
+registers the results in the ~agentdvr~ variable. Several of the
+remaining installation steps are skipped unless
+~agentdvr.stat.exists~.
-#+BEGIN_SRC conf
+#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
+#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
-- name: Create Zoneminder DB.
- become: yes
- mysql_db:
- check_implicit_admin: yes
- name: zm
- collation: utf8mb4_general_ci
- encoding: utf8mb4
+- name: Test for AgentDVR directory.
+ stat:
+ path: /home/agentdvr/AgentDVR
+ register: agentdvr
+- debug:
+ msg: "/home/agentdvr/AgentDVR/ does not yet exist"
+ when: not agentdvr.stat.exists
#+END_SRC
-Unfortunately it does not currently, yet the institute prefers the
-more secure Unix socket authentication method. Rather than create a
-privileged DB user, the ~zm~ database is created manually (below).
+** Create AgentDVR Service
-** Create Zoneminder DB User
+This service definition came from the template downloaded (from [[https://raw.githubusercontent.com/ispysoftware/agent-install-scripts/main/v2/AgentDVR.service][here]])
+by the installer, specifically the =linux_setup2.sh= script downloaded
+by =install.sh=.
-The following task would create the DB user (~mysql_user~ supports
-~check_implicit_admin~) /but/ the ~zm~ database was not created above.
-
-The DB user's password is taken from the ~zoneminder_dbpass~
-variable, kept in =private/vars-abbey.yml=, and generated e.g. with
-the ~apg -n 1 -x 12 -m 12~ command.
+#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
+#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
-#+CAPTION: [[file:private_ex/vars-abbey.yml][=private_ex/vars-abbey.yml=]]
-#+BEGIN_SRC conf :tangle private_ex/vars-abbey.yml
-zoneminder_dbpass: gakJopbikJadsEdd
-#+END_SRC
+ - name: Install AgentDVR.service.
+ become: yes
+ copy:
+ content: |
+ [Unit]
+ Description=AgentDVR
-#+BEGIN_SRC conf
+ [Service]
+ WorkingDirectory=/home/agentdvr/AgentDVR
+ ExecStart=/home/agentdvr/AgentDVR/Agent
-- name: Create Zoneminder DB user.
- become: yes
- mysql_user:
- check_implicit_admin: yes
- name: zmuser
- password: "{{ zoneminder_dbpass }}"
- priv: >-
- zm.*:
- lock tables,alter,create,index,select,insert,update,delete
-#+END_SRC
+ # fix memory management issue with dotnet core
+ Environment="MALLOC_TRIM_THRESHOLD_=100000"
-** Manually Create Zoneminder DB and User
+ # to query logs using journalctl, set a logical name here
+ SyslogIdentifier=AgentDVR
-The Zoneminder database and database user are created manually with
-the following SQL (with the ~zoneminder_dbpass~ spliced in). The SQL
-commands are entered at the SQL prompt of the ~sudo mysql~ command, or
-perhaps piped into the command.
+ User=agentdvr
-#+BEGIN_SRC sql
-create database zm
- character set utf8mb4
- collate utf8mb4_general_ci;
-grant lock tables,alter,create,index,select,insert,update,delete
- on zm.*
- to 'zmuser'@'localhost'
- identified by '{{ zoneminder_dbpass }}';
-flush privileges;
-exit;
-#+END_SRC
+ # ensure the service automatically restarts
+ Restart=always
+ # amount of time to wait before restarting the service
+ RestartSec=5
-Finally, ~zm~'s tables are created, completing the database setup,
+ [Install]
+ WantedBy=multi-user.target
+ dest: /etc/systemd/system/AgentDVR.service
+ when: agentdvr.stat.exists
-#+BEGIN_SRC sh
-sudo mysql < /usr/share/zoneminder/db/zm_create.sql
+ - name: Enable/Start AgentDVR.service.
+ become: yes
+ systemd:
+ service: AgentDVR
+ enabled: yes
+ state: started
+ when: agentdvr.stat.exists
#+END_SRC
-** Use =/Zoneminder/=
+** Create AgentDVR Storage
-The following tasks start with a test for the existence of
-=/Zoneminder=. Configuration tasks that require =/Zoneminder/= or the
-~zm~ database are executed only when ~zoneminder.stat.exists~. The
-last "Link..." task below "forces" the link, whether the target exists
-or not (yet).
+The abbey uses a separate volume to store surveillance recordings,
+lest the DVR program fill the root file system. The volume is mounted
+at =/DVR/=. The following tasks create =/DVR/AgentDVR/video/=
+(whether a large volume is mounted there /or not/!) with appropriate
+permissions so that the instructions for configuring a default storage
+location do not fail.
#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
-- name: Test for /Zoneminder/.
- stat:
- path: /Zoneminder
- register: zoneminder
-- debug:
- msg: "/Zoneminder/ does not yet exist"
- when: not zoneminder.stat.exists
-
-- name: Check /Zoneminder/.
+- name: Create /DVR/AgentDVR/.
become: yes
file:
state: directory
- path: /Zoneminder
- owner: www-data
- group: www-data
- mode: u=rwx,g=rx,o=rx
- when: zoneminder.stat.exists
+ path: /DVR/AgentDVR
+ owner: agentdvr
+ group: agentdvr
+ mode: u=rwx,g=rwx,o=
-- name: Link to /Zoneminder/.
+- name: Create /DVR/AgentDVR/video/.
become: yes
file:
- state: link
- src: /Zoneminder
- path: /var/cache/zoneminder/events
- force: yes
- follow: no
+ state: directory
+ path: /DVR/AgentDVR/video
+ owner: agentdvr
+ group: agentdvr
+ mode: u=rwx,g=rx,o=
#+END_SRC
-** Configure Zoneminder
+** Configure IP Cameras
-The remaining tasks ensure that the =/etc/zm/zm.conf= file has the
-proper permissions and contains the correct password.
+A new security camera is setup as described in [[*Cloistering][Cloistering]], after
+which the camera should be accessible by name on the abbey networks.
+Assuming ~ping -c1 new~ works, the camera's web interface will be
+accessible at ~http://new/~.
-#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
-#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
+The administrator uses this to make the following changes.
-- name: Set /etc/zm/zm.conf permissions.
- become: yes
- file:
- path: /etc/zm/zm.conf
- owner: root
- group: www-data
- mode: u=rw,g=r,o=
+ - Set a password on the administrative account.
+ - Create an unprivileged user with a short password,
+ e.g. ~user:blah~.
+ - Set the frame rate to 5fps. The abbey prefers HD resolution and
+ long duration logs, thus fewer frames per second.
-- name: Set Zoneminder passphrase.
- become: yes
- lineinfile:
- regexp: '^ *ZM_DB_PASS *='
- line: ZM_DB_PASS={{ zoneminder_dbpass }}
- path: /etc/zm/zm.conf
-#+END_SRC
+** Configure AgentDVR's Cameras
-Finally, Zoneminder's service unit can be enabled (and started) /if/
-=/Zoneminder/= exists. It is assumed that, if =/Zoneminder/= exists,
-the ~zm~ database has also been created, and the service is ready to
-run.
+After Ansible has configured and started the AgentDVR service, its web
+UI will be available at ~http://core:8090/~. The initial Live View
+will be empty, overlayed with instructions to click the edit button.
-#+CAPTION: [[file:roles_t/abbey-dvr/tasks/main.yml][=roles_t/abbey-dvr/tasks/main.yml=]]
-#+BEGIN_SRC conf :tangle roles_t/abbey-dvr/tasks/main.yml
-- name: Enable/Start Zoneminder.
- become: yes
- systemd:
- service: zoneminder
- enabled: yes
- state: started
- when: zoneminder.stat.exists
-#+END_SRC
+The wizard will ask for each device's general configuration
+parameters. The abbey uses SV3C IP cameras with a full HD stream as
+well as a standard definition "vice stream". AgentDVR wants both.
-** Configure Cameras
+ - General:
+ + On: yes
+ + Name: Outside
+ + Source Type: Network Camera
+ - Username: user
+ - Password: blah
+ - Live URL: rtsp://new.birchwood.private:554/12
+ - Record URL: rtsp://new.birchwood.private:554/11
-A new security camera is setup as described in [[*Cloistering][Cloistering]], after
-which the camera should be accessible by name on the abbey networks.
-Assuming ~ping -c1 new~ works, the camera's web interface will be
-accessible at ~http://new/~.
+Additional cameras are added via the "New Device" item in the Server
+Menu. This step is completed when all cameras are streaming to
+AgentDVR's Live View.
+
+** Configure AgentDVR's Default Storage
-The abbey's administrator logs into ~http://new/~ and turns off any
-OSD (on-screen display). Zoneminder will add its own timestamp, for
-the best accuracy and reliability. The administrator also turns down
-the frame rate to 5fps. The abbey prefers HD resolution (e.g. 1080p)
-and long duration logs, thus fewer frames per second. The
-administrator also creates an unprivileged user with a short password
-e.g. ~user:gobbledygook~.
-
-After Ansible has configured and started Zoneminder, a camera can be
-created by clicking on "Add" in the Zoneminder console. (If the
-Zoneminder host was named "security", the Zoneminder console can be
-found at ~http://security/zm/~.) In the Add dialog, the following
-settings should be changed. (The parenthesized settings are default
-settings that should be checked but are probably already correctly
-set.)
-
- - In the "General" tab, specify:
- - Name: Front
- - (Server: None)
- - (Source type: Ffmpeg)
- - Function: Record
- - Enabled: yes
- - (Analysis FPS: <blank>)
- - (Maximum FPS: <blank>)
- - (Alarm Maximum FPS: <blank>)
- - In the "Source" tab, specify:
- - Src path: rtsp://user:gobbledygook@new.small.private.:554/11
- - (Method: TCP)
- - (Target colorspace: 32 bit colour)
- - Capture Resolution: 1920x1080 1080p
- - In the "Timestamp" tab, specify:
- - Timestamp Label X: 10
- - Timestamp Label Y: 10
- - Font Size: Large
- - In the "Buffers" tab, specify:
- - Image Buffer Size (frames): 40
+AgentDVR's web interface is also used to configure a default storage
+location. From the Server Menu (upper left), the administrator chooses
+Configuration Settings, the Storage tab, the Configure button, and the
+add (plus) button. The storage location is set to =/DVR/AgentDVR/=
+and the "default" toggle is set. Several OK buttons then need to be
+pressed before the task is complete.
+
+** Configure AgentDVR's Recordings
+
+After a default storage location has been configured, AgentDVR's
+cameras can begin recording. The "Edit Devices" dialog lists (via the
+"Edit Devices" item in the Server Menu) the configured cameras. The
+edit buttons lead to the device settings where the following
+parameters are set (in the Recording and Storage tabs).
+
+ - Recording:
+ + Mode: Constant
+ + Encoder: Raw Record Stream
+ + Max record time: 900 (15 minutes)
+ - Storage:
+ + Location: /DVR/AgentDVR/
+ + Folder: Outside
+ + Storage Management:
+ - On: yes
+ - Max Size: 0 (unlimited)
+ - Max Age: 168 (7 days)
* The Abbey TVR Role
- hosts: dvrs
tasks:
- - name: Restart Zoneminder.
+ - name: Restart AgentDVR.
become: yes
systemd:
- service: "{{ item }}"
+ service: AgentDVR
state: restarted
- loop: [ mysql, zoneminder ]
when: new_tz.changed
- hosts: tvrs